Repository: happyfish100/FastCFS Branch: master Commit: e7d609e56cba Files: 261 Total size: 1.5 MB Directory structure: gitextract_24sao1go/ ├── .gitignore ├── FastCFS-auth.spec.in ├── FastCFS-vote.spec.in ├── FastCFS.spec.in ├── LICENSE ├── README-zh_CN.md ├── README.md ├── conf/ │ ├── full/ │ │ ├── fuse.conf │ │ └── papi.conf │ ├── fuse.conf │ └── papi.conf ├── debian/ │ ├── changelog │ ├── compat │ ├── control │ ├── copyright │ ├── fastcfs-api-dev.install │ ├── fastcfs-api-libs.install │ ├── fastcfs-api-tests.install │ ├── fastcfs-auth-client.install │ ├── fastcfs-auth-config.install │ ├── fastcfs-auth-dev.install │ ├── fastcfs-auth-server-config.install │ ├── fastcfs-auth-server.dirs │ ├── fastcfs-auth-server.install │ ├── fastcfs-fuse-config.install │ ├── fastcfs-fused.dirs │ ├── fastcfs-fused.install │ ├── fastcfs-utils.install │ ├── fastcfs-vote-client.install │ ├── fastcfs-vote-config.install │ ├── fastcfs-vote-dev.install │ ├── fastcfs-vote-server.dirs │ ├── fastcfs-vote-server.install │ ├── rules │ ├── source/ │ │ └── format │ ├── substvars │ └── watch ├── docs/ │ ├── APT-INSTALL-zh_CN.md │ ├── AUTH-zh_CN.md │ ├── CONFIGURE-zh_CN.md │ ├── Easy-install-detail-zh_CN.md │ ├── FAQ-zh_CN.md │ ├── INSTALL-zh_CN.md │ ├── INSTALL.md │ ├── ReleaseNotes.md │ ├── TODO-zh_CN.md │ ├── VoteNode-zh_CN.md │ ├── YUM-INSTALL-Diy-5nodes-zh_CN.md │ ├── YUM-INSTALL-zh_CN.md │ ├── benchmark-step-by-step.md │ ├── benchmark.md │ ├── cluster-expansion-zh_CN.md │ ├── fcfs-ops-tool-zh_CN.md │ ├── fcfs-ops-tool.md │ ├── index.md │ ├── shared-storage-guide-zh_CN.md │ ├── version-history-zh_CN.md │ └── versions.json ├── fastcfs.sh ├── helloWorld.sh ├── libfuse_setup.sh ├── make.sh ├── mkdocs.yml ├── shell/ │ ├── Dockerfile │ ├── conf_tpl_tar.sh │ ├── fcfs.settings │ ├── fcfs.sh │ ├── fcfs_conf.settings │ ├── fcfs_conf.sh │ └── template/ │ └── dependency.2.0.1.settings ├── src/ │ ├── api/ │ │ ├── Makefile.in │ │ ├── async_reporter.c │ │ ├── async_reporter.h │ │ ├── fcfs_api.c │ │ ├── fcfs_api.h │ │ ├── fcfs_api_allocator.c │ │ ├── fcfs_api_allocator.h │ │ ├── fcfs_api_file.c │ │ ├── fcfs_api_file.h │ │ ├── fcfs_api_types.h │ │ ├── fcfs_api_util.c │ │ ├── fcfs_api_util.h │ │ ├── inode_htable.c │ │ ├── inode_htable.h │ │ ├── std/ │ │ │ ├── api_types.h │ │ │ ├── capi.c │ │ │ ├── capi.h │ │ │ ├── fd_manager.c │ │ │ ├── fd_manager.h │ │ │ ├── papi.c │ │ │ ├── papi.h │ │ │ ├── posix_api.c │ │ │ └── posix_api.h │ │ └── tests/ │ │ ├── Makefile.in │ │ ├── fcfs_beachmark.c │ │ ├── fcfs_test_file_copy.c │ │ ├── fcfs_test_file_op.c │ │ ├── fcfs_test_papi_copy.c │ │ └── fcfs_test_read_ahead.c │ ├── auth/ │ │ ├── client/ │ │ │ ├── Makefile.in │ │ │ ├── client_func.c │ │ │ ├── client_func.h │ │ │ ├── client_global.c │ │ │ ├── client_global.h │ │ │ ├── client_proto.c │ │ │ ├── client_proto.h │ │ │ ├── client_types.h │ │ │ ├── fcfs_auth_client.c │ │ │ ├── fcfs_auth_client.h │ │ │ ├── fcfs_auth_for_server.c │ │ │ ├── fcfs_auth_for_server.h │ │ │ ├── session_regenerate.c │ │ │ ├── session_regenerate.h │ │ │ ├── session_sync.c │ │ │ ├── session_sync.h │ │ │ ├── simple_connection_manager.c │ │ │ ├── simple_connection_manager.h │ │ │ └── tools/ │ │ │ ├── Makefile.in │ │ │ ├── fauth_cluster_stat.c │ │ │ ├── fauth_list_servers.c │ │ │ ├── fcfs_pool.c │ │ │ ├── fcfs_user.c │ │ │ ├── tool_func.c │ │ │ └── tool_func.h │ │ ├── common/ │ │ │ ├── auth_func.c │ │ │ ├── auth_func.h │ │ │ ├── auth_global.c │ │ │ ├── auth_global.h │ │ │ ├── auth_proto.c │ │ │ ├── auth_proto.h │ │ │ ├── auth_types.h │ │ │ ├── server_session.c │ │ │ └── server_session.h │ │ ├── conf/ │ │ │ ├── auth.conf │ │ │ ├── client.conf │ │ │ ├── cluster.conf │ │ │ ├── full/ │ │ │ │ ├── auth.conf │ │ │ │ ├── client.conf │ │ │ │ ├── cluster.conf │ │ │ │ ├── server.conf │ │ │ │ └── session.conf │ │ │ ├── keys/ │ │ │ │ └── session_validate.key │ │ │ ├── server.conf │ │ │ └── session.conf │ │ └── server/ │ │ ├── Makefile.in │ │ ├── cluster_handler.c │ │ ├── cluster_handler.h │ │ ├── cluster_info.c │ │ ├── cluster_info.h │ │ ├── cluster_relationship.c │ │ ├── cluster_relationship.h │ │ ├── common_handler.c │ │ ├── common_handler.h │ │ ├── db/ │ │ │ ├── auth_db.c │ │ │ ├── auth_db.h │ │ │ ├── dao/ │ │ │ │ ├── dao.c │ │ │ │ ├── dao.h │ │ │ │ ├── func.c │ │ │ │ ├── func.h │ │ │ │ ├── granted_pool.c │ │ │ │ ├── granted_pool.h │ │ │ │ ├── storage_pool.c │ │ │ │ ├── storage_pool.h │ │ │ │ ├── types.h │ │ │ │ ├── user.c │ │ │ │ └── user.h │ │ │ ├── pool_usage_updater.c │ │ │ └── pool_usage_updater.h │ │ ├── fcfs_authd.c │ │ ├── server_func.c │ │ ├── server_func.h │ │ ├── server_global.c │ │ ├── server_global.h │ │ ├── server_types.h │ │ ├── service_handler.c │ │ ├── service_handler.h │ │ ├── session_subscribe.c │ │ └── session_subscribe.h │ ├── common/ │ │ ├── fcfs_global.c │ │ └── fcfs_global.h │ ├── fuse/ │ │ ├── Makefile.in │ │ ├── fcfs_fused.c │ │ ├── fuse_wrapper.c │ │ ├── fuse_wrapper.h │ │ ├── getgroups.c │ │ ├── getgroups.h │ │ ├── global.c │ │ ├── global.h │ │ ├── groups_htable.c │ │ └── groups_htable.h │ ├── java/ │ │ ├── jni/ │ │ │ ├── Makefile.in │ │ │ ├── com_fastken_fcfs_FCFSConstants.h │ │ │ ├── com_fastken_fcfs_FCFSDirectory.c │ │ │ ├── com_fastken_fcfs_FCFSDirectory.h │ │ │ ├── com_fastken_fcfs_FCFSFile.c │ │ │ ├── com_fastken_fcfs_FCFSFile.h │ │ │ ├── com_fastken_fcfs_FCFSPosixAPI.c │ │ │ ├── com_fastken_fcfs_FCFSPosixAPI.h │ │ │ ├── common.c │ │ │ ├── common.h │ │ │ ├── global.c │ │ │ └── global.h │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── fastken/ │ │ └── fcfs/ │ │ ├── FCFSConstants.java │ │ ├── FCFSDirectory.java │ │ ├── FCFSFile.java │ │ ├── FCFSFileStat.java │ │ ├── FCFSPosixAPI.java │ │ └── FCFSVFSStat.java │ ├── preload/ │ │ ├── Makefile.in │ │ ├── api.c │ │ ├── api.h │ │ ├── global.c │ │ ├── global.h │ │ └── types.h │ ├── tools/ │ │ ├── Makefile.in │ │ ├── fcfs_active_test.c │ │ └── fcfs_pool_stat.c │ └── vote/ │ ├── client/ │ │ ├── Makefile.in │ │ ├── client_func.c │ │ ├── client_func.h │ │ ├── client_global.c │ │ ├── client_global.h │ │ ├── client_proto.c │ │ ├── client_proto.h │ │ ├── client_types.h │ │ ├── fcfs_vote_client.h │ │ └── tools/ │ │ ├── Makefile.in │ │ └── fvote_cluster_stat.c │ ├── common/ │ │ ├── vote_global.c │ │ ├── vote_global.h │ │ ├── vote_proto.c │ │ ├── vote_proto.h │ │ └── vote_types.h │ ├── conf/ │ │ ├── client.conf │ │ ├── cluster.conf │ │ ├── full/ │ │ │ ├── client.conf │ │ │ ├── cluster.conf │ │ │ └── server.conf │ │ └── server.conf │ └── server/ │ ├── Makefile.in │ ├── cluster_handler.c │ ├── cluster_handler.h │ ├── cluster_info.c │ ├── cluster_info.h │ ├── cluster_relationship.c │ ├── cluster_relationship.h │ ├── common_handler.c │ ├── common_handler.h │ ├── fcfs_voted.c │ ├── server_func.c │ ├── server_func.h │ ├── server_global.c │ ├── server_global.h │ ├── server_types.h │ ├── service_group_htable.c │ ├── service_group_htable.h │ ├── service_handler.c │ └── service_handler.h └── systemd/ ├── fastauth.service ├── fastcfs.service └── fastvote.service ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Makefile.in src/api/Makefile src/api/tests/Makefile src/preload/Makefile src/fuse/Makefile src/tools/Makefile src/auth/server/Makefile src/auth/client/Makefile src/auth/client/tools/Makefile src/vote/server/Makefile src/vote/client/Makefile src/vote/client/tools/Makefile src/java/jni/Makefile # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj *.class # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dSYM *.dll # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.out src/api/tests/fcfs_test_file_op src/api/tests/fcfs_test_file_copy src/api/tests/fcfs_test_papi_copy src/api/tests/fcfs_test_read_ahead src/api/tests/fcfs_beachmark src/fuse/fcfs_fused src/tools/fcfs_active_test src/tools/fcfs_pool_stat src/auth/server/fcfs_authd src/auth/client/tools/fcfs_user src/auth/client/tools/fcfs_pool src/auth/client/tools/fauth_list_servers src/auth/client/tools/fauth_cluster_stat src/vote/server/fcfs_voted src/vote/client/tools/fvote_cluster_stat # other logs data *.pid *.swp *.swo # Build tmp path after execute fastcfs.sh build .DS_Store .fcfs .idea ================================================ FILE: FastCFS-auth.spec.in ================================================ %define FastCFSAuthClient FastCFS-auth-client %define FastCFSAuthDevel FastCFS-auth-devel %define FastCFSAuthConfig FastCFS-auth-config %define CommitVersion %(echo $COMMIT_VERSION) Name: FastCFS-auth Version: 5.5.0 Release: 1%{?dist} Summary: the auth client library and config files of FastCFS. FastCFS is a high performance cloud native distributed file system for databases, KVM and K8s License: AGPL v3.0 Group: Arch/Tech URL: http://github.com/happyfish100/FastCFS/ Source: http://github.com/happyfish100/FastCFS/%{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: libserverframe-devel >= 1.2.10 Requires: %__cp %__mv %__chmod %__grep %__mkdir %__install %__id Requires: libserverframe >= 1.2.10 Requires: FastCFS-auth-server = %{version}-%{release} Requires: %{FastCFSAuthClient} = %{version}-%{release} Requires: %{FastCFSAuthConfig} >= 2.0.0 %description the auth client library and config files of FastCFS. FastCFS is a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms. commit version: %{CommitVersion} %package -n %{FastCFSAuthDevel} Requires: %{FastCFSAuthClient} = %{version}-%{release} Summary: header files of FastCFS auth client %package -n %{FastCFSAuthClient} Requires: libserverframe >= 1.2.10 Summary: FastCFS auth client %package -n %{FastCFSAuthConfig} Summary: FastCFS auth config files for sample %description -n %{FastCFSAuthDevel} This package provides the header files of libfcfsauthclient commit version: %{CommitVersion} %description -n %{FastCFSAuthClient} FastCFS auth client commit version: %{CommitVersion} %description -n %{FastCFSAuthConfig} FastCFS auth config files for sample commit version: %{CommitVersion} %prep %setup -q %build ./make.sh --module=authclient clean && ./make.sh --module=authclient %install rm -rf %{buildroot} DESTDIR=$RPM_BUILD_ROOT ./make.sh --module=authclient install AUTH_CONFDIR=%{buildroot}/etc/fastcfs/auth/ mkdir -p $AUTH_CONFDIR cp src/auth/conf/*.conf $AUTH_CONFDIR cp -R src/auth/conf/keys $AUTH_CONFDIR %post %preun %postun %clean rm -rf %{buildroot} %files %files -n %{FastCFSAuthClient} /usr/lib64/libfcfsauthclient.so* /usr/bin/fcfs_user /usr/bin/fcfs_pool /usr/bin/fauth_list_servers /usr/bin/fauth_cluster_stat %files -n %{FastCFSAuthDevel} %defattr(-,root,root,-) /usr/include/fastcfs/auth/* %files -n %{FastCFSAuthConfig} %defattr(-,root,root,-) %config(noreplace) /etc/fastcfs/auth/*.conf %config(noreplace) /etc/fastcfs/auth/keys/* %changelog * Thu Apr 22 2021 YuQing <384681@qq.com> - first RPM release (1.0) ================================================ FILE: FastCFS-vote.spec.in ================================================ %define FastCFSVoteClient FastCFS-vote-client %define FastCFSVoteDevel FastCFS-vote-devel %define FastCFSVoteConfig FastCFS-vote-config %define CommitVersion %(echo $COMMIT_VERSION) Name: FastCFS-vote Version: 5.5.0 Release: 1%{?dist} Summary: the vote client library and config files of FastCFS. FastCFS is a high performance cloud native distributed file system for databases, KVM and K8s License: AGPL v3.0 Group: Arch/Tech URL: http://github.com/happyfish100/FastCFS/ Source: http://github.com/happyfish100/FastCFS/%{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: libserverframe-devel >= 1.2.10 Requires: %__cp %__mv %__chmod %__grep %__mkdir %__install %__id Requires: libserverframe >= 1.2.10 Requires: FastCFS-vote-server = %{version}-%{release} Requires: %{FastCFSVoteClient} = %{version}-%{release} Requires: %{FastCFSVoteConfig} >= 3.5.0 %description the vote client library and config files of FastCFS. FastCFS is a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms. commit version: %{CommitVersion} %package -n %{FastCFSVoteDevel} Requires: %{FastCFSVoteClient} = %{version}-%{release} Summary: header files of FastCFS vote client %package -n %{FastCFSVoteClient} Requires: libserverframe >= 1.2.10 Summary: FastCFS vote client %package -n %{FastCFSVoteConfig} Summary: FastCFS vote config files for sample %description -n %{FastCFSVoteDevel} This package provides the header files of libfcfsvoteclient commit version: %{CommitVersion} %description -n %{FastCFSVoteClient} FastCFS vote client commit version: %{CommitVersion} %description -n %{FastCFSVoteConfig} FastCFS vote config files for sample commit version: %{CommitVersion} %prep %setup -q %build ./make.sh --module=voteclient clean && ./make.sh --module=voteclient %install rm -rf %{buildroot} DESTDIR=$RPM_BUILD_ROOT ./make.sh --module=voteclient install VOTE_CONFDIR=%{buildroot}/etc/fastcfs/vote/ mkdir -p $VOTE_CONFDIR cp src/vote/conf/*.conf $VOTE_CONFDIR %post %preun %postun %clean rm -rf %{buildroot} %files %files -n %{FastCFSVoteClient} /usr/lib64/libfcfsvoteclient.so* /usr/bin/fvote_cluster_stat %files -n %{FastCFSVoteDevel} %defattr(-,root,root,-) /usr/include/fastcfs/vote/* %files -n %{FastCFSVoteConfig} %defattr(-,root,root,-) %config(noreplace) /etc/fastcfs/vote/*.conf %changelog * Thu Apr 22 2021 YuQing <384681@qq.com> - first RPM release (1.0) ================================================ FILE: FastCFS.spec.in ================================================ %define FastCFSFused FastCFS-fused %define FastCFSUtils FastCFS-utils %define FastCFSAPI FastCFS-api-libs %define FastCFSAPITests FastCFS-api-tests %define FastCFSAuthServer FastCFS-auth-server %define FastCFSVoteServer FastCFS-vote-server %define FastCFSAPIDevel FastCFS-api-devel %define FastCFSFuseConfig FastCFS-fuse-config %define CommitVersion %(echo $COMMIT_VERSION) Name: FastCFS Version: 5.5.0 Release: 1%{?dist} Summary: a high performance cloud native distributed file system for databases, KVM and K8s License: AGPL v3.0 Group: Arch/Tech URL: http://github.com/happyfish100/FastCFS/ Source: http://github.com/happyfish100/FastCFS/%{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: fastDIR-devel >= 5.5.0 BuildRequires: faststore-devel >= 5.5.0 BuildRequires: fuse3-devel >= 3.16.2 Requires: %__cp %__mv %__chmod %__grep %__mkdir %__install %__id Requires: %{FastCFSFused} = %{version}-%{release} Requires: %{FastCFSUtils} = %{version}-%{release} Requires: %{FastCFSAPITests} = %{version}-%{release} %description a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms. commit version: %{CommitVersion} %package -n %{FastCFSFused} Requires: fuse3 >= 3.16.2 Requires: %{FastCFSAPI} = %{version}-%{release} Requires: %{FastCFSFuseConfig} >= 1.0.0 Summary: FastCFS fuse %package -n %{FastCFSUtils} Requires: %{FastCFSAPI} = %{version}-%{release} Summary: FastCFS utils %package -n %{FastCFSAPI} Requires: fastDIR-client >= 5.5.0 Requires: faststore-client >= 5.5.0 Summary: FastCFS api library %package -n %{FastCFSAPITests} Requires: %{FastCFSAPI} = %{version}-%{release} Requires: %{FastCFSFuseConfig} >= 1.0.0 Summary: FastCFS api tests %package -n %{FastCFSAPIDevel} Requires: %{FastCFSAPI} = %{version}-%{release} Summary: header files of FastCFS api library %package -n %{FastCFSAuthServer} Requires: fastDIR-client >= 5.5.0 Requires: FastCFS-auth-config >= 2.0.0 Summary: FastCFS auth server %package -n %{FastCFSVoteServer} Requires: libserverframe >= 1.2.10 Requires: FastCFS-vote-config >= 3.5.0 Summary: FastCFS vote server %package -n %{FastCFSFuseConfig} Requires: faststore-config >= 1.0.0 Summary: FastCFS fuse config files for sample %description -n %{FastCFSFused} FastCFS fuse commit version: %{CommitVersion} %description -n %{FastCFSUtils} FastCFS utils commit version: %{CommitVersion} %description -n %{FastCFSAPI} FastCFS api library commit version: %{CommitVersion} %description -n %{FastCFSAPITests} FastCFS api tests commit version: %{CommitVersion} %description -n %{FastCFSAPIDevel} This package provides the header files of libfcfsapi commit version: %{CommitVersion} %description -n %{FastCFSAuthServer} FastCFS auth server commit version: %{CommitVersion} %description -n %{FastCFSVoteServer} FastCFS vote server commit version: %{CommitVersion} %description -n %{FastCFSFuseConfig} FastCFS fuse config files for sample commit version: %{CommitVersion} %prep %setup -q %build ./make.sh clean && ./make.sh --exclude=client %install rm -rf %{buildroot} DESTDIR=$RPM_BUILD_ROOT ./make.sh --exclude=client install FUSE_CONFDIR=%{buildroot}/etc/fastcfs/fcfs/ SYSTEMDIR=%{buildroot}/usr/lib/systemd/system/ mkdir -p $FUSE_CONFDIR mkdir -p $SYSTEMDIR cp conf/*.conf $FUSE_CONFDIR cp systemd/fastcfs.service $SYSTEMDIR cp systemd/fastauth.service $SYSTEMDIR cp systemd/fastvote.service $SYSTEMDIR %post %preun %postun %clean rm -rf %{buildroot} %files %post -n %{FastCFSFused} mkdir -p /opt/fastcfs/fcfs mkdir -p /opt/fastcfs/fuse systemctl enable fastcfs %files -n %{FastCFSFused} /usr/bin/fcfs_fused %config(noreplace) /usr/lib/systemd/system/fastcfs.service %files -n %{FastCFSUtils} /usr/bin/fcfs_active_test /usr/bin/fcfs_pool_stat %files -n %{FastCFSAPI} %defattr(-,root,root,-) /usr/lib64/libfcfsapi.so* /usr/lib64/libfcfspreload.so* %files -n %{FastCFSAPITests} /usr/bin/fcfs_beachmark /usr/bin/fcfs_test_file_op /usr/bin/fcfs_test_file_copy /usr/bin/fcfs_test_papi_copy /usr/bin/fcfs_test_read_ahead %files -n %{FastCFSAPIDevel} %defattr(-,root,root,-) /usr/include/fastcfs/api/* %post -n %{FastCFSAuthServer} mkdir -p /opt/fastcfs/auth systemctl enable fastauth %post -n %{FastCFSVoteServer} mkdir -p /opt/fastcfs/vote systemctl enable fastvote %files -n %{FastCFSAuthServer} /usr/bin/fcfs_authd %config(noreplace) /usr/lib/systemd/system/fastauth.service %files -n %{FastCFSVoteServer} /usr/bin/fcfs_voted %config(noreplace) /usr/lib/systemd/system/fastvote.service %files -n %{FastCFSFuseConfig} %defattr(-,root,root,-) %config(noreplace) /etc/fastcfs/fcfs/*.conf %changelog * Fri Jan 1 2021 YuQing <384681@qq.com> - first RPM release (1.0) ================================================ FILE: LICENSE ================================================ GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 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. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero 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 Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . ================================================ FILE: README-zh_CN.md ================================================ # FastCFS -- 可以跑数据库的高性能通用分布式文件系统 [English](README.md) | 简体中文 ## 1. 简介 FastCFS 是一款强一致性、高性能、高可用、支持百亿级海量文件的通用分布式文件系统,可以作为MySQL、PostgreSQL、Oracle等数据库,k8s、KVM、FTP、SMB和NFS等系统的后端存储。 ### FastCFS 主要特点 * 保证数据强一致前提下实现了高性能:性能完胜Ceph; * 完全兼容POSIX文件接口,支持文件锁,64G内存即可支持百亿级海量文件; * 高可用:不存在单点,自动failover; * 简洁高效的架构和原生实现,不依赖第三方组件,内置运维工具,易用性较好; * 随机写性能强悍:FCFS基于trunk顺序分配空间,将客户端的随机写转换为顺序写。 ### FastCFS 典型应用场景 * **数据库**:支持常规的独享数据和高阶的共享数据两种存储方式,支持数据库云化; * **文件存储**:如文档、图片、视频等等,FastCFS比对象存储更容易与通用软件集成; * **超融合存储**:数据库和文件存储共用一个存储集群,显著提升存储资源利用率; * **高性能计算**:高可靠和高性能的FastCFS,支持RDMA,天然适合高性能计算场景; * **视频监控**:FastCFS采用顺序写盘方式,使用SATA硬盘也可保证多路视频流畅写入。 ## 2. 当前版本:V5.5.0 [FastCFS重大版本一览](docs/version-history-zh_CN.md) ## 3. 支持的操作系统 * Linux: Kernel version >= 3.10 (完全支持,推荐使用4.18及更高版本) * MacOS or FreeBSD (仅支持服务端,不支持FUSE) ## 4. 依赖 * [libfuse](https://gitee.com/mirrors/libfuse) (版本 3.9.4 或更高版本,推荐3.16.2) * [Python](https://python.org/) (版本 3.5 或更高版本) * [Ninja](https://ninja-build.org/) (版本 1.7 或更高版本) * [gcc](https://www.gnu.org/software/gcc/) (版本 4.7.0 或更高版本) * [libfastcommon](https://gitee.com/fastdfs100/libfastcommon) (tag: V1.0.83) * [libserverframe](https://gitee.com/fastdfs100/libserverframe) (tag: V1.2.11) * [libdiskallocator](https://gitee.com/fastdfs100/libdiskallocator) (tag: V1.1.13) * [fastDIR](https://gitee.com/fastdfs100/fastDIR) (tag: V5.5.0) * [faststore](https://gitee.com/fastdfs100/faststore) (tag: V5.5.0) * [FastCFS](https://gitee.com/fastdfs100/FastCFS) (tag: V5.5.0) ## 5. 部署 & 运维 FastCFS包含 libfastcommon、libserverframe、libdiskallocator、fastDIR、faststore和FastCFS 六个安装包。 ### 一键部署 如果你打算快速体验一下FastCFS,可以一键搭建(包括部署和运行)单节点(需要root身份执行): ``` git clone https://gitee.com/fastdfs100/FastCFS.git; cd FastCFS/ ./helloWorld.sh # 注意:helloWorld.sh将更改FastCFS相关配置文件,请不要在多节点集群上执行! ``` 上述操作完成后,执行命令: ``` df -h /opt/fastcfs/fuse | grep fuse ``` 可以看到FastCFS挂载的文件目录,你可以当作本地文件系统访问该目录。 一键部署的详细说明,请参见这里[一键部署详细说明](docs/Easy-install-detail-zh_CN.md) ### 集群部署工具 推荐使用 [FastCFS集群运维工具](docs/fcfs-ops-tool-zh_CN.md) ### DIY安装 如果你要自己搭建FastCFS环境,可以采用如下三种安装方式之一: * yum安装(针对CentOS、Rocky、Fedora、RHEL等),参阅 [yum安装文档](docs/YUM-INSTALL-zh_CN.md) * apt安装(针对Ubuntu、Debian 和 Deepin),参阅 [apt 安装文档](docs/APT-INSTALL-zh_CN.md) * 源码编译安装,参阅 [安装文档](docs/INSTALL-zh_CN.md) ### 配置指南 FastCFS安装完成后,请参阅[配置指南](docs/CONFIGURE-zh_CN.md) ### 集群扩容 详情参见 [FastCFS集群扩容手册](docs/cluster-expansion-zh_CN.md) ## 6. 性能测试 FastCFS性能明显优于Ceph:顺序写是Ceph的6.x倍,顺序读是Ceph的2.x倍,随机写大约是Ceph的2倍。 * [FastCFS与Ceph性能对比测试结果概要](docs/benchmark.md) * 详情参见 [完整PDF文档](docs/benchmark-20210621.pdf) * 如何进行性能测试点击 [这里](docs/benchmark-step-by-step.md) ## 7. K8s CSI驱动 参见项目 [fastcfs-csi](https://gitee.com/fastdfs100/fastcfs-csi) ## 8. 技术文章 参见 技术博客 ## 9. 常见问题 参见 [FastCFS常见问题](docs/FAQ-zh_CN.md) ## 10. 待完成工作 * [fstore] 单盘故障恢复后,自动恢复数据(已完成) * [fstore] 机器故障或网络短暂故障恢复后,master需重新均衡分配(已完成) * [fauth & fdir & fstore] leader选举支持过半原则,防止脑裂(已完成) * [fauth & fdir & fstore] 实现公用选举节点,双副本防脑裂(已完成) * [fdir & fstore] binlog去重及历史数据清理(已完成) * [fdir & fstore] 数据提交采用多数派确认机制保证数据一致性和可靠性(已完成) * [fdir & fstore] 针对两副本,数据提交多数派机制智能化(已完成) * [fstore] 文件读写性能优化(已完成) * [fdir & api] POSIX兼容性测试及改进(已完成) * [fstore] slice存储引擎插件,有限内存支持海量数据(已完成) * [all] 适配RDMA网络,突破网络瓶颈(已完成) * [fdir] 支持回收站功能,不用担心误删除文件(已完成) * [all] 支持集群在线扩容 参见更多 [TODO List](docs/TODO-zh_CN.md),欢迎大家参与。 ## 11. 商业支持 我们提供商业技术支持和定制化开发,欢迎微信或邮件洽谈。 email: 384681(at)qq(dot)com ## 12. 联系我们 查看FastCFS相关技术文章,请关注微信公众号: 微信公众号 微信交流群: 微信交流群 ================================================ FILE: README.md ================================================ # FastCFS -- a high performance general distributed file system for databases, K8s and KVM etc. English | [Chinese](README-zh_CN.md) ## 1. About FastCFS is a general distributed file system with strong consistency, high performance, high availability and supporting 10 billion massive files. FastCFS can be used as the back-end storage of databases (MySQL, PostgreSQL, Oracle etc.), K8s, KVM, FTP, SMB and NFS. ### Main Features * High performance on the premise of strong data consistency: performance is more better than ceph * Fully compatible with POSIX, supporting file lock and 10 billion massive files * High availability: no single point & automatic failover * Easy to use: * simple and efficient architecture and native implementation * independent of third-party components * built-in operation and maintenance tools * Strong random write performance: FCFS allocates space based on the trunk file, converts random writes from the client to sequential writes ### Classical Application Scene * **Database**: supports two storage methods (conventional exclusive data and high-level shared data) and database cloudification * **File Storage**: documents, pictures, videos, etc., FastCFS is easier to integrate with general software than the object storage * **Unified Storage**: database and file storage share a storage cluster, which significantly improves the utilization of storage resources * **High Performance Computing**: FastCFS with high reliability and high performance is naturally suitable for the HPC scene * **Video monitoring**: smooth writing for multi-channel videos with HDD such as SATA because FastCFS uses sequential writing approach ## 2. Current Version: V5.5.0 ## 3. Supported Platforms * Linux: Kernel version >= 3.10 (Full support, >= 4.18 is recommended) * MacOS or FreeBSD (Only server side) ## 4. Dependencies * [libfuse](https://github.com/libfuse/libfuse) (version 3.9.4 or newer, 3.16.2 is recommended) * [Python](https://python.org/) (version 3.5 or newer) * [Ninja](https://ninja-build.org/) (version 1.7 or newer) * [gcc](https://www.gnu.org/software/gcc/) (version 4.7.0 or newer) * [libfastcommon](https://github.com/happyfish100/libfastcommon) (tag: V1.0.83) * [libserverframe](https://github.com/happyfish100/libserverframe) (tag: V1.2.11) * [libdiskallocator](https://github.com/happyfish100/libdiskallocator) (tag: V1.1.13) * [fastDIR](https://github.com/happyfish100/fastDIR) (tag: V5.5.0) * [faststore](https://github.com/happyfish100/faststore) (tag: V5.5.0) * [FastCFS](https://github.com/happyfish100/FastCFS) (tag: V5.5.0) ## 5. Installation ### 5.1 DIY installation you can use [Cluster Operation Tool](docs/fcfs-ops-tool.md) to deploy FastCFS step by step please see [INSTALL](docs/INSTALL.md) recommend to execute libfuse_setup.sh for compiling and installing libfuse ### 5.2 easy installation libfastcommon, libserverframe, fastDIR, faststore and FastCFS can be compiled, installed and auto configurated by fastcfs.sh fastcfs.sh can automatically pull or update above six projects codes from GitHub, compile and install according to dependency orders, and automatically generate cluster related configuration files according to the config templates. ``` git clone https://github.com/happyfish100/FastCFS.git; cd FastCFS/ ``` fastcfs.sh usage: ``` * install: pull/update codes from gitee, then make and install * config: copy config files and configure them with local ip * start | stop | restart: for service processes control ``` one click to build (deploy and run) single node demo environment (MUST run by root): ``` ./helloWorld.sh ``` or execute following commands (MUST run by root): ``` ./fastcfs.sh install ./fastcfs.sh config --force ./fastcfs.sh restart ``` now you can see the mounted path of FastCFS by the command: ``` df -h /opt/fastcfs/fuse | grep fuse ``` ## 6. Benchmark FastCFS has huge better performance than Ceph: the IOPS ratio of sequential write is 6.x, sequential read is 2.x, random write is about 2.0. * [FastCFS vs. Ceph benchmark](docs/benchmark.md) ## 7. K8s CSI Driver [fastcfs-csi](https://github.com/happyfish100/fastcfs-csi) ## 8. Chinese Relative articles CSDN blog ## 9. TODO List * [fstore] data recovery after single disk fault (done) * [fstore] after the machine recovery, the data masters should be rebalanced (done) * [fauth & fdir & fstore] leader election uses more than half principle to prevent brain-split (done) * [fauth & fdir & fstore] import public vote node under 2 copies scene to prevent brain-split (done) * [fdir & fstore] binlog deduplication and historical data deletion (done) * [fdir & fstore] data submission by majority confirmation to ensure data consistency (done) * [fdir & fstore] for two replicas, the data submission majority mechanism is intelligent (done) * [fstore] file read & write performance optimization (done) * [fdir & api] POSIX compatibility test and improvement (done) * [fstore] slice storage engine plugin to support massive data with limited memory (done) * [all] adapt to RDMA network to break through network bottlenecks (done) * [fdir] support recycle bin, don't worry accidentally deleting files (done) * [all] cluster online expansion ## 10. Business Support We provide technical support service and customized development. Welcome to use WeChat or email for discuss. ## 11. Contact us email: 384681(at)qq(dot)com WeChat subscription: search "fastdfs" for the related articles (Chinese Only) ================================================ FILE: conf/full/fuse.conf ================================================ # the base path to store log files # this path must exist base_path = /opt/fastcfs/fcfs # the mount point (local path) for FUSE # the local path must exist mountpoint = /opt/fastcfs/fuse #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level = info #unix group name to run this program, #not set (empty) means run by the group of current user run_by_group = #unix username to run this program, #not set (empty) means run by current user run_by_user = # sync log buff to disk every interval seconds # default value is 1 seconds sync_log_buff_interval = 1 # if rotate the error log every day # default value is false rotate_error_log = false # rotate error log time base, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # default value is 00:00 error_log_rotate_time = 00:00 # if compress the old error log by gzip # default value is false compress_old_error_log = false # compress the error log days before # default value is 1 compress_error_log_days_before = 7 # rotate error log when the log file exceeds this size # 0 means never rotates log file by log file size # default value is 0 rotate_error_log_size = 0 # keep days of the log files # 0 means do not delete old log files # default value is 0 log_file_keep_days = 0 # connect timeout in seconds # default value is 10 # Note: in the intranet network (LAN), 10 seconds is enough. connect_timeout = 10 # network timeout in seconds # default value is 60 network_timeout = 60 # TCP quick ack for Linux (setsockopt with TCP_QUICKACK option) # default value is true tcp_quick_ack = true # the rule of read data, value list: ### any : any available server ### slave : slave first, access master when all slaves down or offline ### master : master only (default) # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] read_rule = master # the mode of retry interval, value list: ### fixed for fixed interval ### multiple for multiplication (default) # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] retry_interval_mode = multiple # the max retry interval in milliseconds # valid when retry_interval_mode set to multiple # default value is 3000 ms # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] retry_max_interval_ms = 3000 # retry times when connect to server fail ### 0 for never retry ### < 0 for infinite retry # default value is 200 # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] connect_retry_times = 200 # retry interval when connect to server fail # unit: milliseconds # default value is 100 ms # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] connect_retry_interval_ms = 100 # retry times when communicate with server fail ### 0 for never retry ### < 0 for infinite retry # default value is 200 # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] network_retry_times = 200 # retry interval when communicate with server fail # unit: milliseconds # default value is 100 ms # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] network_retry_interval_ms = 100 # if use busy polling for RDMA network # should set to true for HPC # default value is false busy_polling = false [idempotency] # if enable RPC idempotency for highest level consistency # default value is false enabled = true # the idempotency channel hashtable capacity # default value is 1361 channel_htable_capacity = 1361 # the heartbeat interval for RPC idempotency channel # unit: seconds # default value is 3s channel_heartbeat_interval = 3 # close the idempotency channel when max idle time reachs # unit: seconds # default value is 300s channel_max_idle_time = 300 # max connections for RPC idempotency report # you should set this parameter larger than the total server count of # FastDIR and FastStore # default value is 256 max_connections = 1024 # work thread count for RPC idempotency report # default value is 1 work_threads = 1 # max pkg size for RPC idempotency report # default value is 256KB max_pkg_size = 256KB # thread stack size, should >= 320KB thread_stack_size = 512KB [FastDIR] # connect timeout in seconds # default value is 10 # Note: in the intranet network (LAN), 10 seconds is enough. connect_timeout = 10 # network timeout in seconds # default value is 60 network_timeout = 60 # the namespace for FastDIR namespace = fs # config the cluster servers cluster_config_filename = ../fdir/cluster.conf # if use sys lock for file append and truncate to avoid conflict # set true when the files appended or truncated by many nodes (FUSE instances) # default value is false use_sys_lock_for_append = false # if async report file attributes (size, modify time etc.) to the FastDIR server # default value is true async_report_enabled = true # the interval in milliseconds for async report file attributes to the FastDIR server # default value is 100 ms async_report_interval_ms = 100 # the sharding count of hashtable # NO more than 1000 is recommended # default value is 17 hashtable_sharding_count = 17 # the capacity (bucket count) of all sharding hashtables # default value is 1403641 hashtable_total_capacity = 1403641 # the shared allocators for hashtabe entry, task etc. # NO more than the CPU cores is recommended # default value is 11 shared_allocator_count = 11 # set the owner (user and group) of new created files and directories # the values are: ## caller: current user and group from the calling process (default) ## fixed: fixed user and group owner_type = caller # set the user owner (username) of the new created files and directories # this parameter is valid only when owner_type set to fixed # empty for the user who runs the fcfs_fused program owner_user = # set the group owner (group name) of the new created files and directories # this parameter is valid only when owner_type set to fixed # empty for the group of the user who runs the fcfs_fused program owner_group = [FastStore] # connect timeout in seconds # default value is 10 # Note: in the intranet network (LAN), 10 seconds is enough. connect_timeout = 10 # network timeout in seconds # default value is 60 network_timeout = 60 # config the cluster servers and groups cluster_config_filename = ../fstore/cluster.conf # the sharding count of hashtable # NO more than 1000 is recommended # default value is 163 hashtable_sharding_count = 163 # the capacity (bucket count) of all sharding hashtables # default value is 1403641 hashtable_total_capacity = 1403641 # the shared allocators for hashtabe entry, task, slice etc. # NO more than the CPU cores is recommended # default value is 17 shared_allocator_count = 17 [write-combine] # if enable write combine feature for FastStore # default value is true enabled = true # the buffer size for write combine # the min value is 64KB and the max value is the data block size such as 4MB # default value is 256KB buffer_size = 256KB # the min wait time in milliseconds for write combine # the min value is 10ms and the max value is 100ms # default value is 20ms min_wait_time_ms = 50 # the max wait time in milliseconds for write combine # the min value is 100ms and the max value is 10000ms # default value is 1000ms max_wait_time_ms = 1000 # the slice size for skipping write combine # skip combine when the slice size >= this parameter # the min value is 64KB and the max value is the data block size such as 4MB # default value is 256KB skip_combine_on_slice_size = 256KB # the merged slice count of last combine for skipping write combine # skip combine when the last combine is in progress and # it's merged slice count <= this parameter # default value is 1 skip_combine_on_last_merged_slices = 1 # the shared locks for timer (timeout manager) # default value is 163 timer_shared_lock_count = 163 # the max waiting slice count in queue for flow control # default value is 16 max_waiting_slice_count = 16 # the thread limit (max threads) for the thread pool # default value is 8 thread_pool_max_threads = 8 # the min idle thread count for the thread pool # default value is 2 thread_pool_min_idle_count = 2 # the max idle time in seconds for the thread pool # the running thread will exit on three conditions: ## 1. this paramter > 0 ## 2. running thread count > thread_pool_min_idle_count ## 3. the thread's idle time exceeds this parameter # default value is 300 (seconds) thread_pool_max_idle_time = 300 [read-ahead] # if enable read ahead feature for FastStore # default value is true enabled = true # the TTL in miliseconds for preread cache # the min value is 100ms and the max value is 10000ms # default value is 1000ms cache_ttl_ms = 1000 # the min buffer size for preread # the min value is 1KB and the max value is 16KB # default value is 4KB min_buffer_size = 4KB # the max buffer size for preread # the min value is 16KB and the max value is 256KB # default value is 128KB max_buffer_size = 128KB # the slice size for skipping read ahead # skip read ahead when the slice size >= this parameter # the min value is 8KB and the max value is 128KB # default value is half of max_buffer_size skip_preread_on_slice_size = 64KB # the shared locks for preread hashtable # default value is 1361 shared_lock_count = 1361 [FUSE] # if single thread mode # set true to disable multi-threaded operation # default value is false singlethread = false # if use separate fuse device fd for each thread # set to true for more high performance # default value is false clone_fd = true # the max worker threads for FUSE # this parameter is valid when the version of libfuse >= 3.12 # default value is 10 max_threads = 10 # the max idle worker threads for FUSE # default value is 10 max_idle_threads = 10 # access permissions for other users # the values are: ## all for all users ## root for root only ## empty or other values for none allow_others = all # mount the filesystem read-only as mount option: ro # default value is false read_only = false # auto unmount on process termination # default value is false auto_unmount = false # cache time for file entry in seconds # default value is 1.0s entry_timeout = 5.0 # cache time for file attribute in seconds # default value is 1.0s attribute_timeout = 5.0 # if enable kernel writeback cache # set to true for unshared data scene (private data for single node) # default value is true writeback_cache = true # if keep kernel cache for read # set to true for unshared data scene (private data for single node) # should set to false on shared data scene for multi nodes # default value is true kernel_cache = true # if enable x-attribute # default value is false # IMPORTANT NOTE: ### because Linux kernel get the xattr "security.capability" *EVERY* write, ### do NOT change this parameter to true unless your application need ### setxattr, getxattr or listxattr really! xattr_enabled = false # if enable additional groups for POSIX ACL # default value is true groups_enabled = true [groups-cache] # if enable additional groups cache # default value is true enabled = true # the timeout of additional groups cache in seconds # default value is 300 seconds timeout = 300 # element limit for less memory usage # default value is 65536 element_limit = 65536 # the sharding count of hashtable # NO more than 1000 is recommended # default value is 163 hashtable_sharding_count = 163 # the capacity (bucket count) of all sharding hashtables # default value is 175447 hashtable_total_capacity = 175447 # the shared allocators for hashtabe entry, task etc. # NO more than the CPU cores is recommended # default value is 7 shared_allocator_count = 7 ================================================ FILE: conf/full/papi.conf ================================================ # the base path to store log files # this path must exist base_path = /opt/fastcfs/fcfs # the virtual mount point mountpoint = /opt/fastcfs/fuse #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level = info #unix group name to run this program, #not set (empty) means run by the group of current user run_by_group = #unix username to run this program, #not set (empty) means run by current user run_by_user = # sync log buff to disk every interval seconds # default value is 1 seconds sync_log_buff_interval = 1 # if rotate the error log every day # default value is false rotate_error_log = false # rotate error log time base, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # default value is 00:00 error_log_rotate_time = 00:00 # if compress the old error log by gzip # default value is false compress_old_error_log = false # compress the error log days before # default value is 1 compress_error_log_days_before = 7 # rotate error log when the log file exceeds this size # 0 means never rotates log file by log file size # default value is 0 rotate_error_log_size = 0 # keep days of the log files # 0 means do not delete old log files # default value is 0 log_file_keep_days = 0 # connect timeout in seconds # default value is 10 # Note: in the intranet network (LAN), 10 seconds is enough. connect_timeout = 10 # network timeout in seconds # default value is 60 network_timeout = 60 # TCP quick ack for Linux (setsockopt with TCP_QUICKACK option) # default value is true tcp_quick_ack = true # the rule of read data, value list: ### any : any available server ### slave : slave first, access master when all slaves down or offline ### master : master only (default) # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] read_rule = master # the mode of retry interval, value list: ### fixed for fixed interval ### multiple for multiplication (default) # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] retry_interval_mode = multiple # the max retry interval in milliseconds # valid when retry_interval_mode set to multiple # default value is 3000 ms # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] retry_max_interval_ms = 3000 # retry times when connect to server fail ### 0 for never retry ### < 0 for infinite retry # default value is 200 # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] connect_retry_times = 200 # retry interval when connect to server fail # unit: milliseconds # default value is 100 ms # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] connect_retry_interval_ms = 100 # retry times when communicate with server fail ### 0 for never retry ### < 0 for infinite retry # default value is 200 # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] network_retry_times = 200 # retry interval when communicate with server fail # unit: milliseconds # default value is 100 ms # this parameter can be overriden / redefined in section [FastDIR] and [FastStore] network_retry_interval_ms = 100 [idempotency] # if enable RPC idempotency for highest level consistency # default value is false enabled = true # the idempotency channel hashtable capacity # default value is 1361 channel_htable_capacity = 1361 # the heartbeat interval for RPC idempotency channel # unit: seconds # default value is 3s channel_heartbeat_interval = 3 # close the idempotency channel when max idle time reachs # unit: seconds # default value is 300s channel_max_idle_time = 300 # max connections for RPC idempotency report # you should set this parameter larger than the total server count of # FastDIR and FastStore # default value is 256 max_connections = 1024 # work thread count for RPC idempotency report # default value is 1 work_threads = 1 # max pkg size for RPC idempotency report # default value is 256KB max_pkg_size = 256KB # thread stack size, should >= 320KB thread_stack_size = 512KB [FastDIR] # connect timeout in seconds # default value is 10 # Note: in the intranet network (LAN), 10 seconds is enough. connect_timeout = 10 # network timeout in seconds # default value is 60 network_timeout = 60 # the namespace for FastDIR namespace = fs # config the cluster servers cluster_config_filename = ../fdir/cluster.conf # if use sys lock for file append and truncate to avoid conflict # set true when the files appended or truncated by many nodes (FUSE instances) # default value is false use_sys_lock_for_append = false # if async report file attributes (size, modify time etc.) to the FastDIR server # default value is true async_report_enabled = true # the interval in milliseconds for async report file attributes to the FastDIR server # default value is 100 ms async_report_interval_ms = 100 # the sharding count of hashtable # NO more than 1000 is recommended # default value is 17 hashtable_sharding_count = 17 # the capacity (bucket count) of all sharding hashtables # default value is 1403641 hashtable_total_capacity = 1403641 # the shared allocators for hashtabe entry, task etc. # NO more than the CPU cores is recommended # default value is 11 shared_allocator_count = 11 # set the owner (user and group) of new created files and directories # the values are: ## caller: current user and group from the calling process (default) ## fixed: fixed user and group owner_type = caller # set the user owner (username) of the new created files and directories # this parameter is valid only when owner_type set to fixed # empty for the user who runs the fs_fused program owner_user = # set the group owner (group name) of the new created files and directories # this parameter is valid only when owner_type set to fixed # empty for the group of the user who runs the fs_fused program owner_group = [FastStore] # connect timeout in seconds # default value is 10 # Note: in the intranet network (LAN), 10 seconds is enough. connect_timeout = 10 # network timeout in seconds # default value is 60 network_timeout = 60 # config the cluster servers and groups cluster_config_filename = ../fstore/cluster.conf # the sharding count of hashtable # NO more than 1000 is recommended # default value is 163 hashtable_sharding_count = 163 # the capacity (bucket count) of all sharding hashtables # default value is 1403641 hashtable_total_capacity = 1403641 # the shared allocators for hashtabe entry, task, slice etc. # NO more than the CPU cores is recommended # default value is 17 shared_allocator_count = 17 [write-combine] # if enable write combine feature for FastStore # default value is true enabled = true # the buffer size for write combine # the min value is 64KB and the max value is the data block size such as 4MB # default value is 256KB buffer_size = 256KB # the min wait time in milliseconds for write combine # the min value is 10ms and the max value is 100ms # default value is 20ms min_wait_time_ms = 50 # the max wait time in milliseconds for write combine # the min value is 100ms and the max value is 10000ms # default value is 1000ms max_wait_time_ms = 1000 # the slice size for skipping write combine # skip combine when the slice size >= this parameter # the min value is 64KB and the max value is the data block size such as 4MB # default value is 256KB skip_combine_on_slice_size = 256KB # the merged slice count of last combine for skipping write combine # skip combine when the last combine is in progress and # it's merged slice count <= this parameter # default value is 1 skip_combine_on_last_merged_slices = 1 # the shared locks for timer (timeout manager) # default value is 163 timer_shared_lock_count = 163 # the max waiting slice count in queue for flow control # default value is 16 max_waiting_slice_count = 16 # the thread limit (max threads) for the thread pool # default value is 8 thread_pool_max_threads = 8 # the min idle thread count for the thread pool # default value is 2 thread_pool_min_idle_count = 2 # the max idle time in seconds for the thread pool # the running thread will exit on three conditions: ## 1. this paramter > 0 ## 2. running thread count > thread_pool_min_idle_count ## 3. the thread's idle time exceeds this parameter # default value is 300 (seconds) thread_pool_max_idle_time = 300 [read-ahead] # if enable read ahead feature for FastStore # default value is true enabled = true # the TTL in miliseconds for preread cache # the min value is 100ms and the max value is 10000ms # default value is 1000ms cache_ttl_ms = 1000 # the min buffer size for preread # the min value is 1KB and the max value is 16KB # default value is 4KB min_buffer_size = 4KB # the max buffer size for preread # the min value is 16KB and the max value is 256KB # default value is 128KB max_buffer_size = 128KB # the slice size for skipping read ahead # skip read ahead when the slice size >= this parameter # the min value is 8KB and the max value is 128KB # default value is half of max_buffer_size skip_preread_on_slice_size = 64KB # the shared locks for preread hashtable # default value is 1361 shared_lock_count = 1361 ================================================ FILE: conf/fuse.conf ================================================ # the base path to store log files # this path must exist base_path = /opt/fastcfs/fcfs # the mount point (local path) for FUSE # the local path must exist mountpoint = /opt/fastcfs/fuse # if use busy polling for RDMA network # should set to true for HPC # default value is false busy_polling = false [idempotency] # if enable RPC idempotency for highest level consistency # default value is false enabled = true # thread stack size, should >= 320KB thread_stack_size = 512KB [FastDIR] # the namespace for FastDIR namespace = fs # config the cluster servers cluster_config_filename = ../fdir/cluster.conf # if use sys lock for file append and truncate to avoid conflict # set true when the files appended or truncated by many nodes (FUSE instances) # default value is false use_sys_lock_for_append = false # if async report file attributes (size, modify time etc.) to the FastDIR server # default value is true async_report_enabled = true # the interval in milliseconds for async report file attributes to the FastDIR server # default value is 100 ms async_report_interval_ms = 100 [FastStore] # config the cluster servers and groups cluster_config_filename = ../fstore/cluster.conf [write-combine] # if enable write combine feature for FastStore # default value is true enabled = true [read-ahead] # if enable read ahead feature for FastStore # default value is true enabled = true [FUSE] # if use separate fuse device fd for each thread # set to true for more high performance # default value is false clone_fd = true # access permissions for other users # the values are: ## all for all users ## root for root only ## empty or other values for none allow_others = all # cache time for file attribute in seconds # default value is 1.0s attribute_timeout = 5.0 # cache time for file entry in seconds # default value is 1.0s entry_timeout = 5.0 # if enable kernel writeback cache # set to true for unshared data scene (private data for single node) # default value is true writeback_cache = true # if keep kernel cache for read # set to true for unshared data scene (private data for single node) # should set to false on shared data scene for multi nodes # default value is true kernel_cache = true # if enable x-attribute # default value is false # IMPORTANT NOTE: ### because Linux kernel get the xattr "security.capability" *EVERY* write, ### do NOT change this parameter to true unless your application need ### setxattr, getxattr or listxattr really! xattr_enabled = false ================================================ FILE: conf/papi.conf ================================================ # the base path to store log files # this path must exist base_path = /opt/fastcfs/fcfs # the virtual mount point mountpoint = / [idempotency] # if enable RPC idempotency for highest level consistency # default value is false enabled = true # thread stack size, should >= 320KB thread_stack_size = 512KB [FastDIR] # the namespace for FastDIR namespace = fs # config the cluster servers cluster_config_filename = ../fdir/cluster.conf # if use sys lock for file append and truncate to avoid conflict # set true when the files appended or truncated by many nodes (FUSE instances) # default value is false use_sys_lock_for_append = false # if async report file attributes (size, modify time etc.) to the FastDIR server # default value is true async_report_enabled = true # the interval in milliseconds for async report file attributes to the FastDIR server # default value is 100 ms async_report_interval_ms = 100 [FastStore] # config the cluster servers and groups cluster_config_filename = ../fstore/cluster.conf [write-combine] # if enable write combine feature for FastStore # default value is true enabled = true [read-ahead] # if enable read ahead feature for FastStore # default value is true enabled = true ================================================ FILE: debian/changelog ================================================ fastcfs (5.5.0-1) unstable; urgency=medium * upgrade to 5.5.0-1 -- YuQing <384681@qq.com> Sun, 23 Nov 2025 10:53:21 +0000 fastcfs (5.5.0-1) unstable; urgency=medium * upgrade to 5.5.0-1 -- YuQing <384681@qq.com> Sun, 23 Nov 2025 10:06:42 +0000 fastcfs (5.5.0-1) unstable; urgency=medium * upgrade to 5.5.0-1 -- YuQing <384681@qq.com> Sun, 23 Nov 2025 09:11:49 +0000 fastcfs (5.4.1-1) unstable; urgency=medium * upgrade to 5.4.1-1 -- YuQing <384681@qq.com> Sat, 16 Aug 2025 16:37:13 +0000 fastcfs (5.4.0-1) unstable; urgency=medium * upgrade to 5.4.0-1 -- YuQing <384681@qq.com> Sun, 06 Apr 2025 17:01:49 +0000 fastcfs (5.3.2-1) unstable; urgency=medium * upgrade to 5.3.2-1 -- YuQing <384681@qq.com> Sun, 29 Sep 2024 15:29:40 +0000 fastcfs (5.3.1-1) unstable; urgency=medium * upgrade to 5.3.1-1 -- YuQing <384681@qq.com> Sat, 15 Jun 2024 14:50:36 +0000 fastcfs (5.3.0-1) unstable; urgency=medium * upgrade to 5.3.0-1 -- YuQing <384681@qq.com> Sun, 17 Mar 2024 15:16:01 +0000 fastcfs (5.2.0-1) unstable; urgency=medium * upgrade to 5.2.0-1 -- YuQing <384681@qq.com> Wed, 31 Jan 2024 12:05:19 +0000 fastcfs (5.1.0-1) unstable; urgency=medium * upgrade to 5.1.0-1 -- YuQing <384681@qq.com> Mon, 01 Jan 2024 11:30:03 +0000 fastcfs (5.0.0-3) unstable; urgency=medium * upgrade to 5.0.0-3 -- YuQing <384681@qq.com> Tue, 21 Nov 2023 14:41:40 +0000 fastcfs (5.0.0-2) unstable; urgency=medium * upgrade to 5.0.0-2 -- YuQing <384681@qq.com> Mon, 20 Nov 2023 13:29:08 +0000 fastcfs (5.0.0-1) unstable; urgency=medium * upgrade to 5.0.0-1 -- YuQing <384681@qq.com> Sun, 19 Nov 2023 14:50:41 +0000 fastcfs (4.3.0-1) unstable; urgency=medium * upgrade to 4.3.0-1 -- YuQing <384681@qq.com> Sun, 06 Aug 2023 07:28:20 +0000 fastcfs (4.2.0-1) unstable; urgency=medium * upgrade to 4.2.0-1 -- YuQing <384681@qq.com> Sun, 23 Jul 2023 14:33:49 +0000 fastcfs (4.1.0-1) unstable; urgency=medium * upgrade to 4.1.0-1 -- YuQing <384681@qq.com> Sat, 24 Jun 2023 06:57:02 +0000 fastcfs (4.0.0-1) unstable; urgency=medium * upgrade to 4.0.0-1 -- YuQing <384681@qq.com> Sun, 04 Jun 2023 10:59:03 +0000 fastcfs (3.7.2-1) unstable; urgency=medium * upgrade to 3.7.2-1 -- YuQing <384681@qq.com> Sat, 18 Feb 2023 05:50:44 +0000 fastcfs (3.7.1-1) unstable; urgency=medium * upgrade to 3.7.1-1 -- YuQing <384681@qq.com> Sun, 15 Jan 2023 13:56:32 +0000 fastcfs (3.7.0-1) unstable; urgency=medium * upgrade to 3.7.0-1 -- YuQing <384681@qq.com> Mon, 21 Nov 2022 15:01:45 +0000 fastcfs (3.6.2-1) unstable; urgency=medium * upgrade to 3.6.2-1 -- YuQing <384681@qq.com> Sat, 08 Oct 2022 13:30:32 +0000 fastcfs (3.6.1-1) unstable; urgency=medium * upgrade to 3.6.1-1 -- YuQing <384681@qq.com> Thu, 22 Sep 2022 12:24:49 +0000 fastcfs (3.6.0-1) unstable; urgency=medium * upgrade to 3.6.0-1 -- YuQing <384681@qq.com> Wed, 07 Sep 2022 13:38:59 +0000 fastcfs (3.5.0-2) unstable; urgency=medium * upgrade to 3.5.0-2 -- YuQing <384681@qq.com> Sat, 30 Jul 2022 12:58:43 +0000 fastcfs (3.5.0-1) unstable; urgency=medium * upgrade to 3.5.0-1 -- YuQing <384681@qq.com> Mon, 25 Jul 2022 13:54:45 +0000 fastcfs (3.4.0-2) unstable; urgency=medium * upgrade to 3.4.0-2 -- YuQing <384681@qq.com> Thu, 30 Jun 2022 15:20:18 +0000 fastcfs (3.4.0-1) unstable; urgency=medium * upgrade to 3.4.0-1 -- YuQing <384681@qq.com> Wed, 15 Jun 2022 14:28:17 +0000 fastcfs (3.3.0-1) unstable; urgency=medium * upgrade to 3.3.0-1 -- YuQing <384681@qq.com> Thu, 28 Apr 2022 11:56:12 +0000 fastcfs (3.2.0-1) unstable; urgency=medium [ sungness ] * Update README.md * Update AUTH-zh_CN.md * Update docs style * Update CONFIGURE-zh_CN.md * update docs style * Update fcfs.sh add status command * Update fcfs-ops-tool.md add description for status command * Update fcfs.sh [ YuQing ] * CONFIGURE-zh_CN.md changed for storage engine [ sungness ] * Update fcfs.sh and fcfs_conf.sh add storage.conf for fdir [ YuQing ] * AUTH-zh_CN.md changed * AUTH-zh_CN.md refined * AUTH-zh_CN.md format * fcfs_authd.service rename to fastauth.service * README: description improved * fix permission check for storage pool list * fcfs_pool.c: beautify spool output * fix list_spool and list_gpool return status * [fauth] support regenerate secret key * use -y option to confirm when the user is same with the admin * beautify prompt for -y option * change FUSEOwnerType to FCFSAPIOwnerType * add files src/api/fcfs_posix_api.[hc] * move fcfs_posix_api.[hc] to std/posix_api.[hc] * add files: std/fd_manager.[hc] * add files: std/papi_file.[hc] and std/papi_dir.[hc] * fcfs_write and fcfs_pwrite impl. * fcfs_writev and fcfs_pwritev impl. * fcfs_read[v] and fcfs_pread[v] impl. * impl. fcfs_fstat, fcfs_symlinkat, etc. * rename papi_file.[hc] to papi.[hc] * call stat_dentry_xxx with flags * call link_dentry_xxx with flags * fcfs_api_file.c: check_writable and check_readable * impl. fcfs_utimes, fcfs_rename etc. * call remove_dentry_xxx with flags * impl. fcfs_setxattr, fcfs_getxattr etc. * impl. opendir, readdir, closedir ... * fcntl impl. * chdir and getcwd impl. * wrapper chroot, dup, dup2 etc. * add src/api/tests/test_papi_copy.c and test passed * test_papi_copy.c: support readv & writev * add preload files: types.h and global.[hc] * adapt dlsym in mac OS * preload api wrapper done. * set get size flags for getxattr and listxattr * proxy api correctly (such as use syscall) when not inited * __xstat, __lxstat etc. implement OK. [ vazmin ] * fcfs_authd service rename to fastauth [ YuQing ] * support posix_fallocate, dprintf etc. * support euidaccess and eaccess * preload support posix_fallocate, dprintf etc. * C API fcfs_fopen impl. * remove ctx parameter for posix functions with fd * fdopen, freopen impl. * impl. fputc, fgetc, fread, fwrite etc. * impl. getdelim, getline etc. * support function fcfs_readline * preload for c APIs * change function prototypes for __xmknod and __xmknodat * compile passed in MacOS * impl. __fprintf_chk and __vfprintf_chk * impl. fsync, fdatasync and fcloseall * preload use syscall to forward request * papi.[hc] use fcfs_getcwd for current directory * extract common codes for posix_api_init * impl. fcfs_api_log_client_common_configs and fcfs_posix_api_log_configs * add docs/cluster-expansion-zh_CN.md * docs/cluster-expansion-zh_CN.md refined * docs/cluster-expansion-zh_CN.md refined v2 * docs/cluster-expansion-zh_CN.md refined v3 * upgrade version to 3.2.0 * upgrade versions for RPM spec files * compile passed in CentOS 7.4 * fuse.conf add parameter: xattr_enabled * docs/cluster-expansion-zh_CN.md refined v4 * add link to docs/cluster-expansion-zh_CN.md [ Jerry ] * add mkdocs for documents build [ YuQing ] * add comments for posix_api.h * cluster-expansion-zh_CN.md refined v5 * change version declare for README.md * README refined -- YuQing <384681@qq.com> Sun, 13 Mar 2022 17:07:08 +0800 fastcfs (3.1.0-1) unstable; urgency=medium * upgrade version to 3.1.0 -- YuQing <384681@qq.com> Sat, 15 Jan 2022 20:54:06 +0800 fastcfs (3.0.0-1) unstable; urgency=medium * upgrade version to 3.0.0 -- YuQing <384681@qq.com> Sun, 26 Dec 2021 21:55:51 +0800 fastcfs (2.3.0-1) unstable; urgency=medium * Initial release. -- YuQing <384681@qq.com> Tue, 20 Jul 2021 00:55:33 +0800 ================================================ FILE: debian/compat ================================================ 11 ================================================ FILE: debian/control ================================================ Source: fastcfs Section: admin Priority: optional Maintainer: YuQing <384681@qq.com> Build-Depends: debhelper (>=11~), fastdir-dev (>= 3.2.0) , faststore-dev (>= 3.2.0) , libfuse3-dev (>= 3.10.1) , fastcfs-auth-dev (>=3.6.0) , fastcfs-vote-dev (>=3.6.0) , libfastcommon-dev (>= 1.0.56), libserverframe-dev (>= 1.1.13) Standards-Version: 4.1.4 Homepage: http://github.com/happyfish100/FastCFS/ Package: fastcfs Architecture: any Multi-Arch: foreign Build-Profiles: Depends: fastcfs-fused (= ${binary:Version}), fastcfs-utils (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: the auth client library and config files of FastCFS. a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms. Package: fastcfs-fused Architecture: any Multi-Arch: foreign Build-Profiles: Depends: fastcfs-api-libs (= ${binary:Version}), fuse3 (>= 3.10.1), fastcfs-fuse-config (>= ${fastcfs-fuse-config:Version}), ${misc:Depends}, ${shlibs:Depends} Description: FastCFS fuse Package: fastcfs-utils Architecture: any Multi-Arch: foreign Build-Profiles: Depends: libserverframe (>= ${libserverframe:Version}), ${misc:Depends}, ${shlibs:Depends} Description: FastCFS utils Package: fastcfs-api-libs Architecture: any Multi-Arch: foreign Build-Profiles: Depends: fastdir-client (>= ${fastdir-client:Version}), faststore-client (>= ${faststore-client:Version}), ${misc:Depends}, ${shlibs:Depends} Description: FastCFS api library Package: fastcfs-api-tests Architecture: any Multi-Arch: foreign Build-Profiles: Depends: fastcfs-fuse-config (>= ${fastcfs-fuse-config:Version}), ${misc:Depends}, ${shlibs:Depends} Description: FastCFS api tests Package: fastcfs-api-dev Architecture: any Multi-Arch: foreign Build-Profiles: Depends: fastcfs-api-libs (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: header files of FastCFS api library Package: fastcfs-auth-server Architecture: any Multi-Arch: foreign Build-Profiles: Depends: fastdir-client (>= ${fastdir-client:Version}), fastcfs-auth-config (>= ${fastcfs-auth-config:Version}), ${misc:Depends}, ${shlibs:Depends} Description: FastCFS auth server Package: fastcfs-fuse-config Architecture: any Multi-Arch: foreign Build-Profiles: Depends: faststore-config (>= ${faststore-config:Version}) ${misc:Depends}, ${shlibs:Depends} Description: FastCFS fuse config files for sample Package: fastcfs-auth Architecture: any Multi-Arch: foreign Build-Profiles: Depends: libfastcommon (>= ${libfastcommon:Version}), libserverframe (>= ${libserverframe:Version}), fastcfs-auth-server (= ${binary:Version}), fastcfs-auth-client (= ${binary:Version}), fastcfs-auth-config (>= ${fastcfs-auth-config:Version}), ${misc:Depends} Description: the auth client library and config files of FastCFS. FastCFS is a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms. Package: fastcfs-auth-dev Architecture: any Multi-Arch: foreign Build-Profiles: Depends: fastcfs-auth-client (= ${binary:Version}), ${misc:Depends} Description: header files of FastCFS auth client This package provides the header files of libfcfsauthclient Package: fastcfs-auth-client Architecture: any Multi-Arch: foreign Build-Profiles: Depends: libfastcommon (>= ${libfastcommon:Version}), libserverframe (>= ${libserverframe:Version}), ${misc:Depends}, ${shlibs:Depends} Description: FastCFS auth client FastCFS auth client Package: fastcfs-auth-config Architecture: any Multi-Arch: foreign Build-Profiles: Depends: ${misc:Depends} Description: FastCFS auth config files for sample FastCFS auth config files for sample Package: fastcfs-vote-server Architecture: any Multi-Arch: foreign Build-Profiles: Depends: libserverframe (>= ${libserverframe:Version}), fastcfs-vote-config (>= ${fastcfs-vote-config:Version}), ${misc:Depends}, ${shlibs:Depends} Description: FastCFS vote server Package: fastcfs-vote Architecture: any Multi-Arch: foreign Build-Profiles: Depends: libfastcommon (>= ${libfastcommon:Version}), libserverframe (>= ${libserverframe:Version}), fastcfs-vote-server (= ${binary:Version}), fastcfs-vote-client (= ${binary:Version}), fastcfs-vote-config (>= ${fastcfs-vote-config:Version}), ${misc:Depends} Description: the vote client library and config files of FastCFS. FastCFS is a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms. Package: fastcfs-vote-dev Architecture: any Multi-Arch: foreign Build-Profiles: Depends: fastcfs-vote-client (= ${binary:Version}), ${misc:Depends} Description: header files of FastCFS vote client This package provides the header files of libfcfsauthclient Package: fastcfs-vote-client Architecture: any Multi-Arch: foreign Build-Profiles: Depends: libfastcommon (>= ${libfastcommon:Version}), libserverframe (>= ${libserverframe:Version}), ${misc:Depends}, ${shlibs:Depends} Description: FastCFS vote client FastCFS vote client Package: fastcfs-vote-config Architecture: any Multi-Arch: foreign Build-Profiles: Depends: ${misc:Depends} Description: FastCFS vote config files for sample FastCFS vote config files for sample ================================================ FILE: debian/copyright ================================================ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: fastcfs Source: # # Please double check copyright with the licensecheck(1) command. Files: * Copyright: 2020 YuQing <384681@qq.com> License: AGPL-3.0+ This program is free software: you can use, redistribute, and/or modify it under the terms of the GNU Affero General Public License, version 3 or later ("AGPL"), as published by the Free Software Foundation. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. . You should have received a copy of the GNU Affero General Public License along with this program. If not, see . # License file: LICENSE GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 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. . Preamble . The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. . The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. . When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. . Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. . A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. . The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. . An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. . The precise terms and conditions for copying, distribution and modification follow. . TERMS AND CONDITIONS . 0. Definitions. . "This License" refers to version 3 of the GNU Affero General Public License. . "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. . "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. . To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. . A "covered work" means either the unmodified Program or a work based on the Program. . To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. . To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. . An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. . 1. Source Code. . The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. . A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. . The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. . The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. . The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. . The Corresponding Source for a work in source code form is that same work. . 2. Basic Permissions. . All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. . You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. . Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. . 3. Protecting Users' Legal Rights From Anti-Circumvention Law. . No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. . When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. . 4. Conveying Verbatim Copies. . You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. . You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. . 5. Conveying Modified Source Versions. . You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: . a) The work must carry prominent notices stating that you modified it, and giving a relevant date. . b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". . c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. . d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. . A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. . 6. Conveying Non-Source Forms. . You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: . a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. . b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. . c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. . d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. . e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. . A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. . A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. . "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. . If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). . The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. . Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. . 7. Additional Terms. . "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. . When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. . Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: . a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or . b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or . c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or . d) Limiting the use for publicity purposes of names of licensors or authors of the material; or . e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or . f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. . All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. . If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. . Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. . 8. Termination. . You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). . However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. . Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. . Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. . 9. Acceptance Not Required for Having Copies. . You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. . 10. Automatic Licensing of Downstream Recipients. . Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. . An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. . You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. . 11. Patents. . A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". . A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. . Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. . In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. . If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. . If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. . A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. . Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. . 12. No Surrender of Others' Freedom. . If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. . 13. Remote Network Interaction; Use with the GNU General Public License. . Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. . Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. . 14. Revised Versions of this License. . The Free Software Foundation may publish revised and/or new versions of the GNU Affero 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 Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. . If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. . Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. . 15. Disclaimer of Warranty. . THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. . 16. Limitation of Liability. . IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. . 17. Interpretation of Sections 15 and 16. . If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. . END OF TERMS AND CONDITIONS . How to Apply These Terms to Your New Programs . If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. . To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. . Copyright (C) . This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. . You should have received a copy of the GNU Affero General Public License along with this program. If not, see . . Also add information on how to contact you by electronic and paper mail. . If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. . You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . ================================================ FILE: debian/fastcfs-api-dev.install ================================================ usr/include/fastcfs/api/* ================================================ FILE: debian/fastcfs-api-libs.install ================================================ usr/lib/libfcfsapi.so* ================================================ FILE: debian/fastcfs-api-tests.install ================================================ usr/bin/fcfs_beachmark usr/bin/fcfs_test_file_op usr/bin/fcfs_test_file_copy usr/bin/fcfs_test_papi_copy usr/bin/fcfs_test_read_ahead ================================================ FILE: debian/fastcfs-auth-client.install ================================================ usr/lib/libfcfsauthclient.so* usr/bin/fcfs_user usr/bin/fcfs_pool usr/bin/fauth_list_servers usr/bin/fauth_cluster_stat ================================================ FILE: debian/fastcfs-auth-config.install ================================================ etc/fastcfs/auth/*.conf etc/fastcfs/auth/keys/* ================================================ FILE: debian/fastcfs-auth-dev.install ================================================ usr/include/fastcfs/auth/* ================================================ FILE: debian/fastcfs-auth-server-config.install ================================================ usr/lib/systemd/system/fastauth.service ================================================ FILE: debian/fastcfs-auth-server.dirs ================================================ opt/fastcfs/auth ================================================ FILE: debian/fastcfs-auth-server.install ================================================ usr/bin/fcfs_authd ================================================ FILE: debian/fastcfs-fuse-config.install ================================================ etc/fastcfs/fcfs/*.conf ================================================ FILE: debian/fastcfs-fused.dirs ================================================ opt/fastcfs/fuse opt/fastcfs/fcfs ================================================ FILE: debian/fastcfs-fused.install ================================================ usr/bin/fcfs_fused ================================================ FILE: debian/fastcfs-utils.install ================================================ usr/bin/fcfs_active_test usr/bin/fcfs_pool_stat ================================================ FILE: debian/fastcfs-vote-client.install ================================================ usr/lib/libfcfsvoteclient.so* usr/bin/fvote_cluster_stat ================================================ FILE: debian/fastcfs-vote-config.install ================================================ etc/fastcfs/vote/*.conf ================================================ FILE: debian/fastcfs-vote-dev.install ================================================ usr/include/fastcfs/vote/* ================================================ FILE: debian/fastcfs-vote-server.dirs ================================================ opt/fastcfs/vote ================================================ FILE: debian/fastcfs-vote-server.install ================================================ usr/bin/fcfs_voted ================================================ FILE: debian/rules ================================================ #!/usr/bin/make -f export DESTDIR = $(CURDIR)/debian/tmp export FUSE_CONFDIR=$(DESTDIR)/etc/fastcfs/fcfs/ export AUTH_CONFDIR=$(DESTDIR)/etc/fastcfs/auth/ export VOTE_CONFDIR=$(DESTDIR)/etc/fastcfs/vote/ %: dh $@ override_dh_auto_build: ifneq ($(filter pkg.fastcfs.core,$(DEB_BUILD_PROFILES)),) ./make.sh --exclude=client clean && ./make.sh --exclude=client endif ifneq ($(filter pkg.auth.client,$(DEB_BUILD_PROFILES)),) ./make.sh --module=auth_client clean && ./make.sh --module=auth_client endif ifneq ($(filter pkg.vote.client,$(DEB_BUILD_PROFILES)),) ./make.sh --module=vote_client clean && ./make.sh --module=vote_client endif override_dh_auto_install: ifneq ($(filter pkg.fastcfs.core,$(DEB_BUILD_PROFILES)),) ./make.sh --exclude=client install mkdir -p $(FUSE_CONFDIR) cp conf/*.conf $(FUSE_CONFDIR) cp systemd/fastcfs.service debian/fastcfs-fused.fastcfs.service cp systemd/fastauth.service debian/fastcfs-auth-server.fastauth.service cp systemd/fastvote.service debian/fastcfs-vote-server.fastvote.service endif ifneq ($(filter pkg.auth.client,$(DEB_BUILD_PROFILES)),) ./make.sh --module=auth_client install AUTH_CONFDIR=$(DESTDIR)/etc/fastcfs/auth/ mkdir -p $(AUTH_CONFDIR) cp src/auth/conf/*.conf $(AUTH_CONFDIR) cp -R src/auth/conf/keys $(AUTH_CONFDIR) endif ifneq ($(filter pkg.vote.client,$(DEB_BUILD_PROFILES)),) ./make.sh --module=vote_client install VOTE_CONFDIR=%{buildroot}/etc/fastcfs/vote/ mkdir -p $(VOTE_CONFDIR) cp src/vote/conf/*.conf $(VOTE_CONFDIR) endif dh_auto_install override_dh_installsystemd: dh_installsystemd --package=fastcfs-fused --name=fastcfs --no-start --no-restart-on-upgrade dh_installsystemd --package=fastcfs-auth-server --name=fastauth --no-start --no-restart-on-upgrade dh_installsystemd --package=fastcfs-vote-server --name=fastvote --no-start --no-restart-on-upgrade .PHONY: override_dh_gencontrol override_dh_gencontrol: dh_gencontrol -- -Tdebian/substvars ================================================ FILE: debian/source/format ================================================ 3.0 (quilt) ================================================ FILE: debian/substvars ================================================ libfastcommon:Version=1.0.83 libserverframe:Version=1.2.11 fastcfs-auth-config:Version=2.0.0 fastcfs-vote-config:Version=3.4.0 fastdir-client:Version=5.5.0 faststore-config:Version=1.0.0 faststore-client:Version=5.5.0 fastcfs-fuse-config:Version=1.0.0 ================================================ FILE: debian/watch ================================================ version=3 opts="mode=git" https://github.com/happyfish100/FastCFS.git \ refs/tags/v([\d\.]+) debian uupdate ================================================ FILE: docs/APT-INSTALL-zh_CN.md ================================================ ## 支持Debian 10及以上版本,Ubuntu 18.04及以上版本 和 Deepin 20及以上版本,amd64 和arm64 两种架构。 ### 1. 配置 apt 存储库 配置 apt 存储库和签名密钥,以使系统的包管理器启用自动更新。 ```shell sudo apt-get install curl gpg curl http://www.fastken.cn/aptrepo/packages.fastos.pub | gpg --dearmor > fastos-archive-keyring.gpg sudo install -D -o root -g root -m 644 fastos-archive-keyring.gpg /usr/share/keyrings/fastos-archive-keyring.gpg sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/fastos-archive-keyring.gpg] http://www.fastken.cn/aptrepo/fastos/ fastos main" > /etc/apt/sources.list.d/fastos.list' rm -f fastos-archive-keyring.gpg ``` 如果需要安装调试包,执行: ```shell sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/fastos-archive-keyring.gpg] http://www.fastken.cn/aptrepo/fastos-debug/ fastos-debug main" > /etc/apt/sources.list.d/fastos-debug.list' ``` 然后更新包缓存 ``` sudo apt update ``` ### 2. fastDIR server安装 在需要运行 fastDIR server的服务器上执行: ```shell sudo apt install fastdir-server -y ``` ### 3. faststore server安装 在需要运行 faststore server的服务器上执行: ```shell sudo apt install faststore-server -y ``` ### 4. FastCFS客户端安装 在需要使用FastCFS存储服务的机器(即FastCFS客户端)上执行: ```shell # 注:fastcfs-fused依赖fuse3 sudo apt install fastcfs-fused -y ``` ### 5. Vote server安装(可选) Vote server作为FastCFS多个服务模块共用的选举节点,主要作用是实现双副本防脑裂(即双活互备防脑裂)。 在需要运行选举节点的服务器上执行: ```shell sudo apt install fastcfs-vote-server -y ``` ### 6. Auth server安装(可选) 如果需要存储池或访问控制,则需要安装本模块。 在需要运行 Auth server的服务器上执行: ```shell sudo apt install fastcfs-auth-server -y ``` ### 7. 集群配置(必须) 安装完成后,需要修改对应的配置文件,服务才可以正常启动。请参阅[配置指南](CONFIGURE-zh_CN.md) ### 8. 启动 FastCFS后台程序可通过systemd管理。systemd服务名称如下: * fastdir: 目录服务,对应程序为 fdir_serverd * faststore:存储服务,对应程序为 fs_serverd * fastcfs: FUSE后台服务,对应程序为 fcfs_fused * fastvote: 选举节点,对应程序为 fcfs_voted * fastauth: 认证服务,对应程序为 fcfs_authd 可以使用标准的systemd命令对上述5个服务进行管理,例如: ```shell sudo systemctl restart fastdir sudo systemctl restart faststore sudo systemctl restart fastcfs sudo systemctl restart fastvote sudo systemctl restart fastauth ``` ================================================ FILE: docs/AUTH-zh_CN.md ================================================ # Auth (认证模块)配置及运行 如果你不需要使用存储池或访问权限控制,可以跳过本文档。 本文档以FastCFS RPM包设定的路径(配置文件目录和程序工作目录等)进行说明,如果你采用自助编译安装方式的话,请自行对应。 为了防脑裂,建议配置3个节点(服务器),因认证服务占用资源较少,可以和其他服务共用服务器。 ## 1. Auth配置文件目录结构 ``` /etc/fastcfs/ | |__ auth: 认证中心 |__ keys: 存放用户密钥文件,每个用户对应一个密钥文件,例如 admin.key | |__ session_validate.key: 用于FastDIR和FastStore请求auth服务验证session和权限 |__ cluster.conf: 服务器列表,配置服务器ID、IP和端口 |__ server.conf: fcfs_authd对应的配置文件 |__ client.conf: 客户端配置文件 |__ auth.conf: 认证相关的公共配置文件,在FastDIR和FastStore的cluster.conf中引用 |__ session.conf: session相关配置文件,在Auth、FastDIR和FastStore的server.conf中引用 ``` ## 2. authd程序工作目录 ``` /opt/fastcfs/ | |__ auth |__ authd.pid: 服务进程fcfs_authd的pid文件 |__ logs: 日志文件目录 |__ fcfs_authd.log: 错误日志 |__ slow.log: 慢查询日志 ``` 开启认证功能需要设置认证中心、FastDIR server、FastStore server和FastCFS客户端。 ## 3. 认证中心(authd server)配置 配置文件路径:/etc/fastcfs/auth Auth集群内各个server配置的cluster.conf必须完全一样。 建议配置一次,分发到其他服务器即可。 ### 3.1 把Auth集群中的所有服务实例配置到cluster.conf中 每个Auth服务实例包含2个服务端口:cluster 和 service 一个Auth服务实例需要配置一个[server-$id]的section,其中$id为实例ID。 ### 3.2 配置 server.conf * [cluster] 和 [service] 配置的端口(port)必须与cluster.conf中本机的一致,否则启动会报错 ### 3.3 配置 auth.conf 将 auth_enabled 设置为 true 以开启认证功能 ### 3.4 复制FastDIR server上的如下配置文件到 /etc/fastcfs/fdir/ ``` /etc/fastcfs/fdir/client.conf /etc/fastcfs/fdir/cluster.conf ``` ### 3.5 启动authd authd命令直接重启: ``` /usr/bin/fcfs_authd /etc/fastcfs/auth/server.conf restart ``` 或者系统服务命令启动: ``` sudo systemctl restart fastauth ``` 查看日志: ``` tail /opt/fastcfs/auth/logs/fcfs_authd.log ``` * 第一次运行将自动创建 admin 用户,默认生成的密钥文件名为 **/etc/fastcfs/auth/keys/admin.key** * 友情提示:只在master上生成该密钥文件,请在master上分发密钥文件。 ### 3.6 创建存储池 创建名为 fs 的存储池,配额无限制: ``` fcfs_pool create fs unlimited ``` **_注:存储池名称必须和FastCFS fuse客户端配置文件fuse.conf中的命名空间一致(缺省配置为fs,可按需修改)_** ## 4. FastDIR server ### 4.1 复制auth server上的如下配置文件到 /etc/fastcfs/auth/ ``` /etc/fastcfs/auth/auth.conf /etc/fastcfs/auth/session.conf /etc/fastcfs/auth/cluster.conf /etc/fastcfs/auth/client.conf ``` ### 4.2 复制auth server上的如下密钥文件到 /etc/fastcfs/auth/keys/ ``` /etc/fastcfs/auth/keys/admin.key /etc/fastcfs/auth/keys/session_validate.key ``` 拷贝完成后重启FastDIR服务(fdir_serverd) ## 5. FastStore server * 参见 4. FastDIR server 部分 拷贝完成后重启FastStore服务(fs_serverd) ## 6. FastCFS客户端(fused) * 参见 4. FastDIR server 部分 拷贝完成后重启fuse服务(fcfs_fused) ## 友情提示 * 4 ~ 6部分的配置及启动参见 [配置指南](CONFIGURE-zh_CN.md) ## 7. 命令行工具 * fcfs_user:用户管理,主要包括创建用户、删除用户、设置用户权限(权限包括用户管理、创建存储池等等) * fcfs_pool:储存池管理,主要包括创建pool、删除pool、把pool读写权限授权给其他用户 友情提示:直接执行上述命令可以查看使用帮助。 ## 8. 注意事项 * Auth server依赖FastDIR server,需要先启动FastDIR server,然后启动Auth server。 ================================================ FILE: docs/CONFIGURE-zh_CN.md ================================================ # 配置及运行 本文档以FastCFS RPM包设定的路径(配置文件目录和程序工作目录等)进行说明,如果你采用自助编译安装方式的话,请自行对应。 FastCFS集群配置包含如下五部分: * fastDIR server(服务实例)配置 * faststore server(服务实例)配置 * FastCFS客户端配置 * 选举节点配置(可选) * 认证配置(可选) ## 1. 配置文件目录结构 ``` /etc/fastcfs/ | |__ fcfs: fused服务 | |__ fuse.conf: fcfs_fused对应的配置文件 | |__ fdir: FastDIR目录服务 | |__ cluster.conf: 服务器列表,配置服务器ID、IP和端口 | |__ server.conf: fdir_serverd对应的配置文件 | |__ storage.conf: 持久化存储配置文件 | |__ client.conf: 客户端配置文件 | |__ fstore: faststore存储服务 |__ cluster.conf: 服务器列表(配置服务器ID、IP和端口),并配置服务器分组及数据分组之间的对应关系 |__ storage.conf: 存储路径及空间分配和回收配置 |__ server.conf: fs_serverd对应的配置文件 |__ client.conf: 客户端配置文件 ``` ## 2. 程序工作目录 ``` /opt/fastcfs/ | |__ fcfs | |__ fused.pid: 服务进程fcfs_fused的pid文件 | |__ logs: 日志文件目录 | |__ fcfs_fused.log: 错误日志 | |__ fdir | |__ serverd.pid: 服务进程fdir_serverd的pid文件 | |__ data: 系统数据文件目录,包含集群拓扑结构和binlog | | |__ cluster.info: 集群拓扑信息 | | |__ .inode.sn: 当前inode顺序号 | | |__ binlog: 存放binlog文件 | | | |__ db: 存储引擎默认数据目录 | | |__ trunk: trunk binlog | | |__ inode: segment索引(一个segment包含多个inode,最多65536个,segment id顺序递增) | | |__ ####(如0001、0002等等): 以trunk file方式存放inode数据 | | | |__ logs: 日志文件目录 | |__ fdir_serverd.log: 错误日志 | |__ slow.log: 慢查询日志 | |__ fstore |__ serverd.pid: 服务进程fs_serverd的pid文件 |__ data: 系统数据文件目录,包含集群拓扑结构和binlog | |__ data_group.info: 集群数据分组信息 | |__ .store_path_index.dat: 存储路径索引 | |__ .trunk_id.dat: 当前trunk id信息 | |__ replica: replica binlog,一个DG对应一个子目录 | |__ slice: slice binlog | |__ trunk: trunk binlog | |__ recovery: slave追加master历史数据,一个DG对应一个子目录 | |__ migrate: 迁移出去的DG数据清理 | |__ rebuild: 单盘数据恢复 | |__ db: slice索引存储引擎默认数据目录 | |__ trunk: trunk binlog | |__ block: segment索引(一个segment包含多个block,segment id哈希分布) | |__ ####(如0001、0002等等): 以trunk file方式存放block索引数据 | |__ logs: 日志文件目录 |__ fs_serverd.log: 错误日志 |__ slow.log: 慢查询日志 ``` ## 3. fastDIR server(服务实例)配置 配置文件路径: > /etc/fastcfs/fdir fastDIR集群内各个server配置的cluster.conf必须完全一样,建议配置一次,分发到集群中的所有服务器和客户端即可。 ### 3.1 把fastDIR集群中的所有服务实例配置到cluster.conf中 每个 **fastDIR** 服务实例包含2个服务端口:**cluster** 和 **service**; **cluster.conf** 中配置集群所有实例,一个实例由IP + 端口(包括 cluster和service 2个端口 )组成; 一个fastDIR服务实例需要配置一个[server-$id]的section,其中$id为实例ID(从1开始编号); 如果一台服务器上启动了多个实例,因端口与全局配置的不一致,此时必须指定端口。 一个服务实例的配置示例如下: ``` [server-3] cluster-port = 11015 service-port = 11016 host = 172.16.168.128 ``` ### 3.2 配置 server.conf * 如果需要修改数据存放路径,修改配置项 data_path 为绝对路径 * 本机端口包含cluster和service 2个端口,分配在[cluster] 和 [service] 中配置 * 本机IP + 本机端口必须配置在cluster.conf的一个实例中,否则启动时会出现下面类似的出错信息 ``` ERROR - file: cluster_info.c, line: 119, cluster config file: /etc/fastcfs/fdir/cluster.conf, can't find myself by my local ip and listen port ``` * V3.0支持的持久化特性默认是关闭的,在 [storage-engine] 中设置,详情参见源码的conf目录下的配置示例。 启用存储引擎配置示例: ``` [storage-engine] # if enable the storage engine ### false: use binlog directly ### true: use storage engine for massive files # default value is false enabled = true # the config filename for storage storage_config_filename = storage.conf # the path to store the data files # can be an absolute path or a relative path # the relative path for sub directory under the base_path # this path will be created auto when not exist # default value is db data_path = db # the memory limit ratio for dentry # the valid limit range is [1%, 99%] # default value is 80% memory_limit = 80% ``` ### 3.3 配置 storage.conf * 开启持久化存储特性的情况下,才需要配置storage.conf * 通常采用默认设置即可 fastDIR重启: ``` /usr/bin/fdir_serverd /etc/fastcfs/fdir/server.conf restart ``` 或者: ``` sudo systemctl restart fastdir ``` 查看日志: ``` tail /opt/fastcfs/fdir/logs/fdir_serverd.log ``` ## 4. faststore server(服务实例)配置 配置文件路径: > /etc/fastcfs/fstore faststore集群各个服务实例配置的cluster.conf必须完全一样,建议把cluster.conf一次性配置好,然后分发到集群中的所有服务器和客户端。 ### 4.1 把 faststore 集群中的所有服务实例配置到 cluster.conf 中 每个 **faststore** 服务实例包含3个服务端口:cluster、replica 和 service,和 **fastDIR** 的 cluster.conf 相比,多了一个replica端口,二者配置方式完全相同。 ### 4.2 在 cluster.conf 中配置服务器分组和数据分组对应关系 对于生产环境,为了便于今后扩容,建议数据分组数目至少为64,最好不要超过1024(视业务未来5年发展规模而定)。 ### 4.3 在storage.conf 中配置存储路径等参数 支持配置多个存储路径。为了充分发挥出硬盘性能,建议挂载单盘,每块盘作为一个存储路径。 配置示例: ``` store_path_count = 1 [store-path-1] path = /opt/faststore/data ``` ### 4.4 配置 server.conf * 如果需要修改binlog存放路径,修改配置项 data_path 为绝对路径 * 本机端口包含cluster、replica和service 3个端口,分配在[cluster]、[replica] 和 [service] 中配置 * 本机IP + 本机端口必须配置在cluster.conf的一个实例中,否则启动时会出现类似下面的出错信息 ``` ERROR - file: server_group_info.c, line: 347, cluster config file: /etc/fastcfs/fstore/cluster.conf, can't find myself by my local ip and listen port ``` * V4.0支持的slice索引持久化特性默认是关闭的,在 [storage-engine] 中设置,详情参见源码的conf目录下的配置示例。 启用存储引擎配置示例: ``` [storage-engine] # if enable the storage engine ### false: use binlog directly ### true: use storage engine for huge storage # default value is false enabled = true # the config filename for storage storage_config_filename = dbstore.conf # the path to store the data files # can be an absolute path or a relative path # the relative path for sub directory under the base_path # this path will be created auto when not exist # default value is db data_path = db # the memory limit ratio for slice index # the valid limit range is [1%, 99%] # default value is 60% memory_limit = 60% ``` ### 4.5 配置 dbstore.conf * slice索引开启持久化存储特性的情况下,才需要配置dbstore.conf * 通常采用默认设置即可 faststore重启: ``` /usr/bin/fs_serverd /etc/fastcfs/fstore/server.conf restart ``` 或者: ``` sudo systemctl restart faststore ``` 查看日志: ``` tail /opt/fastcfs/fstore/logs/fs_serverd.log ``` ## 5. FastCFS客户端配置 fused 配置文件路径: > /etc/fastcfs/fcfs _注:如果fused客户端与faststore和fastDIR部署在同一个服务器上,可跳过5.1、5.2节,否则需要将faststore和fastDIR的cluster.conf配置文件复制到fused客户端所在服务器上。_ ### 5.1 复制faststore server上的如下配置文件到 /etc/fastcfs/fstore/ ``` /etc/fastcfs/fstore/cluster.conf ``` ### 5.2 复制fastDIR server上的如下配置文件到 /etc/fastcfs/fdir/ ``` /etc/fastcfs/fdir/cluster.conf ``` ### 5.3 修改fuse挂载点(可选) 如有必要,可修改配置文件 **fuse.conf** 中的 **mountpoint** 参数来指定挂载点: > mountpoint = /opt/fastcfs/fuse fused 重启: > /usr/bin/fcfs_fused /etc/fastcfs/fcfs/fuse.conf restart 或者: > sudo systemctl restart fastcfs 查看日志: > tail /opt/fastcfs/fcfs/logs/fcfs_fused.log 查看fastDIR集群状态: > fdir_cluster_stat 查看单台fastDIR 服务状态: > fdir_service_stat $IP:$port **_友情提示:可以拷贝fdir_cluster_stat 输出的IP和服务端口_** 查看faststore集群状态: > fs_cluster_stat 查看非ACTIVE的服务列表,使用: > fs_cluster_stat -N 使用 -h 查看更多选项 查看FastCFS磁盘使用情况: > df -h /opt/fastcfs/fuse | grep fuse 至此,mountpoint(如:/opt/fastcfs/fuse)可以作为本地文件目录访问了。 ## 6. 选举节点配置(可选) 如果需要实现双副本防脑裂(即双活互备防脑裂),请参阅 [选举节点配置文档](VoteNode-zh_CN.md) ## 7. 认证配置(可选) 如果需要开启存储池或访问权限控制,请参阅 [认证配置文档](AUTH-zh_CN.md) ## 8. 共享数据配置(可选) 将FastCFS作为Oracle RAC等系统的共享存储,请参阅 [共享数据配置指南](shared-storage-guide-zh_CN.md) 祝顺利! have a nice day! ================================================ FILE: docs/Easy-install-detail-zh_CN.md ================================================ # 快速体验FastCFS 以CentOS 8为例,用单个服务节点,以一键安装的方式体验下FastCFS。 此方式仅供体验使用,不适合在生产环境使用. ## 1. 获得最新的代码 ``` git clone https://gitee.com/fastdfs100/FastCFS.git ``` ## 2. 安装 涉及到libfuse的安装更新以及在/usr/local下创建目录,请务必使用sudo或者root的方式安装。 ``` cd FastCFS/ sudo ./helloWorld.sh ``` 安装过程中会有进度提示信息,全自动进行,当看到以下提示时说明安装成功了。 ``` INFO: Copy file fuse.conf to /etc/fastcfs/fcfs/ waiting services ready ... /dev/fuse 38G 0 38G 0% /opt/fastcfs/fuse the mounted path is: /opt/fastcfs/fuse have a nice day! ``` ## 3. 验证:查看安装的文件目录 安装成功后,通过df -h 可以看到新增一个/dev/fuse的设备,默认容量与你的磁盘容量有关,非固定值。比如我的是38G. ``` Filesystem Size Used Avail Use% Mounted on devtmpfs 189M 0 189M 0% /dev tmpfs 219M 0 219M 0% /dev/shm tmpfs 219M 6.6M 212M 4% /run tmpfs 219M 0 219M 0% /sys/fs/cgroup /dev/mapper/cs-root 47G 5.9G 42G 13% / /dev/sda1 1014M 242M 773M 24% /boot tmpfs 44M 4.0K 44M 1% /run/user/1000 /dev/fuse 38G 0 38G 0% /opt/fastcfs/fuse ``` ## 4. 使用:在新挂载的目录/opt/fastcfs/fuse 中,可以进行标准的文件创建/编辑/删除操作 ``` [first@192.168.126.50 /opt/fastcfs/fuse] $ pwd /opt/fastcfs/fuse [first@192.168.126.50 /opt/fastcfs/fuse] $ touch abc // 创建文件 [first@192.168.126.50 /opt/fastcfs/fuse] $ ll total 0 -rw-rw-r--. 1 first first 0 May 14 17:06 abc [first@192.168.126.50 /opt/fastcfs/fuse] $ rm abc // 删除文件 [first@192.168.126.50 /opt/fastcfs/fuse] $ mkdir -p d1/d2/d3 // 创建目录 [first@192.168.126.50 /opt/fastcfs/fuse] $ ll total 0 drwxrwxr-x. 2 first first 0 May 14 17:06 d1 [first@192.168.126.50 /opt/fastcfs/fuse] $ touch d1/d2/d3/123 // 创建文件 [first@192.168.126.50 /opt/fastcfs/fuse] $ ll d1/d2/d3/123 -rw-rw-r--. 1 first first 0 May 14 17:06 d1/d2/d3/123 [first@192.168.126.50 /opt/fastcfs/fuse] $ rm -rf d1 // 删除目录 [first@192.168.126.50 /opt/fastcfs/fuse] $ ll total 0 ``` ## 5. 停止服务 使用fashcfs.sh为管理脚本,可以停止、重启服务: ``` [first@192.168.126.50 ~/FastCFS] $ sudo ./fastcfs.sh stop [sudo] password for first: waiting for pid [2364] exit ... pid [2364] exit. waiting for pid [2379] exit ... pid [2379] exit. waiting for pid [2383] exit ... pid [2383] exit. waiting for pid [2400] exit ... pid [2400] exit. ``` ## 6. 移除安装包恢复初始环境 ``` sudo yum remove FastCFS-auth-server FastCFS-fused fastDIR-server faststore-server -y sudo yum remove fastDIR-config faststore-config FastCFS-auth-config FastCFS-fuse-config -y sudo rm -rf /etc/fastcfs /opt/fastcfs /opt/faststore ``` ================================================ FILE: docs/FAQ-zh_CN.md ================================================ # FAQ ## 1. 服务器最低配置要求 2核CPU、4G内存、10G硬盘 ## 2. 网络连接异常 当日志出现`Connection refused`或者`Transport endpoint is not connected`之类的异常记录。 请检查防火墙设置。FastCFS 共有9个服务端口,这些对应的端口应该打开: * fdir * 默认集群端口 `11011` * 默认服务端口 `11012` * fvote * 默认集群端口 `41011` * 默认服务端口 `41012` * fauth * 默认集群端口 `31011` * 默认服务端口 `31012` * fstore * 默认集群端口 `21014` * 默认副本端口 `21015` * 默认服务端口 `21016` ## 3. 服务有没有启动顺序? 有。应按顺序启动`fvote`(可选),`fdir`, `fauth`(可选),`fstore`,`fuseclient`。 ## 4. FastCFS支持单盘数据恢复吗? ``` 支持。 实现机制:从本组的其他服务器上获取数据,恢复指定硬盘(即存储路径)上的所有数据。 使用场景:硬盘坏掉后更换为新盘,或者因某种原因重新格式化硬盘。 单盘故障恢复后,重启fs_serverd时,加上命令行参数 --data-rebuild , 其中 store_path为要恢复数据硬盘对应的存储路径。 ``` ## 5. FastCFS是如何防脑裂的? ``` leader/master选举时采用过半数机制,一个分组的节点数最好为奇数(比如3个)。 配置项为 quorum,默认值为auto,表示节点数为奇数时开启,为偶数时关闭。 详情参见源码目录下的conf/full/cluster.conf v3.4引入公用选举节点,在偶数节点数(比如双副本)时也可以防脑裂,详情参见配置文档。 ``` ## 6. fdir和fstore多副本(如3副本)情况下,停机后重启不能写入数据 ``` 为了保证数据一致性,当停机时间超过配置参数max_shutdown_duration(默认配置300秒), 需要等待本组的其他服务器在线才可以选出master/leader。 以3副本为例,如果3台服务器停机均超过max_shutdown_duration,而有的服务器没有启动, 那么master/leader选举不出来,导致不能写入数据。此时启动fdir_serverd需要加上命令行 参数--force-master-election,启动fs_serverd需要加上命令行参数--force-leader-election ``` ## 7. 如何验证多个副本的数据一致性? ``` faststore需要源码编译,修改make.sh,将 CFLAGS='-Wall' 修改为: CFLAGS='-Wall -DFS_DUMP_SLICE_FOR_DEBUG=32' 启动fs_serverd,数据将导出到 $base_path/data/dump/slice.index,例如: /opt/fastcfs/fstore/data/dump/slice.index 文件格式: 类型 文件ID block偏移量 slice偏移量 slice长度 数据长度 数据CRC32 可以通过 diff 命令比较同一组服务器下的两个节点导出的slice.index是否相同。 注意:-DFS_DUMP_SLICE_FOR_DEBUG=32 这个编译选项仅供测试环境进行数据一致性验证,请不要在生产环境使用。 ``` ## 8. fuse客户端通过df看到的空间明显大于存储池配额 这是因为客户端没有启用auth,需要将配置文件/etc/fastcfs/auth/auth.conf中的auth_enabled设置为true,修改后重启fcfs_fused生效。 友情提示:如何配置auth服务请参阅 [认证配置文档](AUTH-zh_CN.md) ## 9. FastCFS作为NFS后端存储时NFS client mount失败 出错信息:reason given by server: No such file or directory 解决方法: NFS v3直接使用服务端配置的目录如:/opt/fastcfs/fuse,而v4将服务端配置的路径作为基路径,mount要使用/ NFS挂载默认会使用最新的NFS协议,挂载命令示例(支持v4前提下使用): ``` mount -t nfs -onolock 172.16.168.131:/ /mnt/nfs ``` NFS v3挂载命令示例: ``` mount -t nfs -onolock 172.16.168.131:/opt/fastcfs/fuse /mnt/nfs ``` 指定NFS v4挂载命令示例: ``` mount -t nfs -onolock,nfsvers=4 172.16.168.131:/ /mnt/nfs ``` 友情提示: * CentOS 6需要使用NFS v3挂载 * /etc/exports中需要设置fsid=0,例如:/opt/fastcfs/fuse 172.16.168.130(fsid=0,rw,sync,no_root_squash,no_all_squash) ## 10. 为何删除了足够多的数据,df看到磁盘占用空间不见降低呢? FastCFS基于trunk file进行空间分配,目前trunk file只会增加而不会释放。文件数据删除后空间会回收利用,通过fuse client上df命令可以看到FastCFS的可用空间将增加。 磁盘空间使用率达到阈值后才会启动空间回收,默认阈值为50%,可以根据实际需要调整。配置示例参见faststore源码目录下的conf/full/storage.conf。 ## 11. 如何将fastore的数据清除干净? 一些特殊情况下,需要清除掉faststore的数据,此时要删除faststore的系统目录(server.conf中配置的base_path,默认为/opt/fastcfs/fstore)和用户数据存放目录(默认为/opt/faststore/data,配置多盘的情况要逐一删除数据目录),删除命令示例(文件删除后不可恢复,请再三确认后执行): ``` rm -rf /opt/fastcfs/fstore /opt/faststore/data ``` ## 12. 日志中报没有访问权限,如何进行排查? /opt/fastcfs/fcfs/logs/fcfs_fused.log 中的错误示例: [2023-02-25 06:39:08] ERROR - file: client_proto.c, line: 607, fdir server 192.168.3.210:11012 response message: response status 1, error info: Operation not permitted 问题排查: 依次查看各台fdir server上的日志文件 /opt/fastcfs/fdir/logs/fdir_serverd.log,找到对应的出错信息,然后使用最新版本的fdir_stat 查看对应的完整路径(文件名),根据文件或目录owner、权限、访问用户及其用户组进行排查。 按文件或目录inode查询示例: ``` fdir_stat -Fn fs 9007199660325177 ``` 按父目录inode + 子目录名或者文件名查询示例: ``` fdir_stat -Fn fs 9007199667318991 test ``` 友情提示:如果采用的是装包方式,程序fdir_stat 所在的rpm包名为fastDIR-client,deb包名为fastdir-client ================================================ FILE: docs/INSTALL-zh_CN.md ================================================ # FastCFS安装手册 ## 1、fastcfs.sh 脚本统一安装 通过执行fastcfs.sh脚本,可自动安装软件包,并能根据配置文件模版自动生成集群相关配置文件。 fastcfs.sh 命令参数说明: ``` * install: 安装程序包 * config: 复制配置文件并自动配置为单节点 * start | stop | restart: 服务进程控制 ``` 或执行如下命令(需要root身份执行): ``` ./fastcfs.sh install ./fastcfs.sh config ./fastcfs.sh restart ``` 通过 df 命令查看FastCFS挂载的文件目录: ``` df -h /opt/fastcfs/fuse | grep fuse ``` 以上命令执行成功,你就可以通过本地目录 /opt/fastcfs/fuse 访问FastCFS了。 ## 2、DIY编译和安装 ### 2.1 安装libaio devel包 对于 CentOS或REHL,执行: ``` yum install libaio-devel -y ``` 对于 Unbuntu或Debian,执行: ``` apt install libaio-dev -y ``` 其他Linux系统,需要编译安装 **libaio** 库。 ### 2.2 安装存储插件包和RDMA通信包【可选】 fastDIR 和faststore 的存储插件以及RDMA通信库都是闭源的,可以通过 yum 或 apt 安装使用。 配置我们的yum源和apt源: * yum安装方式(针对CentOS、Rocky、Fedora、RHEL等),参阅 [yum安装文档](YUM-INSTALL-zh_CN.md) * apt安装方式(针对Ubuntu、Debian 和 Deepin),参阅 [apt 安装文档](APT-INSTALL-zh_CN.md) 如果使用存储插件特性,需要通过 yum 或 apt 安装相应的存储插件。 * fastDIR 存储插件包名:libfdirstorage * faststore 存储插件包名:libfsstorage 具备RDMA网络环境,要启动 RDMA 通信方式,需要安装RDMA通信包:libfastrdma ### 2.3 libfastcommon 编译安装 ``` git clone https://gitee.com/fastdfs100/libfastcommon.git; cd libfastcommon/ git checkout master ./make.sh clean && ./make.sh && ./make.sh install ``` 默认安装目录: ``` /usr/lib64 /usr/lib /usr/include/fastcommon ``` ### 2.4 libserverframe 编译安装 ``` git clone https://gitee.com/fastdfs100/libserverframe.git; cd libserverframe/ ./make.sh clean && ./make.sh && ./make.sh install ``` ### 2.5 libdiskallocator 编译安装 ``` git clone https://gitee.com/fastdfs100/libdiskallocator.git; cd libdiskallocator/ ./make.sh clean && ./make.sh && ./make.sh install ``` ### 2.6 Vote Node client 编译安装 ``` git clone https://gitee.com/fastdfs100/FastCFS.git; cd FastCFS/ ./make.sh clean && ./make.sh --module=voteclient && ./make.sh --module=voteclient install ``` ### 2.7 Auth client 编译安装 ``` git clone https://gitee.com/fastdfs100/FastCFS.git; cd FastCFS/ ./make.sh clean && ./make.sh --module=authclient && ./make.sh --module=authclient install ``` ### 2.8 fastDIR 编译安装 ``` git clone https://gitee.com/fastdfs100/fastDIR.git; cd fastDIR/ ./make.sh clean && ./make.sh && ./make.sh install mkdir -p /etc/fastcfs/fdir/ cp conf/*.conf /etc/fastcfs/fdir/ ``` ### 2.9 faststore 编译安装 ``` git clone https://gitee.com/fastdfs100/faststore.git; cd faststore/ ./make.sh clean && ./make.sh && ./make.sh install mkdir -p /etc/fastcfs/fstore/ cp conf/*.conf /etc/fastcfs/fstore/ ``` ### 2.10 fuse客户端编译安装 #### 2.10.1 libfuse 编译安装 libfuse 编译依赖比较复杂,建议使用脚本libfuse_setup.sh一键编译和安装。或者执行如下步骤DIY: 构建libfuse需要先安装meson和ninja。安装meson和ninja需要python3.5及更高版本。 ##### 2.10.1.1 gcc 安装 友情提示:gcc版本必须 >= 4.7.0 Ubuntu下安装命令: ``` apt install gcc g++ -y ``` CentOS下安装命令: ``` yum install gcc gcc-c++ -y ``` ##### 2.10.1.2 pkg-config 和 python安装 需要安装的包名: * pkg-config * python3 * python3-pip Ubuntu下安装命令: ``` apt install pkg-config python3 python3-pip -y ``` CentOS下安装命令: ``` yum install pkgconfig python3 python3-pip -y ``` ##### 2.10.1.3 meson 和 ninja 安装 友情提示: ninja 版本必须 >= 1.7 ``` pip3 install meson pip3 install ninja ``` ##### 2.10.1.4 libfuse 安装 ``` git clone https://gitee.com/mirrors/libfuse.git; cd libfuse/ git checkout fuse-3.16.2 mkdir build/; cd build/ meson .. meson configure -D prefix=/usr meson configure -D examples=false ninja && ninja install sed -i 's/#user_allow_other/user_allow_other/g' /etc/fuse.conf sed -i "s#prefix=.*#prefix=/usr#g" /usr/lib64/pkgconfig/fuse3.pc ``` #### 2.10.2 依赖的项目编译和安装 参见上面的2.3 ~2.8,按照步骤逐一编译和安装 #### 2.10.3 fcfs_fused 编译安装 进入之前clone下来的FastCFS目录,然后执行: ``` ./make.sh --module=fuseclient clean && ./make.sh --module=fuseclient && ./make.sh --module=fuseclient install mkdir -p /etc/fastcfs/fcfs/ mkdir -p /etc/fastcfs/vote/ mkdir -p /etc/fastcfs/auth/ cp conf/*.conf /etc/fastcfs/fcfs/ cp -R src/vote/conf/* /etc/fastcfs/vote/ cp -R src/auth/conf/* /etc/fastcfs/auth/ ``` 配置参见 [配置文档](CONFIGURE-zh_CN.md) ================================================ FILE: docs/INSTALL.md ================================================ ### 1. install libaio devel for CentOS or REHL: ``` yum install libaio-devel -y ``` for Unbuntu or Debian: ``` apt install libaio-dev -y ``` ### 2. libfastcommon ``` git clone git@github.com:happyfish100/libfastcommon.git || git clone https://github.com/happyfish100/libfastcommon.git cd libfastcommon/ git checkout master ./make.sh clean && ./make.sh && ./make.sh install ``` default install directories: ``` /usr/lib64 /usr/lib /usr/include/fastcommon ``` ### 3. libserverframe ``` git clone git@github.com:happyfish100/libserverframe.git || git clone https://github.com/happyfish100/libserverframe.git cd libserverframe/ ./make.sh clean && ./make.sh && ./make.sh install ``` ### 4. libdiskallocator ``` git clone https://gitee.com/fastdfs100/libdiskallocator.git || git clone https://github.com/happyfish100/libdiskallocator.git cd libdiskallocator/ ./make.sh clean && ./make.sh && ./make.sh install ``` ### 5. Vote Node client ``` git clone git@github.com:happyfish100/FastCFS.git || git clone https://github.com/happyfish100/FastCFS.git cd FastCFS/ ./make.sh clean && ./make.sh --module=voteclient && ./make.sh --module=voteclient install ``` ### 6. Auth client ``` git clone git@github.com:happyfish100/FastCFS.git || git clone https://github.com/happyfish100/FastCFS.git cd FastCFS/ ./make.sh clean && ./make.sh --module=authclient && ./make.sh --module=authclient install ``` ### 7. fastDIR ``` git clone git@github.com:happyfish100/fastDIR.git || git clone https://github.com/happyfish100/fastDIR.git cd fastDIR/ ./make.sh clean && ./make.sh && ./make.sh install mkdir -p /etc/fastcfs/fdir/ cp conf/*.conf /etc/fastcfs/fdir/ ``` ### 8. faststore ``` git clone git@github.com:happyfish100/faststore.git || git clone https://github.com/happyfish100/faststore.git cd faststore/ ./make.sh clean && ./make.sh && ./make.sh install mkdir -p /etc/fastcfs/fstore/ cp conf/*.conf /etc/fastcfs/fstore/ ``` ### 9. fuse client #### 9.1. libfuse libfuse requires meson and ninja which require python3.5 or higher version ##### 9.1.1. pkg-config and python packages: pkg-config python3 python3-pip Ubuntu: ``` apt install pkg-config python3 python3-pip -y ``` CentOS: ``` yum install pkgconfig python3 python3-pip -y ``` ##### 9.1.2. meson and ninja ``` pip3 install meson pip3 install ninja ``` ##### 9.1.3. gcc Ubuntu: ``` apt install gcc g++ -y ``` CentOS: ``` yum install gcc gcc-c++ -y ``` ##### 9.1.4. libfuse ``` git clone git@github.com:libfuse/libfuse.git || git clone https://github.com/libfuse/libfuse.git cd libfuse/ git checkout fuse-3.10.1 mkdir build/; cd build/ meson .. meson configure -D prefix=/usr meson configure -D examples=false ninja && ninja install sed -i 's/#user_allow_other/user_allow_other/g' /etc/fuse.conf sed -i "s#prefix=.*#prefix=/usr#g" /usr/lib64/pkgconfig/fuse3.pc ``` #### 9.2. fcfs_fused ``` cd FastCFS/ ./make.sh --module=fuseclient clean && ./make.sh --module=fuseclient && ./make.sh --module=fuseclient install mkdir -p /etc/fastcfs/fcfs/ mkdir -p /etc/fastcfs/vote/ mkdir -p /etc/fastcfs/auth/ cp conf/*.conf /etc/fastcfs/fcfs/ cp -R src/vote/conf/* /etc/fastcfs/vote/ cp -R src/auth/conf/* /etc/fastcfs/auth/ ``` ================================================ FILE: docs/ReleaseNotes.md ================================================ ## FastCFS V3.1.0 发布,主要改进如下: ### 新特性 1. [fdir] 对核心组件FastDIR进行改进,实现了LRU淘汰算法,以有限内存支持海量文件 分布式目录服务FastDIR的淘汰算法具有两大特性: 1. 按目录结构淘汰:先淘汰子节点,然后淘汰父节点; 2. 按数据线程淘汰:每个数据线程作为一个独立的数据单元,数据存取和淘汰均在其数据线程中以无锁方式完成。 ### Bug修复 1. 采用引用计数方式,不再延迟释放 dentry; 2. delay free namespace string for lockless; 3. bugfixed: get trunk_file_size from ini file correctly。 --------------- ## FastCFS V3.0.0 发布,主要改进如下: ### 新特性 1. [fdir] 核心组件FastDIR通过插件方式实现数据存储引擎,采用binlog + 存储引擎插件,按需加载inode数据,单机以有限内存(如64GB)支持100亿级的海量文件 ### Bug修复 1. [fdir] increase/decrease parent's nlink on rename operation 1. [fdir] set dentry->kv_array->count to 0 correctly 1. [fstore] should init barray->count to 0 --------------- ## FastCFS V2.3.0发布,主要改进如下: ### 新特性 1. [fauth] auth server以主备方式支持多节点,避免单点; 1. [fstore&fdir] leader/master选举/切换引入超时机制,选举时长可控; 1. [网络] 网络通信相关改进: 1)握手失败,server端主动断开连接; 2)cluster内部通信server端超时控制; 3)调整网络通信超时默认值(连接超时由10秒调整为2秒,收发数据超时由30秒调整为10秒)。 ### Bug修复 --------------- ## FastCFS V2.2.0 发布,主要改进如下: ### 新特性 1. [fstore] 使用libaio实现异步读,随机读性能提升明显; 1. [fstore] 支持预读机制,顺序读性能提升显著; 1. [csi] k8s驱动(fastcfs-csi)已同步发布, 1. [devOPS] 集群运维工具(fcfs.sh) ### Bug修复 1. [fstore] 修复V2.1.0引入的bug:第一次运行时,一个关键bool变量没有正确赋值; 2. [fuseclient] 修复列举目录导致元数据缓存的一致性问题; 3. [fauth] 修复username和poolname格式修饰符不当导致的乱码问题。 --------------- ## FastCFS V2.0.0 发布,为了更好地对接虚拟机和K8s,V2.0主要实现了存储池和访问权限控制,并支持配额: ### 新特性 1. [fauth] 用户和权限体系 ### Bug修复 --------------- ## FastCFS V1.2.0 发布,主要对数据恢复和master任命机制做了改进,修复了5个稳定性bug,FastCFS的可靠性和稳定性上了一个新台阶: ### 新特性 ### Bug修复 --------------- ## FastCFS V1.0.0 发布: FastCFS 3人小团队历经11个月的研发,推出了FastCFS第一个可用版本,MySQL、PostgresSQL和Oracle可以跑通。 FastCFS服务端两个核心组件是 FastStore 和 FastDIR。FastStore是基于块存储的分布式数据存储服务,其文件block大小为4MB,文件 inode + 文件偏移量 (offset)作为block的key,对应的文件内容为 value,FastStore本质是一个分布式Key-Value系统。 ### 主要特性 1. [fdir] 高性能分布式目录服务,管理文件元数据,支持命名空间 1. [fstore] FastStore是一个分布式Key-Value系统,提供了文件块操作,基于数据块的无中心设计,(类一致性Hash),理论上可以无限扩容; 1. [fuseclient] 基于FUSE的标准文件接口已经实现,可以使用fused模块mount到本地,为数据库、虚拟机以及其他使用标准文件接口的应用提供存储。 ================================================ FILE: docs/TODO-zh_CN.md ================================================ # TODO List 欢迎有志之士参与完善! ## 一、测试与反馈 ## 二、对外接口 ### 1. 对象存储 #### 1.1 S3 #### 1.2 OSS #### 1.3 OBS #### 1.4 COS ### 2. VFS ### 3. NBD ### 4. iSCSI ================================================ FILE: docs/VoteNode-zh_CN.md ================================================ # Vote Node (选举节点)配置及运行 如果你不需要实现双副本防脑裂(即双活互备防脑裂),可以跳过本文档。 本文档以FastCFS RPM包设定的路径(配置文件目录和程序工作目录等)进行说明,如果你采用自助编译安装方式的话,请自行对应。 为了防脑裂,建议配置3个节点(服务器),因选举服务占用资源较少,可以和其他服务共用服务器。 ## 1. Vote Node配置文件目录结构 ``` /etc/fastcfs/ | |__ vote: 选举节点 |__ cluster.conf: 服务器列表,配置服务器ID、IP和端口 |__ server.conf: fcfs_voted对应的配置文件 |__ client.conf: 客户端配置文件 ``` ## 2. voted程序工作目录 ``` /opt/fastcfs/ | |__ vote |__ voted.pid: 服务进程fcfs_voted的pid文件 |__ logs: 日志文件目录 |__ fcfs_voted.log: 错误日志 ``` 开启共用选举服务需要设置选举节点、Auth server、FastDIR server和FastStore server。 ## 3. 选举节点(vote server)配置 配置文件路径:/etc/fastcfs/vote Vote集群内各个server配置的cluster.conf必须完全一样。 建议配置一次,分发到其他服务器即可。 ### 3.1 把Vote集群中的所有服务实例配置到cluster.conf中 每个Vote服务实例包含2个服务端口:cluster 和 service 一个Vote服务实例需要配置一个[server-$id]的section,其中$id为实例ID。 ### 3.2 配置 server.conf * [cluster] 和 [service] 配置的端口(port)必须与cluster.conf中本机的一致,否则启动会报错 ### 3.3 启动voted voted命令直接重启: ``` /usr/bin/fcfs_voted /etc/fastcfs/vote/server.conf restart ``` 或者系统服务命令启动: ``` sudo systemctl restart fastvote ``` 查看日志: ``` tail /opt/fastcfs/vote/logs/fcfs_voted.log ``` ## 4. Auth server ### 4.1 复制vote server上的如下配置文件到 /etc/fastcfs/vote/ ``` /etc/fastcfs/vote/cluster.conf /etc/fastcfs/vote/client.conf ``` 修改 /etc/fastcfs/auth/cluster.conf,将vote_node_enabled 设置为true。 配置片段如下: ``` [master-election] # if enable vote node when the number of servers is even # the default value is false vote_node_enabled = true # the cluster config filename of the vote node # this parameter is valid when vote_node_enabled is true vote_node_cluster_filename = ../vote/cluster.conf ``` 拷贝完成后重启认证服务(fcfs_authd) ## 5. FastDIR server 修改 /etc/fastcfs/fdir/cluster.conf,将vote_node_enabled 设置为true。 * 详情参见 4. Auth server 部分 拷贝和设置完成后重启FastDIR服务(fdir_serverd) ## 6. FastStore server * 参见 4. Auth server 部分 修改 /etc/fastcfs/fstore/cluster.conf,将vote_node_enabled 设置为true。 配置片段如下: ``` [leader-election] # if enable vote node when the number of servers is even # the default value is false vote_node_enabled = true # the cluster config filename of the vote node # this parameter is valid when vote_node_enabled is true vote_node_cluster_filename = ../vote/cluster.conf ``` 拷贝完成后重启FastStore服务(fs_serverd) ## 友情提示 * 4 ~ 6部分的配置及启动参见 [配置指南](CONFIGURE-zh_CN.md) ================================================ FILE: docs/YUM-INSTALL-Diy-5nodes-zh_CN.md ================================================ ### 安装的集群架构 FastCFS支持大规模的集群,下面以最为典型的最小化集群的部署方式,说明下自定义安装的过程。更大规模的安装方式可参照此过程扩展安装。 1. 节点类型和数量:共计5个节点,3个服务端节点(3副本策略),2客户端节点 2. 服务端集群:FastStore和FastDir公用,也可以分开独立部署,生产环境建议分开。本说明为了降低节点数量,采用了公用部署的方式。 3. 客户端:为了说明支持多客户端,使用了2个客户端节点。生产环境中如果只需要1个客户端,也是没问题的。 5个节点的集群架构图 ### 前置依赖 先安装FastOS.repo yum源,FastCFS所需要的pkg都在此源中,安装成功后就可以安装FastCFS相关软件包了。 - CentOS 7等el7系列的Linux发行版 ``` rpm -ivh http://www.fastken.cn/yumrepo/el7/noarch/FastOSrepo-1.0.1-1.el7.noarch.rpm ``` - CentOS 8等el8系列的Linux发行版 ``` rpm -ivh http://www.fastken.cn/yumrepo/el8/noarch/FastOSrepo-1.0.1-1.el8.noarch.rpm ``` 友情提示:支持的Linux发行版以及与el7或el8的对应关系参见 [yum安装方式](YUM-INSTALL-zh_CN.md) ### 服务端集群安装 在192.168.126.[101,102,103]上分别安装FastStore、FastDIR 两个服务. #### 安装FastDIR ``` yum install fastDIR-server ``` 安装过程中会安装所依赖的包,选y就可以了. ``` ================================================================================================================== Package Architecture Version Repository Size ================================================================================================================== Installing: fastDIR-server x86_64 2.0.1-1.el8 FastOS 71 k Installing dependencies: FastCFS-auth-client x86_64 2.0.1-1.el8 FastOS 36 k FastCFS-auth-config x86_64 2.0.1-1.el8 FastOS 10 k fastDIR-config x86_64 2.0.1-1.el8 FastOS 9.5 k libfastcommon x86_64 1.0.50-1.el8 FastOS 140 k libserverframe x86_64 1.1.7-1.el8 FastOS 47 k Transaction Summary ================================================================================================================== Install 6 Packages Total download size: 313 k Installed size: 711 k Is this ok [y/N]: y // 安装过程中会安装所依赖的包,选y就可以了. ``` 安装完毕后,在/etc/fastcfs 下可看到fdir的目录,存储的是FastDIR的配置文件. 配置项的内容,后续会说明。 ``` $ ll /etc/fastcfs/fdir/ total 16 -rw-r--r--. 1 root root 1565 May 9 13:49 client.conf -rw-r--r--. 1 root root 753 May 9 13:49 cluster.conf -rw-r--r--. 1 root root 5242 May 9 13:49 server.conf ``` #### 安装FastStore ``` yum install faststore-server ``` 再说明下:实际生产环境部署中,FastDIR是和FastStore两个服务可以分开部署的,不需要再同一台服务器上。 ``` ================================================================================================================== Package Architecture Version Repository Size ================================================================================================================== Installing: faststore-server x86_64 2.0.1-1.el8 FastOS 134 k Installing dependencies: faststore-config x86_64 2.0.1-1.el8 FastOS 11 k Transaction Summary ================================================================================================================== Install 2 Packages Total download size: 145 k Installed size: 422 k Is this ok [y/N]: y // 安装过程中会安装所依赖的包,选y就可以了. ``` 安装完毕后,在/etc/fastcfs 下可看到fstore目录,存储的是FastStore的配置文件。配置项的内容,后续会说明。 ``` $ ll /etc/fastcfs/fstore/ total 20 -rw-r--r--. 1 root root 1577 May 9 13:52 client.conf -rw-r--r--. 1 root root 3256 May 9 13:52 cluster.conf -rw-r--r--. 1 root root 4965 May 9 13:52 server.conf -rw-r--r--. 1 root root 2609 May 9 13:52 storage.conf ``` ### 安装客户端 在192.168.126.[201,202]上安装Client包, 包名为FastCFS-fused. ``` yum install FastCFS-fused -y ``` 安装完毕后,在/etc/fastcfs 下可看到以下目录,存储是对应服务的配置文件. ``` $ ll /etc/fastcfs/ drwxr-xr-x. 3 root root 113 May 14 15:20 auth drwxr-xr-x. 2 root root 23 May 14 15:20 fcfs drwxr-xr-x. 2 root root 64 May 14 15:20 fdir drwxr-xr-x. 2 root root 84 May 14 15:20 fstore ``` **为什么在Client的节点上,需要有fdir和fstore的目录呢?因为FastCFS是无中心配置的,Client需要知道fdir和fstore的集群情况,所以就需要fdir和fstore的配置文件。** ### 自定义安装配置 #### 说明 FastCFS并没有统一的配置中心,需要在各个节点上单独部署配置文件。配置文件分为两大类:集群配置文件和服务配置文件。 1. **集群配置文件:** 指的是描述 FastDir 和 FastStore 的配置文件,入口文件名称为cluster.conf . 该配置文件中设定是集群的参数,如服务节点的IP和端口号,服务节点的数量等。 cluster.conf 文件全局统一,各个节点上的内容是相同的, FastDir 和 FastStore 有各自的cluster.conf文件。 2. **服务配置文件:** 指的是服务本身的配置文件,入口文件名称server.conf 如线程数量、链接数量、缓冲区大小、日志配置等。 服务配置文件的内容,可以全局不统一。不过从集群的角度考虑,如果各服务器配置相同,最好是统一的。 #### FastDIR 配置 1. **配置文件说明** ``` $ ll /etc/fastcfs/fdir/ -rw-r--r--. 1 root root 1565 May 9 13:49 client.conf // 暂时不使用,不用关注 -rw-r--r--. 1 root root 753 May 9 13:49 cluster.conf // 集群配置文件 -rw-r--r--. 1 root root 5242 May 9 13:49 server.conf // 服务配置文件 ``` 2. **集群配置文件** :在101节点上,修改/etc/fastcfs/fdir/cluster.conf 文件,修改为正确的IP地址,修改后的内容如下 ``` 1 # config the auth config filename 2 auth_config_filename = /etc/fastcfs/auth/auth.conf 3 4 5 [group-cluster] 6 # the default cluster port 7 port = 11011 // 集群之间的通信使用的默认端口号,不用修改 8 9 [group-service] 10 # the default service port 11 port = 11012 // 对client提供服务,使用的默认端口号,不用修改 12 13 # config a server instance 14 # section format: [server-$id] 15 # server id is a 32 bits natural number (1, 2, 3 etc.), 16 [server-1] 17 18 # format: host[:port] 19 # host can be an IP address or a hostname 20 # IP address is recommended 21 # can occur more than once 22 host = 192.168.126.101 // 节点101的地址,使用默认的端口号 23 24 [server-2] 25 # cluster-port = 11013 26 # service-port = 11014 27 host = 192.168.126.102 // 节点102的地址,使用默认的端口号 28 29 [server-3] 30 # cluster-port = 11015 31 # service-port = 11016 32 host = 192.168.126.103 // 节点103的地址,使用默认的端口号 ``` 3. **服务配置文件** : 在101节点上,/etc/fastcfs/fdir/server.conf, 该文件配置项较多,但默认不用任何调整,确认文件底部的[service]区内容就好. ``` 180 [cluster] 181 # bind an address of this host 182 # empty for bind all addresses of this host 183 bind_addr = 184 185 # the listen port 186 port = 11011 187 188 # the accept thread count 189 # default value is 1 which is recommended 190 accept_threads = 1 191 192 # the network thread count 193 # these threads deal network io 194 # dispatched by the incoming socket fd 195 # default value is 4 196 work_threads = 2 197 198 [service] 199 bind_addr = // 保持为空,FastDIR 会自动使用本机的IP地址 200 port = 11012 // 确保与cluster.conf 中 group-service 的设定值相同 201 accept_threads = 1 202 work_threads = 4 ``` 4. 把cluster.conf文件复制到102-103,201-202 这5台服务器上,保证所有节点都能知道集群配置 ``` scp /etc/fastcfs/fdir/cluster.conf 192.168.126.102:/etc/fastcfs/fdir/ scp /etc/fastcfs/fdir/cluster.conf 192.168.126.103:/etc/fastcfs/fdir/ scp /etc/fastcfs/fdir/cluster.conf 192.168.126.201:/etc/fastcfs/fdir/ scp /etc/fastcfs/fdir/cluster.conf 192.168.126.202:/etc/fastcfs/fdir/ ``` #### FastStore 配置 1. **配置文件说明** ``` $ ll /etc/fastcfs/fstore -rw-r--r--. 1 root root 1577 May 9 13:52 client.conf // 暂时不使用,不用关注 -rw-r--r--. 1 root root 3253 May 14 13:08 cluster.conf // 集群配置文件 -rw-r--r--. 1 root root 4965 May 9 13:52 server.conf // 服务配置文件 -rw-r--r--. 1 root root 2609 May 9 13:52 storage.conf // 服务配置文件 ``` 2. **集群配置文件** :在101节点上,修改/etc/fastcfs/fstore/cluster.conf 文件,仅需要修改server_ids的值和[group-cluster]部分,其它参数不需要调整。修改后的内容如下. ``` 57 [server-group-1] 58 59 # config one or more server id(s) from servers.conf 60 ## multiple server ids separated by comma(s). 61 ## [start, end] for range, including start and end. 62 # this parameter can occurs more than once. 63 # server_ids = [1, 3] 64 server_ids = [1,3] // 使用了3副本的策略,有3个服务节点101/102/103,所以设定为[1,3] 65 66 # the data group id based 1. the formats as: 67 ## * multiple data group ids separated by comma(s). 68 ## * [start, end] for range, including start and end. 69 # this parameter can occurs more than once. 70 data_group_ids = [1, 32] 71 data_group_ids = [33, 64] 72 73 74 [group-cluster] 75 # the default cluster port 76 port = 21014 // 集群之间的通信使用的默认端口号,不用修改 77 78 [group-replica] 79 # the default replica port 80 port = 21015 // Master-Slave 之间同步数据使用的默认端口号,不用修改 81 82 [group-service] 83 # the default service port 84 port = 21016 // 对Client提供服务,使用的默认端口号,不用修改 85 86 # config a server 87 # section format: [server-$id] 88 # server id is a 32 bits natural number (1, 2, 3 etc.), 89 [server-1] 90 91 # format: host[:port] 92 # host can be an IP address or a hostname 93 # IP address is recommended 94 # can occur more than once 95 host = 192.168.126.101 // 节点101的地址,使用默认的端口号 96 97 [server-2] 99 host = 192.168.126.102 // 节点102的地址,使用默认的端口号 100 101 [server-3] 103 host = 192.168.126.103 // 节点103的地址,使用默认的端口号 ``` 3. **服务配置文件** 在101节点上,/etc/fastcfs/fstore/server.conf 和 /etc/fastcfs/fstore/storage.conf. 默认均需要修改,只需确认下server.conf的配置是否正确 ``` 190 [replica] 191 bind_addr = 192 port = 21015 // 保持与fstore/server.conf 中 [group-replica]设定的port值相同 193 accept_threads = 1 194 work_threads = 4 195 196 [service] 197 bind_addr = 198 port = 21016 // 保持与fstore/server.conf 中 [group-service]设定的port值相同 199 accept_threads = 1 200 work_threads = 4 ``` 4. 把cluster.conf文件复制到102-103,201-202 这5台服务器上,保证所有节点都能知道集群配置 ``` scp /etc/fastcfs/fstore/cluster.conf 192.168.126.102:/etc/fastcfs/fstore/ scp /etc/fastcfs/fstore/cluster.conf 192.168.126.103:/etc/fastcfs/fstore/ scp /etc/fastcfs/fstore/cluster.conf 192.168.126.201:/etc/fastcfs/fstore/ scp /etc/fastcfs/fstore/cluster.conf 192.168.126.202:/etc/fastcfs/fstore/ ``` #### FastClient 配置 1. Client 需要知道FastDIR和FastStore的集群配置数据,在上述scp过程中,已经分别scp到了对应的目录下,不需要修改. 2. Client 自身的配置文件为 /etc/fastcfs/fcfs/fuse.conf 属于服务配置文件,也不需要修改. ### 启动服务 安装并配置完成后,可以启动服务并验证. 1. **关闭防火墙:** CentOS 8 默认情况下开启防火墙,会导致各节点之间无法访问,可暂关闭防火墙。 ``` sudo systemctl stop firewalld.service ``` 2. **启动FastDir:*** 在101/102/103 三个节点上,执行下列命令启动. 注意必须是3个节点都启动。 ``` sudo systemctl restart fastdir ``` 或者 ``` sudo /usr/bin/fdir_serverd /etc/fastcfs/fdir/server.conf restart ``` 查看日志,确认启动成功. ``` $ tail /opt/fastcfs/fdir/logs/fdir_serverd.log [2021-05-14 15:39:36] ERROR - file: connection_pool.c, line: 140, connect to server 192.168.126.102:11011 fail, errno: 113, error info: No route to host [2021-05-14 15:39:36] ERROR - file: connection_pool.c, line: 140, connect to server 192.168.126.103:11011 fail, errno: 113, error info: No route to host [2021-05-14 15:39:36] INFO - file: cluster_relationship.c, line: 668, round 19th select master, alive server count: 1 < server count: 3, the candidate server status: 0 (INIT) does not match the selection rule. you must start ALL servers in the first time, or remove the deprecated server(s) from the config file. try again after 32 seconds. [2021-05-14 15:40:01] INFO - file: cluster_relationship.c, line: 474, the master server id: 3, ip 192.168.126.103:11011 ``` 日志中有error提示,是因为防火墙导致的3个节点无法互相通信产生的,关闭防火墙后FastDIR会自动进行尝试链接。从最后一行INFO日志可看到,服务已经启动成功了,并且选择了103节点作为Master. 3. **启动FastStore:** 与FastDir的启动方式类似,在101/102/103 三个节点上,执行下列命令启动. 注意必须是3个节点都启动。 ``` sudo systemctl restart faststore ``` 或者 ``` sudo /usr/bin/fs_serverd /etc/fastcfs/fstore/server.conf restart ``` 查看日志,确认启动成功. ``` [2021-05-14 16:06:08.326966] INFO - file: replication/replication_processor.c, line: 419, connect to replication peer id: 3, 192.168.126.103:21015 successfully [2021-05-14 16:06:08.344415] INFO - file: replication/replication_processor.c, line: 419, connect to replication peer id: 3, 192.168.126.103:21015 successfully [2021-05-14 16:06:08.368437] INFO - file: replica_handler.c, line: 598, replication peer id: 1, 192.168.126.101:21014 join in [2021-05-14 16:06:09.942673] INFO - file: replica_handler.c, line: 598, replication peer id: 1, 192.168.126.101:21014 join in [2021-05-14 16:06:10.374344] INFO - file: recovery/binlog_fetch.c, line: 589, data group id: 1, wait replica channel of master id 1 ready success, waiting count: 2 [2021-05-14 16:06:10.415848] INFO - file: recovery/binlog_fetch.c, line: 589, data group id: 6, wait replica channel of master id 1 ready success, waiting count: 2 ``` 看到以上类似的日志,即是启动成功了。 4. **启动FastClient:** 在201、202 节点上启动Client. ``` sudo systemctl restart fastcfs ``` 或者 ``` sudo /usr/bin/fcfs_fused /etc/fastcfs/fcfs/fuse.conf restart ``` 查看日志,确认启动成功 ``` $ tail /opt/fastcfs/fcfs/logs/fcfs_fused.log [2021-05-14 16:14:15] INFO - file: sf_nio.c, line: 205, connect to server 192.168.126.101:11012 successfully [2021-05-14 16:14:15] INFO - file: sf_nio.c, line: 205, connect to server 192.168.126.103:11012 successfully [2021-05-14 16:14:15] INFO - file: sf_connection_manager.c, line: 827, [FastDIR] connection manager thread start [2021-05-14 16:14:15] INFO - file: sf_connection_manager.c, line: 827, [FastStore] connection manager thread start [2021-05-14 16:14:55] INFO - file: sf_nio.c, line: 205, connect to server 192.168.126.101:21016 successfully ``` 查看文件目录是否成功挂载 ``` $ df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 189M 0 189M 0% /dev tmpfs 219M 0 219M 0% /dev/shm tmpfs 219M 6.6M 212M 4% /run tmpfs 219M 0 219M 0% /sys/fs/cgroup /dev/mapper/cs-root 47G 4.9G 43G 11% / /dev/sda1 1014M 242M 773M 24% /boot tmpfs 44M 4.0K 44M 1% /run/user/1000 /dev/fuse 38G 0 38G 0% /opt/fastcfs/fuse // 这是即是FastCFS挂载的根目录 ``` 5. 验证 :在201节点上创建文件 ``` [first@192.168.126.201 /opt/fastcfs/fuse] $ mkdir -p abc/123 [first@192.168.126.201 /opt/fastcfs/fuse] $ touch abc/123/demo.txt [first@192.168.126.201 /opt/fastcfs/fuse] $ vim abc/123/demo.txt [first@192.168.126.201 /opt/fastcfs/fuse] $ ll abc/123 total 1 -rw-rw-r--+ 1 first first 13 May 14 16:22 demo.txt ``` 在202节点上查看该文件,可见文件已经创建成功了 ``` [first@192.168.126.202 /opt/fastcfs/fuse] $ tail abc/123/demo.txt hello world [first@192.168.126.202 /opt/fastcfs/fuse] ``` ### 服务监控 1. 查看FastDir集群状态 ``` $ fdir_cluster_stat server_id: 1, host: 192.168.126.101:11012, status: 23 (ACTIVE), is_master: 0 server_id: 2, host: 192.168.126.102:11012, status: 23 (ACTIVE), is_master: 0 server_id: 3, host: 192.168.126.103:11012, status: 23 (ACTIVE), is_master: 1 server count: 3 ``` 2. 查看单个FastDir节点状态 ``` [first@192.168.126.201 /etc/fastcfs/fcfs] $ fdir_service_stat 192.168.126.101:11012 server_id: 1 status: 23 (ACTIVE) is_master: false connection : {current: 4, max: 4} binlog : {current_version: 24} dentry : {current_inode_sn: 1000009, ns_count: 1, dir_count: 3, file_count: 1} ``` 3. 查看FastStore集群状态 ``` $ fs_cluster_stat | more data_group_id: 1 server_id: 1, host: 192.168.126.101:21016, status: 5 (ACTIVE), is_preseted: 1, is_master: 1, data_version: 0 server_id: 2, host: 192.168.126.102:21016, status: 5 (ACTIVE), is_preseted: 0, is_master: 0, data_version: 0 server_id: 3, host: 192.168.126.103:21016, status: 5 (ACTIVE), is_preseted: 0, is_master: 0, data_version: 0 data_group_id: 2 server_id: 1, host: 192.168.126.101:21016, status: 5 (ACTIVE), is_preseted: 0, is_master: 0, data_version: 0 server_id: 2, host: 192.168.126.102:21016, status: 5 (ACTIVE), is_preseted: 1, is_master: 1, data_version: 0 ..................... ``` 4. 查看磁盘使用情况 ``` df -h ``` ================================================ FILE: docs/YUM-INSTALL-zh_CN.md ================================================ ## yum安装方式 yum 安装方式支持intel的x86_64和ARM的aarch64架构,主要用于测试和生产环境搭建。 支持的Linux发行版如下: * CentOS & CentOS Stream * Fedora * Rocky * Anolis * AlmaLinux * RHEL (Red Hat Enterprise Linux) * Oracle Linux * Amazon Linux * Alibaba Cloud Linux * openEuler * Kylin * UOS ### 1. 安装FastOS.repo 先安装FastOS.repo yum源,然后就可以安装FastCFS相关软件包了。 CentOS 7、RHEL 7、Oracle Linux 7、Alibaba Cloud Linux 2、Anolis OS 7、AlmaLinux 7、Amazon Linux 2、Fedora 27及以下版本: ``` rpm -ivh http://www.fastken.cn/yumrepo/el7/noarch/FastOSrepo-1.0.1-1.el7.noarch.rpm ``` CentOS 8、Rocky Linux 8、RHEL 8、Oracle Linux 8、Alibaba Cloud Linux 3、Anolis OS 8、AlmaLinux 8、openEuler [20, 23]、Kylin V10、UOS 20、Amazon Linux 3、Fedora [28, 37]: ``` rpm -ivh http://www.fastken.cn/yumrepo/el8/noarch/FastOSrepo-1.0.1-1.el8.noarch.rpm ``` CentOS Stream 9、Rocky Linux 9、RHEL 9、Oracle Linux 9、Alibaba Cloud Linux 4、Anolis OS 23、AlmaLinux 9、openEuler 24.03、Kylin V11、Amazon Linux 2023、Fedora 38及以上版本: ``` rpm -ivh http://www.fastken.cn/yumrepo/el9/noarch/FastOSrepo-1.0.1-1.el9.noarch.rpm ``` ### 2. fastDIR server安装 在需要运行 fastDIR server的服务器上执行: ``` yum install fastDIR-server -y ``` ### 3. faststore server安装 在需要运行 faststore server的服务器上执行: ``` yum install faststore-server -y ``` ### 4. FastCFS客户端安装 在需要使用FastCFS存储服务的机器(即FastCFS客户端)上,除了openEuler之外的其他Linux发行版执行: ``` yum install FastCFS-fused -y ``` 对于openEuler 20.03和Kylin V10,因需安装的fuse3和系统已有的fuse在依赖关系上存在冲突,需要用rpm命令强制安装fuse3,执行: ``` arch=$(uname -r | awk -F '.' '{print $NF}'); dist=el8; ver='3.16.2-2'; rpm -ivh http://www.fastken.cn/yumrepo/$dist/$arch/fuse3-libs-$ver.$dist.$arch.rpm \ http://www.fastken.cn/yumrepo/$dist/$arch/fuse-common-$ver.$dist.$arch.rpm \ http://www.fastken.cn/yumrepo/$dist/$arch/fuse3-$ver.$dist.$arch.rpm --force --nodeps; yum install FastCFS-fused -y ``` 对于openEuler 22.03及更高版本,直接执行: ``` yum install FastCFS-fused -y ``` ### 5. Vote server安装(可选) Vote server作为FastCFS多个服务模块共用的选举节点,主要作用是实现双副本防脑裂(即双活互备防脑裂)。 在需要运行选举节点的服务器上执行: ``` yum install FastCFS-vote-server -y ``` ### 6. Auth server安装(可选) 如果需要存储池或访问控制,则需要安装本模块。 在需要运行 Auth server的服务器上执行: ``` yum install FastCFS-auth-server -y ``` 安装完成后,需要修改对应的配置文件,服务才可以正常启动。请参阅[配置指南](CONFIGURE-zh_CN.md) FastCFS后台程序可通过systemd管理。systemd服务名称如下: ``` * fastdir: 目录服务,对应程序为 fdir_serverd * faststore:存储服务,对应程序为 fs_serverd * fastcfs: FUSE后台服务,对应程序为 fcfs_fused * fastvote: 选举节点,对应程序为 fcfs_voted * fastauth: 认证服务,对应程序为 fcfs_authd ``` 可以使用标准的systemd命令对上述5个服务进行管理,例如: ``` systemctl restart fastdir systemctl restart faststore systemctl restart fastcfs systemctl restart fastvote systemctl restart fastauth ``` ### 6. 一个包含5个节点的安装过程的详细文档 请参与[5节点安装文档](YUM-INSTALL-Diy-5nodes-zh_CN.md) ================================================ FILE: docs/benchmark-step-by-step.md ================================================ ## FastCFS性能测试参考文档 服务器之间需要实现免密登录,集群部署参见:[FastCFS集群部署工具介绍](fcfs-ops-tool-zh_CN.md) ### 集群快速部署 在施压服务器(即client机器)上执行如下命令: ``` curl -o /usr/bin/fcfs.sh http://fastken.cn/fastcfs/ops/fcfs.sh && sudo chmod +x /usr/bin/fcfs.sh; curl -o /usr/bin/fcfs_conf.sh http://fastken.cn/fastcfs/ops/fcfs_conf.sh && sudo chmod +x /usr/bin/fcfs_conf.sh; mkdir -p fastcfs-ops && cd fastcfs-ops; server_ips='请替换为西文逗号分隔的服务器IP列表'; client_ip=$(ip addr | grep -w inet | grep -v 127.0.0.1 | awk '{print $2}' | tr -d 'addr:' | awk -F '/' '{print $1}' | head -n 1); cat > fcfs_conf.settings < fcfs.settings <= this parameter, # otherwise switch to Linux epoll # default value is 10240 polling_switch_on_iops = 1024 ``` 修改conf/fcfs/fuse.conf的配置项,配置示例如下: ``` # the mount point (local path) for FUSE # the local path must exist mountpoint = /opt/fastcfs/fuse # if use busy polling for RDMA network # should set to true for HPC # default value is false busy_polling = true [read-ahead] # if enable read ahead feature for FastStore # default value is true enabled = false ``` 首次部署: ``` fcfs.sh setup; ``` 修改配置后重新部署: ``` fcfs.sh config; fcfs.sh restart; ``` ### 安装压测工具 CentOS、Alibaba Cloud Linux等发行版: ``` yum install fio FastCFS-api-tests -y ``` Ubuntu 或者Debian: ``` apt install fio fastcfs-api-tests -y ``` ### fio压测示例 ``` cd /opt/fastcfs/fuse; jobnum=4; rw=randread; out_path=FastCFS-fio/$rw-$jobnum; log_file=$out_path/fio; mkdir -p $out_path; fio --filename_format=test_file.\$jobnum --direct=1 --rw=$rw --thread --numjobs=$jobnum --iodepth=16 --ioengine=psync --bs=4k --group_reporting --name=FastCFS --loops=1000 --log_avg_msec=500 --write_bw_log=$log_file --write_lat_log=$log_file --write_iops_log=$log_file --runtime=300 --size=256M ``` ### fcfs_beachmark压测示例 ``` fcfs_beachmark -m randread -s 256M -T 4 -t 300 -f /opt/fastcfs/fuse/test_file ``` ================================================ FILE: docs/benchmark.md ================================================ ## FastCFS vs. Ceph benchmark under 3 nodes cluster (three copies of data)
read/write type fio jobs   IOPS(4KB Block) ratio  
FastCFS   Ceph  
sequential write 4   32,256   5,120   630%  
8   55,296   8,371   661%  
16   76,800   11,571   664%  
rand write 4   6,374   4,454   143%  
8   11,264   6,400   176%  
16   16,870   7,091   238%  
sequential read 4   34,880   14,848   235%  
8   62,751   24,883   252%  
16   86,508   38,912   222%  
rand read 4   12,527   12,160   103%  
8   23,610   22,220   106%  
16   41,790   36,608   114%  
more info see Chinese doc: [FastCFS vs. Ceph benchmark detail](benchmark-20210621.pdf) ================================================ FILE: docs/cluster-expansion-zh_CN.md ================================================ # FastCFS集群扩容手册 FastCFS集群三个服务组件:fauth、fdir 和fstore,下面将分别介绍这三个服务组件的扩容方法和步骤。 ## 1. fauth(认证服务) 认证服务的用户和权限等数据保存在fdir中,服务采用热备模式,建议部署2 ~ 3个服务器(节点),主节点失效后会自动切换到备用节点。 因认证服务器自身不保存数据,因此可以根据实际需要随时增加或减少认证服务器。 ## 2. fdir(目录服务) FastCFS目录服务用于文件元数据管理,目前只支持一组服务器,使用常规服务器(如64GB内存 + SSD)可以支持百亿级文件。 详情参见[高性能大容量分布式目录服务FastDIR简介](https://my.oschina.net/u/3334339/blog/5405816) 建议将fdir部署在3台服务器上,可以根据实际需要随时增加或减少fdir服务器,fdir将基于binlog自动完成数据同步。 ## 3. fstore(数据存储) fstore 采用分组方式保存文件内容。为了便于扩容,FastCFS引入了数据分组(DG)的概念。 按4MB分块的文件内容(文件块),根据其hash code(文件ID + 文件块偏移量),路由到对应的数据分组。计算公式如下: ``` DGI = HashCode % DGC DGI:数据分组索引号 HashCode:文件分块哈希值 DGC:数据分组总数 ``` 数据分组是逻辑或虚拟概念,映射到的物理或实体概念是服务器分组(SG),二者是多对一关系,即:一个服务器分组(SG)可以容纳多个数据分组(DG)。 fstore集群的服务器分组总数,英文缩写为SGC。 DG 和SG映射关系配置在fstore的cluster.conf中,配置示例片段(简洁起见,只配置了一个3副本的SG): ``` # SGC server_group_count = 1 # DGC data_group_count = 256 # 配置SG1 [server-group-1] server_ids = [1-3] data_group_ids = [1, 256] ``` fstore集群扩容,可以一次增加一个SG。当集群规模较小(比如SGC小于等于4)时,建议一次扩容一倍(SGC翻番)。 DGC一旦确定就不可更改,除非建立新集群。因此在初始化集群时,需要确定好数据分组总数(DGC),可以根据业务发展规划,充分预估出DGC。 比如根据存储量预估5年后需要的服务器分组数(SGC)为20,为了充分发挥多核性能,每台服务器上跑32个数据分组(DG),DGC为20 * 32 = 640,按2次幂向上对齐,最终配置为1024。 * 友情提示:建议生产环境DGC至少配置为256。 fstore在线扩容分为两个步骤修改cluster.conf: 1. 增加扩容的SG 和迁移过去的DG映射; 2. 新增的SG自动同步完成后,将原有SG迁移出去的DG映射删除。 上述两个步骤将cluster.conf修改完成后,都需要将cluster.conf分发到fstore集群和fuseclient,然后重启fstore集群和fuseclient。推荐重启步骤如下: ``` 1. 停止fuseclient 2. 重启fstore集群 3. 启动fuseclient ``` 将上述配置示例的1个SG扩容为2个SG(均采用3副本),我们如何调整cluster.conf: ### 步骤1 修改后的cluster.conf内容片段如下: ``` # SGC,由1扩容为2 server_group_count = 2 # DGC data_group_count = 256 # 修改DG映射,将 [129, 256]迁移至SG2 [server-group-1] server_ids = [1-3] data_group_ids = [1, 128] # 配置SG2 # 必须加上server [1-3]用于同步已有数据 [server-group-2] server_ids = [1-6] data_group_ids = [129, 256] ``` 将cluster.conf分发到fstore集群所有服务器以及所有fuseclient后,重启fstore集群和fuseclient。 等待新增的SG同步完成,然后进入步骤2。 * 友情提示: * 数据同步过程中fstore集群正常提供服务; * 可以使用工具 fs_cluster_stat 查看fstore集群状态,比如: ``` # 查看ACTIVE列表,确保所有服务器状态为ACTIVE,确认一下最后一行输出的总数 fs_cluster_stat -A # 查看非ACTIVE列表,确保输出为空 fs_cluster_stat -N # 抽查某个数据分组(如ID为256)的ACTIVE状态,确保全部为ACTIVE fs_cluster_stat -g 256 # 查看帮助 fs_cluster_stat -h ``` ### 步骤2 修改后的cluster.conf内容片段如下: ``` # SGC server_group_count = 2 # DGC data_group_count = 256 [server-group-1] server_ids = [1-3] data_group_ids = [1, 128] # 去掉server [1-3] [server-group-2] server_ids = [4-6] data_group_ids = [129, 256] ``` 将cluster.conf分发到fstore集群所有服务器以及所有fuseclient后,重启fstore集群和fuseclient ### 清除已迁走的数据 数据迁移完成后,为了清除迁移出去的数据占用空间,V3.2支持清除迁移出去的replica和slice binlog,启动 fs_serverd时带上参数 --migrate-clean 即可,示例如下: ``` /usr/bin/fs_serverd /etc/fastcfs/fstore/server.conf restart --migrate-clean ``` **注意:** 为了防止误操作,正常情况下启动fs_serverd **不要** 使用--migrate-clean! * 友情提示: * 配置文件分发和程序启停可以使用fcfs.sh,使用说明参见[FastCFS集群运维工具](fcfs-ops-tool-zh_CN.md) * 步骤1和2重启fstore集群和fuseclient的过程,会导致服务不可用,建议在业务低峰期进行 ================================================ FILE: docs/fcfs-ops-tool-zh_CN.md ================================================ # FastCFS 运维工具介绍 * [fcfs.sh](#fcfs.sh) -- 用于管理 FastCFS 集群的快捷运维工具 * [fcfs_conf.sh](#fcfs_conf.sh) -- 用于快速创建集群配置文件的工具 ## 1. fcfs.sh 介绍 fcfs.sh 是一个用于快速部署和管理 FastCFS 集群的脚本工具。它基于 SSH 访问协议连接远程节点服务器,并使用服务器上带有 sudo 功能的账号(也可以使用root)进行相关命令的执行。它只需在你的工作站机器上运行,不需要服务器、数据库或者其他额外资源。 如果你需要经常搭建、卸载或配置 FastCFS集群,又想节省大量的重复工作,那么它很适合你。 ***注: 该脚本工具目前适用于以下操作系统:*** * CentOS 7 和 CentOS 8 及以上版本 * Red Hat Linux 8 及以上版本 * Rocky Linux 8 及以上版本 * Anolis 7及以上版本 * AlmaLinux 7及以上版本 * Fedora Linux 20 及以上版本 * Oracle Linux 8 及以上版本 * Amazon Linux * Alibaba Cloud Linux * BigCLoud 21及以上版本 * openEuler 20及以上版本 * Kylin V10及以上版本 * UOS 20及以上版本 * Debian 10 及以上版本 * Ubuntu 18.04 及以上版本 ### 1.1. 这个工具能做什么? 它能快速构建 FastCFS 集群,包括安装软件、分发配置文件,以及管理集群服务和查看集群日志。 它也能对 FastCFS 单节点或整个集群的相关软件进行升级或降级。 它能重新安装软件或者重新分发配置文件。 ### 1.2. 这个工具不能做什么? 它不是一个通用的部署工具, 仅适用于 FastCFS 集群。除了将配置文件分发到集群的各个服务器节点以外,它不对集群配置做任何处理。你必须为它提供可用的集群配置文件。 ### 1.3. 获取 fcfs.sh 从以下地址下载 fcfs.sh,并将其放置在工作站机器的bin目录下,以便命令行能够找到: > http://fastken.cn/fastcfs/ops/fcfs.sh 获取命令: > sudo curl -o /usr/bin/fcfs.sh http://fastken.cn/fastcfs/ops/fcfs.sh && sudo chmod +x /usr/bin/fcfs.sh 下载完成后可通过命令 ***which fcfs.sh*** 测试工具是否生效。 ### 1.4. 使用工具的前置条件 * fcfs.settings -- 集群运维配置,放置在当前工作目录中 * conf -- 集群配置文件目录,放置在当前工作目录中 * 远程服务器 SSH 免密登录,参见 [5. SSH 免密登录](#SSH-password-free-login) 你必须为每个集群创建一个独立的工作目录(例如:***fastcfs-ops***),将 ***fcfs.settings*** 和 ***conf*** 放入其中。然后所有针对该集群的后续命令操作,都必须在该工作目录中执行。 #### 1.4.1. fcfs.settings fcfs.settings 包括两个字段 ***fastcfs_version*** 和 ***fuseclient_ips***,fastcfs_version 用于指定 FastCFS 主版本号,fuseclient_ips 用于指定要部署 fuse client 的服务器主机。当需要对 FastCFS 集群软件进行升级或降级操作时,你需要先修改 fastcfs_version 对应的版本号为你期望的版本。 fcfs.settings 配置文件内容举例: ``` # 要安装的集群版本号(例如:5.5.0) fastcfs_version=5.5.0 # 要安装 fuseclient 客户端的IP列表,多个ip以英文逗号分隔 fuseclient_ips=10.0.1.14 ``` ***注:推荐使用 5.5.0 及更高版本用于集群部署。*** #### 1.4.2. conf FastCFS集群的所有配置文件必须提前放置在工作目录中的 ***conf*** 目录,conf 包含以下四个子目录: * vote -- fvote 服务器的配置文件 * fdir -- fdir 服务器的配置文件 * fstore -- fstore 服务器的配置文件 * auth -- fauth 服务器的配置文件 * fcfs -- fuseclient 客户端的配置文件 **获取配置文件样例** ``` mkdir fastcfs-ops cd fastcfs-ops/ curl -o fcfs-config-sample.tar.gz http://fastken.cn/fastcfs/ops/fcfs-config-sample.tar.gz tar -xzvf fcfs-config-sample.tar.gz ``` **使用fcfs_conf.sh创建集群配置文件** 也可以根据自己的需要,使用脚本工具 fcfs_conf.sh 创建 FastCFS集群配置文件。参见:[fcfs_conf.sh](#fcfs_conf.sh) ### 1.5. SSH 免密登录 fcfs.sh 将通过 SSH 连接所有集群主机。它需要工作站机器可以通过 SSH 在远程主机上执行脚本,执行命令的用户身份可以是 root 或带 sudo的用户,并且必须支持免密登录。 为了开启免密登录,你需要先为 SSH 生成 rsa 公钥/私钥对: > ssh-keygen -t rsa -C cfs -f cfs-ssh-key 将生成密钥文件: * cfs-ssh-key -- 私钥文件 * cfs-ssh-key.pub -- 公钥文件 #### 1.5.1. 部署私钥 将私钥文件 cfs-ssh-key 拷贝到工作站机器的当前用户home目录 ***"~/.ssh/"***,然后将下面的配置片段加入配置文件 ~/.ssh/config(每个集群主机一份): ``` Host [node ip] HostName [node host name] User [ssh user] Port 22 PubkeyAuthentication yes IdentityFile ~/.ssh/cfs-ssh-key ``` #### 1.5.2. 部署公钥 将公钥文件内容加入到每个节点服务器sudo 用户的home目录下 ***.ssh/authorized_keys*** 文件中: > ssh-copy-id -i cfs-ssh-key.pub [user name]@[node server ip] 并确保服务器上sshd的配置文件中开启了以下配置参数: ``` RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys ``` ### 1.6. 工具命令介绍 fcfs.sh工具的命令包括三部分:command,module 和 node。Command 是必须的,module 和 node 是可选的。 用法: > fcfs.sh command [module] [node] **Commands**: * setup -- 安装并运行 FastCFS 软件的快捷命令,组合了 install、config 和 restart三个命令 * install -- 安装 FastCFS 软件 * reinstall -- 重新安装 FastCFS 软件 * remove -- 移除 FastCFS 软件,功能与 erase 命令相同 * erase -- 移除 FastCFS 软件 * config -- 将集群配置文件拷贝到目标主机目录 * start -- 启动集群中的所有或单个模块服务 * restart -- 重启集群中的所有或单个模块服务 * stop -- 停止集群中的所有或单个模块服务 * tail -- 查看指定模块日志的最后一部分内容(等同于远程服务器的tail命令) * status -- 查看指定模块服务进程状态 * help -- 查看命令的详细说明和样例 **Modules**: * fvote -- Vote 服务器 * fdir -- fastDIR 服务器 * fstore -- faststore 服务器 * fauth -- FastCFS auth 服务器 * fuseclient -- FastCFS fuse 客户端 **Node**: 可以用于指定要执行命令的单个集群节点主机名或IP,如果不指定,命令将在所有节点上执行。node 参数必须在使用了 module 参数的情况下使用,不能单独直接使用。 #### 1.6.1. FastCFS 一键安装 你可以使用命令 ***setup*** 在集群节点上快速安装 FastCFS 软件,并自动配置和启动模块服务. **样例** 安装并启动整个 FastCFS 集群: > fcfs.sh setup 在所有 fdir 节点上安装并启动 fdir 服务器: > fcfs.sh setup fdir 在节点 10.0.1.11 上安装 fdir 服务器: > fcfs.sh setup fdir 10.0.1.11 节点 10.0.1.11 必须属于 fdir 集群,否则命令将失败。 #### 1.6.2. FastCFS 软件安装 你可以使用命令 ***install*** 只安装 FastCFS 软件。这个命令是 setup 命令的一部分。 第一次执行该命令时,必须在所有节点上安装所有软件(也就是说,此时不能指定module和node)。之后,当需要增加新节点时,可以携带 module 和 node 参数使用。 **样例** 在所有节点安装所需的 FastCFS 软件: > fcfs.sh install 在所有 fdir 节点上安装 fdir 服务器: > fcfs.sh install fdir 在 10.0.1.11 节点上安装 fdir 服务器: > fcfs.sh install fdir 10.0.1.11 #### 1.6.3. FastCFS 软件升级 当你想升级 FastCFS 软件时,你需要先将配置文件 fcfs.settings 中的 fastcfs_version 字段的值修改为新的版本号,然后执行 install 命令: > fcfs.sh install #### 1.6.4. FastCFS 软件降级 如果你确实需要将 FastCFS 软件降级为旧的版本,你必须先执行 ***remove*** 命令: > fcfs.sh remove 或: > fcfs.sh erase 并将字段 fastcfs_version 的值修改为旧的版本号,然后执行 install 命令: > fcfs.sh install #### 1.6.5. FastCFS 软件卸载 可以使用命令 ***remove*** 或 ***erase*** 卸载 FastCFS 软件。 **样例** 移除所有节点上的所有软件: > fcfs.sh remove 移除所有 fdir 节点上的 fdir 服务器: > fcfs.sh remove fdir 移除节点10.0.1.11上的 fdir 服务器: > fcfs.sh remove fdir 10.0.1.11 #### 1.6.6. FastCFS 配置文件分发 成功安装 FastCFS 软件之后,你必须通过执行命令 ***config*** 来将配置文件分发到各个集群节点。如果节点上的目标配置文件目录不存在,它将会自动创建它们。 **样例** 将所有模块的配置文件分发到所有相应的节点上: > fcfs.sh config 将 fdir 服务器的配置文件分发到所有 fdir 节点上: > fcfs.sh config fdir 将 fdir 服务器的配置文件分发到 fdir 节点 10.0.1.11 上: > fcfs.sh config fdir 10.0.1.11 #### 1.6.7. 集群管理 管理集群的命令包括 ***start***, ***stop***, ***restart***。你可以同时操作所有模块的所有节点,也可以同时操作某一模块的所有节点,也可以单独操作某一模块的某一个节点。 如果想操作单个节点,你必须在 module 参数后指定 node(host)参数(也就是说,你不能同时操作某一节点上的所有模块)。 **样例** 启动所有节点上的所有服务: > fcfs.sh start 重启所有 fdir 节点上的 fdir 服务: > fcfs.sh restart fdir 停止 节点10.0.1.11上的 fdir 服务: > fcfs.sh stop fdir 10.0.1.11 #### 1.6.8. 查看集群日志 当你想查看 FastCFS 服务的最新日志时,你可以使用命令 ***tail*** 来显示指定模块日志文件的最后一部分。 **样例** 显示 10.0.1.11节点上 fdir 服务日志文件的最后 100 行: > fcfs.sh tail fdir 10.0.1.11 -n 100 或: > fcfs.sh tail fdir 10.0.1.11 -100 显示每个 fdir 服务器日志的最后 10 行: > fcfs.sh tail fdir #### 1.6.9. 查看集群服务进程状态 当你想查看 FastCFS 服务的进程状态时,你可以使用命令 ***status*** 来显示指定模块的服务进程状态。 **样例** 显示 10.0.1.11 节点上 fdir 服务进程状态: > fcfs.sh status fdir 10.0.1.11 显示每个 fdir 服务器进程状态: > fcfs.sh status fdir 显示所有服务进程状态: > fcfs.sh status ## 2. fcfs_conf.sh 介绍 fcfs_conf.sh 是一个快速创建 FastCFS集群配置文件的运维工具。它通过 bash shell 访问本地配置文件 fcfs_conf.settings。它只需在你的工作站机器上运行,不需要服务器、数据库或者其他额外资源。 如果你需要在指定的服务器IP列表上快速生成集群配置文件,那么它很适合你。 ### 2.1. 获取 fcfs_conf.sh 从以下地址下载 fcfs_conf.sh,并将其放置在工作站机器的bin目录下,以便命令行能够找到: > http://fastken.cn/fastcfs/ops/fcfs_conf.sh 获取命令: > sudo curl -o /usr/bin/fcfs_conf.sh http://fastken.cn/fastcfs/ops/fcfs_conf.sh && sudo chmod +x /usr/bin/fcfs_conf.sh 下载完成后可通过命令 ***which fcfs_conf.sh*** 测试工具是否生效。 ### 2.2. 使用fcfs_conf.sh的前置条件 * fcfs_conf.settings -- 生成集群配置文件的参数配置,放置在当前工作目录中 你必须为每个集群创建一个独立的工作目录(例如:***fastcfs-ops***),将 ***fcfs_conf.settings***放入其中。然后在该工作目录中执行fcfs_conf.sh命令。 #### 2.2.1. fcfs_conf.settings fcfs_conf.settings 包括以下六种字段: * fastcfs_version -- 要生成哪个版本集群的配置文件 * vote_ips -- vote 服务器IP地址列表,多个IP以英文半角逗号分隔 * auth_ips -- FastCFS auth 服务器IP地址列表,多个IP以英文半角逗号分隔 * fdir_ips -- fastDIR服务器IP地址列表,多个IP以英文半角逗号分隔 * fstore_group_count -- faststore服务器分组数量 * fstore_group_[N] -- faststore服务器分组IP地址列表,多个IP以英文半角逗号分隔 * data_group_count -- faststore服务区数据分组数(推荐64的整数倍,建议至少配置256) 参数 fstore_group_count 设置的数量必须与 fstore_group_[N] 数量相匹配,并且编号从1开始递增。 fcfs_conf.settings 配置文件内容举例: ``` # 要生成配置的集群版本号(例如:5.5.0) fastcfs_version=5.5.0 # 集群主机列表和分组 vote_ips=10.0.1.11,10.0.1.12,10.0.1.13 auth_ips=10.0.1.11,10.0.1.12,10.0.1.13 fdir_ips=10.0.1.11,10.0.1.12,10.0.1.13 fstore_group_count=2 fstore_group_1=10.0.1.11,10.0.1.12,10.0.1.13 fstore_group_2=10.0.2.14,10.0.2.15,10.0.2.16 data_group_count=256 ``` ***注:推荐使用 5.5.0 及以后的版本用于生成集群配置文件,尤其是vote(投票)模块。*** ### 2.3. fcfs_conf.sh工具命令介绍 fcfs_conf.sh工具的命令包括两部分:command,module。Command 是必须的,module 是可选的。 用法: > fcfs_conf.sh command [module] **Commands**: * create -- 创建配置文件 * help -- 查看命令的详细说明和样例 **Modules**: * fvote -- vote 服务器 * fdir -- fastDIR 服务器 * fstore -- faststore 服务器 * fauth -- FastCFS auth 服务器 * fuseclient -- FastCFS fuse 客户端 ## 3. conf_tpl_tar.sh 介绍 一个简单的集群配置文件模版打包工具,仅供FastCFS开发者使用。 它将从FastCFS, fastDIR, faststore的源代码仓库将FastCFS 集群配置文件模版打包成tar包。 这三个仓库必须在同一个目录,并且 conf_tpl_tar.sh 必须在子目录 FastCFS/shell/ 中执行。 命令格式: ``` conf_tpl_tar.sh [update] ``` * version -- 要创建模版压缩包的集群版本号. * update -- 在创建之前获取最新的源代码,可选。 举例: > ./conf_tpl_tar.sh 5.5.0 update 将在当前目录创建配置模版压缩包文件 **conf.5.5.0.tpl.tar.gz**。 ================================================ FILE: docs/fcfs-ops-tool.md ================================================ # FastCFS ops tools introduction * [fcfs.sh](#fcfs.sh) -- a ops tool for quickly manage FastCFS clusters * [fcfs_conf.sh](#fcfs_conf.sh) -- a ops tool for quickly create FastCFS clusters config files ## 1. fcfs.sh introduction fcfs.sh is a ops tool for quickly deploy and manage FastCFS clusters. It only relying on SSH access to the servers and sudo. It runs fully on your workstation, requiring no servers, databases, or anything like that. If you set up and tear down FastCFS clusters a lot, and want minimal extra repeating works, this is for you. ***Tip: This shell support for the following operating systems:*** * CentOS 7, CentOS 8 or higher * Red Hat Linux 8 or higher * Rocky Linux 8 or higher * Fedora Linux 20 or higher * Oracle Linux 8 or higher * Amazon Linux * Alibaba Cloud Linux * Debian 10 or higher * Ubuntu 18.04 or higher ### 1.1. What this tool can do? It can quickly setup FastCFS cluster, include install softwares, distribute config files, manage cluster services and query cluster logs. It can also upgrade or downgrade FastCFS softwares on single node or full cluster. It can reinstall software or redistribute config files. ### 1.2. What this tool cannot do? It is not a generic deployment tool, it is only for FastCFS. It does not handle cluster configuration beyond distribute config files to cluster servers. You must provide usable cluster config files for it. ### 1.3. Fetch fcfs.sh Get it from http://fastken.cn/fastcfs/ops/fcfs.sh and put it in workstation's bin path. > sudo curl -o /usr/bin/fcfs.sh http://fastken.cn/fastcfs/ops/fcfs.sh && sudo chmod +x /usr/bin/fcfs.sh ### 1.4. Use preconditions * fcfs.settings -- cluster ops settings, in current working directory * conf -- cluster config files, in current working directory * remote server SSH password-free login, see also [SSH password-free login](#SSH-password-free-login) You must create a independent working directory(exp: ***fastcfs-ops***) for a cluster, put "fcfs.settings" and "conf" into it. And then, all subsequent commands for this cluster must be execute at the same working directory. #### 1.4.1. fcfs.settings fcfs.settings include two fields ***fastcfs_version*** and ***fuseclient_ips***, fastcfs_version specify FastCFS main version number, fuseclient_ips specify host to deploy fuse client. When upgrade or downgrade FastCFS, you need change fastcfs_version to version you expect. fcfs.settings content Example: ``` # Version of FastCFS cluster fastcfs_version=5.5.0 # Hosts which fuseclient will install,multiple hosts separated by comma fuseclient_ips=10.0.1.14 ``` ***Tip:Use version 5.5.0 and later for deploy.*** #### 1.4.2. conf All config files of FastCFS cluster must be put into working directory beforehand, conf include five subdirectories: * vote -- config files for fvote server * fdir -- config files for fdir server * fstore -- config files for fstore server * auth -- config files for fauth server * fcfs -- config files for fuseclient **Fetch config sample** ``` mkdir fastcfs-ops cd fastcfs-ops/ curl -o fcfs-config-sample.tar.gz http://fastken.cn/fastcfs/ops/fcfs-config-sample.tar.gz tar -xzvf fcfs-config-sample.tar.gz ``` **Create config files use fcfs_conf.sh** You can also create FastCFS cluster config files use fcfs_conf.sh according to your own needs. see also [fcfs_conf.sh](#fcfs_conf.sh) ### 1.5. SSH password-free login fcfs.sh will connect to cluster hosts via SSH. It requires that the workstation machine from which the shell is being run can connect via SSH as root(or sudo user) without password into cluster nodes. To enable password-free login, you need generate rsa key pair for ssh: > ssh-keygen -t rsa -C cfs -f cfs-ssh-key it will generate key files: * cfs-ssh-key -- private key file * cfs-ssh-key.pub -- public key file #### 1.5.1. Deploy private key Copy private key file cfs-ssh-key to workstation machine's current user directory ***"~/.ssh/"***, and add the following phrase to ~/.ssh/config(for each host): ``` Host [node ip] HostName [node host name] User [ssh user] Port 22 PubkeyAuthentication yes IdentityFile ~/.ssh/cfs-ssh-key ``` #### 1.5.2. Deploy public key Copy public key to file .ssh/authorized_keys in every node server's sudo user home: > ssh-copy-id -i cfs-ssh-key.pub [user name]@[node server ip] and ensure that the following lines are in server's sshd config: ``` RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys ``` ### 1.6. Tool commands introduction The tool's command include three parts: command, module and node. Command is required, module and node are optional. Usage: > fcfs.sh command [module] [node] **Commands**: * setup -- Setup FastCFS softwares, a shortcut command combines install, config, and restart * install -- Install FastCFS softwares * reinstall -- Reinstall FastCFS softwares * remove -- Remove FastCFS softwares, same as erase * erase -- Erase FastCFS softwares * config -- Copy cluster config files to target host path * start -- Start all or one module service in cluster * restart -- Restart all or one module service in cluster * stop -- Stop all or one module service in cluster * tail -- Display the last part of the specified module's log * status -- Display the service processes status * help -- Show the detail of commands and examples **Modules**: * fvote -- Vote server * fdir -- fastDIR server * fstore -- faststore server * fauth -- FastCFS auth server * fuseclient -- FastCFS fuse client **Node**: You can specify a single cluster IP, or command will be executed on all nodes. #### 1.6.1. FastCFS setup You can use command ***setup*** to quickly setup FastCFS softwares on nodes, then config and start module services. **Examples** Setup the full FastCFS cluster: > fcfs.sh setup Setup fdir server on all fdir nodes: > fcfs.sh setup fdir Setup fdir server on node 10.0.1.11: > fcfs.sh setup fdir 10.0.1.11 node 10.0.1.11 must belong to fdir cluster, or it will fail. #### 1.6.2. FastCFS softwares install You can use command ***install*** to install softwares only. This command is part of setup. First time, you must install all softwares on all nodes(that is to say you can't specify module and node). After that, you can install a new node with module and node params. **Examples** Install all FastCFS softwares on all nodes: > fcfs.sh install Install fdir server on all fdir nodes: > fcfs.sh install fdir Install fdir server on node 10.0.1.11: > fcfs.sh install fdir 10.0.1.11 #### 1.6.3. FastCFS softwares upgrade When you want to upgrade FastCFS's software, you can modify fcfs.settings, change field fastcfs_version to new version, then execute install command: > fcfs.sh install #### 1.6.4. FastCFS softwares downgrade If you realy want to downgrade FastCFS softwares to an old version, you must execute command ***remove*** first: > fcfs.sh remove or: > fcfs.sh erase and change field fastcfs_version to old version, then execute install command: > fcfs.sh install #### 1.6.5. FastCFS softwares remove You can use command ***remove*** or ***erase*** to remove FastCFS softwares. **Examples** Remove all softwares on all nodes: > fcfs.sh remove Remove fdir server on all nodes: > fcfs.sh remove fdir Remove fdir server on node 10.0.1.11: > fcfs.sh remove fdir 10.0.1.11 #### 1.6.6. FastCFS config files distribute After install FastCFS softwares, you must execute command ***config*** to distribute config files to cluster nodes. If target config file directory does't exist on node, it will create it automatically. **Exmaples** Distribute all module config files to all nodes respective: > fcfs.sh config Distribute fdir server config files to all fdir nodes: > fcfs.sh config fdir Distribute fdir server config files to fdir node 10.0.1.11: > fcfs.sh config fdir 10.0.1.11 #### 1.6.7. Cluster manage Manage commands include ***start***, ***stop***, ***restart***. You can operate on all modules or one module's all nodes at same time, or a single node for one module. If you want to operate on a single node, you must specify node host after module, that is to say you can't operate on all modules on a single node at same time. Start all service on all nodes: > fcfs.sh start Restart fdir service on all fdir nodes: > fcfs.sh restart fdir Stop fdir service on fdir node 10.0.1.11: > fcfs.sh stop fdir 10.0.1.11 #### 1.6.8. Display cluster logs When you want to query FastCFS services last logs, you can use command ***tail*** to display the last part of the specified module's log. **Example** Display the last 100 lines of fdir server log: > fcfs.sh tail fdir 10.0.1.11 -n 100 or: > fcfs.sh tail fdir 10.0.1.11 -100 Display the last 10 lines of each fdir server log: > fcfs.sh tail fdir #### 1.6.9. Display cluster service status When you want to query FastCFS services process status, you can use command ***status*** to display the status of the specified module's service process. **Example** Display the status of fdir service process: > fcfs.sh status fdir 10.0.1.11 Display the status of each fdir service process: > fcfs.sh status fdir Display the status of all service processes: > fcfs.sh status ## 2. fcfs_conf.sh introduction fcfs_conf.sh is a ops tool for quickly generate FastCFS cluster config files.It only relying on bash shell access to the local fcfs_conf.settings file. It runs fully on your workstation, requiring no servers, databases, or anything like that. If you want to generate cluster config files with specify server ips quickly, this is for you. ### 2.1. Fetch fcfs_conf.sh Get it from http://fastken.cn/fastcfs/ops/fcfs_conf.sh and put it in workstation's bin path. > sudo curl -o /usr/bin/fcfs_conf.sh http://fastken.cn/fastcfs/ops/fcfs_conf.sh && sudo chmod +x /usr/bin/fcfs_conf.sh ### 2.2. Use preconditions of fcfs_conf.sh * fcfs_conf.settings -- cluster ops settings for create config files, in current working directory You must create a independent working directory(exp: ***fastcfs-ops***) for a cluster, put "fcfs_conf.settings" into it. And then, subsequent commands for fcfs_conf.sh must be execute at the same working directory. #### 2.2.1. fcfs_conf.settings fcfs.settings include six fields: ***fastcfs_version*** and ***fuseclient_ips***, fastcfs_version specify FastCFS main version number, fuseclient_ips specify host to deploy fuse client. When upgrade or downgrade FastCFS, you need change fastcfs_version to version you expect. * fastcfs_version -- The FastCFS version you expect to create config files for. * vote_ips -- vote server IP list, multiple hosts separated by comma * auth_ips -- FastCFS auth server IP list, multiple hosts separated by comma * fdir_ips -- fastDIR server IP list, multiple hosts separated by comma * fstore_group_count -- faststore server group count * fstore_group_[N] -- faststore server group IP list, multiple hosts separated by comma * data_group_count - faststore data group count(multiples of 64 are recommended) Parameter fstore_group_count's value must match fstore_group_[N], and N increases from 1。 fcfs_conf.settings content Example: ``` # Version of FastCFS cluster fastcfs_version=5.5.0 # Cluster hosts list and group count vote_ips=10.0.1.11,10.0.1.12,10.0.1.13 auth_ips=10.0.1.11,10.0.1.12,10.0.1.13 fdir_ips=10.0.1.11,10.0.1.12,10.0.1.13 fstore_group_count=2 fstore_group_1=10.0.1.11,10.0.1.12,10.0.1.13 fstore_group_2=10.0.2.14,10.0.2.15,10.0.2.16 data_group_count=128 ``` ***Tip:Use version 5.5.0 and later for create config files, especially for vote server.*** ### 2.3. fcfs_conf.sh Tool commands introduction The fcfs_conf.sh command include two parts: command and module. Command is required, module is optional. Usage: > fcfs_conf.sh command [module] **Commands**: * create -- Create config files. * help -- Show the detail of commands and examples **Modules**: * fvote -- Vote server * fdir -- fastDIR server * fstore -- faststore server * fauth -- FastCFS auth server * fuseclient -- FastCFS fuse client ## 3. conf_tpl_tar.sh introduction A simple cluster config file templates pack tool for developer of FastCFS. It will pack FastCFS cluster config file templates to a tar file from source code repository FastCFS, fastDIR, faststore. Three repository FastCFS, fastDIR, faststore must at same path, and execute conf_tpl_tar.sh in FastCFS/shell/ Command: ``` conf_tpl_tar.sh [update] ``` * version -- Cluster version number to create template tar file. * update -- Pull the last source codes before create tar file. Optional Exmaple: > ./conf_tpl_tar.sh 5.5.0 update It will create tar file **conf.5.5.0.tpl.tar.gz** in current dir. ================================================ FILE: docs/index.md ================================================ # FastCFS -- a high performance general distributed file system for databases, K8s and VM etc. English | [Chinese](./README-zh_CN.md) ## 1. About FastCFS is a general distributed file system with strong consistency, high performance, high availability and supporting 10 billion massive files. FastCFS can be used as the back-end storage of databases (MySQL, PostgresSQL, Oracle etc.), K8s and NAS. ## 2. Current Version V3.1.0 ## 3. Supported Platforms * Linux: Kernel version >= 3.10 (Full support) * MacOS or FreeBSD (Only server side) ## 4. Dependencies * [libfuse](https://github.com/libfuse/libfuse) (version 3.9.4 or newer) * [Python](https://python.org/) (version 3.5 or newer) * [Ninja](https://ninja-build.org/) (version 1.7 or newer) * [gcc](https://www.gnu.org/software/gcc/) (version 4.7.0 or newer) * [libfastcommon](https://github.com/happyfish100/libfastcommon) (tag: V1.0.55) * [libserverframe](https://github.com/happyfish100/libserverframe) (tag: V1.1.12) * [libdiskallocator](https://github.com/happyfish100/libdiskallocator) (tag: V1.0.1) * [fastDIR](https://github.com/happyfish100/fastDIR) (tag: V3.1.0) * [faststore](https://github.com/happyfish100/faststore) (tag: V3.1.0) * [FastCFS](https://github.com/happyfish100/FastCFS) (tag: V3.1.0) ## 5. Installation ### 5.1 DIY installation you can use [Cluster Operation Tool](docs/fcfs-ops-tool.md) to deploy FastCFS step by step please see [INSTALL](docs/INSTALL.md) recommend to execute libfuse_setup.sh for compiling and installing libfuse ### 5.2 easy installation libfastcommon, libserverframe, fastDIR, faststore and FastCFS can be compiled, installed and auto configurated by fastcfs.sh fastcfs.sh can automatically pull or update above six projects codes from GitHub, compile and install according to dependency orders, and automatically generate cluster related configuration files according to the config templates. ``` git clone https://github.com/happyfish100/FastCFS.git; cd FastCFS/ ``` fastcfs.sh usage: ``` * install: pull/update codes from gitee, then make and install * config: copy config files and configure them with local ip * start | stop | restart: for service processes control ``` one click to build (deploy and run) single node demo environment (MUST run by root): ``` ./helloWorld.sh ``` or execute following commands (MUST run by root): ``` ./fastcfs.sh install ./fastcfs.sh config --force ./fastcfs.sh restart ``` now you can see the mounted path of FastCFS by the command: ``` df -h /opt/fastcfs/fuse | grep fuse ``` ## 6. Benchmark FastCFS has huge better performance than Ceph: the IOPS ratio of sequential write is 6.x, sequential read is 2.x, random write is about 2.0. * [FastCFS vs. Ceph benchmark](docs/benchmark.md) ## 7. K8s CSI Driver [fastcfs-csi](https://github.com/happyfish100/fastcfs-csi) ## 8. Chinese Relative articles CSDN blog ## 9. TODO List * [fstore] provide cluster tools for automatic expansion * [fstore] hierarchical storage & slice merging: supporting two-level storage, such as SSD + HDD * [fdir & fstore] binlog deduplication (fdir binlog, fstore replica & slice binlog) * [fstore] after the machine recovery, the data masters should be rebalanced ## 10. Contact us email: 384681(at)qq(dot)com WeChat subscription: search "fastdfs" for the related articles (Chinese Only) ================================================ FILE: docs/shared-storage-guide-zh_CN.md ================================================ # 共享数据配置指南 FastCFS客户端缓存默认是开启的,这主要针对独享数据场景(对同一文件单节点读写),也可以支持非实时场景下对同一文件一写多读。 如果将FastCFS作为Oracle RAC等系统的共享存储(对同一文件多节点写入和多节点读取),为了保证数据一致性,需要关闭客户端相关缓存。 默认安装的客户端配置文件为 /etc/fastcfs/fcfs/fuse.conf,下面列出需要修改的配置项: ``` [FastDIR] # 对于文件追加写或文件truncate操作,通过加锁避免冲突 # if use sys lock for file append and truncate to avoid conflict # set true when the files appended or truncated by many nodes (FUSE instances) # default value is false use_sys_lock_for_append = true # 禁用异步报告文件属性(即采用同步报告方式) # if async report file attributes (size, modify time etc.) to the FastDIR server # default value is true async_report_enabled = false # 禁用合并写 [write-combine] # if enable write combine feature for FastStore # default value is true enabled = false # 禁用预读机制 [read-ahead] # if enable read ahead feature for FastStore # default value is true enabled = false [FUSE] # 禁用Linux对inode缓存 # cache time for file entry in seconds # default value is 1.0s entry_timeout = 0.0 # 禁用Linux对文件属性缓存 # cache time for file attribute in seconds # default value is 1.0s attribute_timeout = 0.0 # 禁用内核合并写 # if enable kernel writeback cache # default value is true writeback_cache = false # 禁用内核读缓存 # if keep kernel cache for read # set to true for unshared data scene (private data for single node) # should set to false on shared data scene for multi nodes # default value is true kernel_cache = false ``` 友情提示:配置文件修改后,需要重启fcfs_fused方可生效。 ================================================ FILE: docs/version-history-zh_CN.md ================================================ # FastCFS 重大版本一览 * V1.0:2020年12月第一个版本(历时12个月) * V2.0:2021年4月支持k8s(历时3个月) * V3.0:2021年12月实现fdir存储插件,支持百亿级海量文件(历时6个月) * V3.3:2022年4月生产环境可用(从V3.1到V3.3,历时4个月) * V3.6:2022年9月文件读写性能大幅提升(从V3.4到V3.6,历时5个月) * V3.7:2022年11月遵循POSIX ACL,有用户用作NFS后端存储(历时2个月) * V4.0:2023年6月实现fstore存储插件,支持单机PB级存储(历时4个月) * V4.3:2023年8月V4系列稳定版本(从V4.1到V4.3,历时2个月) * V5.0:2023年11月原生支持RDMA,解决网络瓶颈(历时4个月) * V5.3:2024年3月支持IPv6和bug修复(从V5.1到V5.3,历时4个月) * V5.4:2025年4月存储插件性能优化和bug修复(历时3个月) * V5.5:2025年11月适配io_uring高性能异步IO框架,最新稳定版本 ================================================ FILE: docs/versions.json ================================================ [ {"version": "v3.1.0", "title": "v3.2.0", "aliases": ["latest"]}, {"version": "v3.1.0", "title": "v3.1.0", "aliases": []} ] ================================================ FILE: fastcfs.sh ================================================ #!/bin/bash # # fastcfs install, config and start a local fastcfs cluster on Linux. # fastcfs's primary goals are to be the best shell for local FastCFS # application development and to support all fastcfs features that fit. # # fastcfs shell support commands # setup -- combin install, config and restart, quickly setup single node FastCFS cluster # install -- pull code, make, install # config -- copy config files to default conf path(/etc/fastcfs/) # start -- start fdir_serverd, fs_serverd, fcfs_fused service # restart -- restart fdir_serverd, fs_serverd, fcfs_fused service # stop -- stop fdir_serverd, fs_serverd, fcfs_fused service # # When setup, This shell will make and install these six libs in order: # libfastcommon # libserverframe # libdiskallocator # auth_client # fastDIR # faststore # FastCFS # # If no source code in build path, it will git clone from: # https://gitee.com/fastdfs100/libfastcommon.git # https://gitee.com/fastdfs100/libserverframe.git # https://gitee.com/fastdfs100/libdiskallocator.git # https://gitee.com/fastdfs100/fastDIR.git # https://gitee.com/fastdfs100/faststore.git # # FastCFS modules and repositores COMMON_LIB="libfastcommon" COMMON_LIB_URL="https://gitee.com/fastdfs100/libfastcommon.git" FRAME_LIB="libserverframe" FRAME_LIB_URL="https://gitee.com/fastdfs100/libserverframe.git" DISK_ALLOCATOR_LIB="libdiskallocator" DISK_ALLOCATOR_LIB_URL="https://gitee.com/fastdfs100/libdiskallocator.git" FDIR_LIB="fastDIR" FDIR_LIB_URL="https://gitee.com/fastdfs100/fastDIR.git" STORE_LIB="faststore" STORE_LIB_URL="https://gitee.com/fastdfs100/faststore.git" FASTCFS_LIB=".." VOTECLIENT_LIB=".." AUTHCLIENT_LIB=".." STORE_CONF_FILES=(client.conf server.conf cluster.conf storage.conf dbstore.conf) STORE_CONF_PATH="/etc/fastcfs/fstore/" FDIR_CONF_FILES=(client.conf cluster.conf server.conf dbstore.conf) FDIR_CONF_PATH="/etc/fastcfs/fdir/" AUTH_CONF_FILES=(auth.conf client.conf cluster.conf server.conf session.conf) AUTH_CONF_PATH="/etc/fastcfs/auth/" AUTH_KEYS_FILES=(session_validate.key) AUTH_KEYS_PATH="/etc/fastcfs/auth/keys/" FUSE_CONF_FILES=(fuse.conf) FUSE_CONF_PATH="/etc/fastcfs/fcfs/" YUM_OS_ARRAY=(Red Rocky Oracle Fedora CentOS AlmaLinux Alibaba Anolis Amazon openEuler Kylin UOS BigCLoud) BUILD_PATH="build" this_shell_name=$0 mode=$1 # setup|install|config|start|restart|stop make_shell=make.sh force_reconfig=0 uname=$(uname) pull_source_code() { if [ $# != 2 ]; then echo "ERROR: $0 - pull_source_code() function need repository name and url!" exit 1 fi module_name=$1 module_url=$2 if ! [ -d $module_name ]; then echo "INFO: The $module_name local repository does not exist!" echo "INFO: =====Begin to clone $module_name , it will take some time...=====" git clone $module_url git checkout master else echo "INFO: The $module_name local repository have existed." cd $module_name echo "INFO: =====Begin to pull $module_name, it will take some time...=====" git checkout master git pull cd .. fi } make_op() { if [ $# -lt 3 ]; then echo "ERROR: $0 - make_op() function need module repository name and mode!" exit 1 fi module_name=$1 module_src_path=$2 extend_options=$4 make_mode=$3 module_lib_path=$BUILD_PATH/$module_src_path if ! [ -d $module_lib_path ]; then echo "ERROR: $0 - module repository {$module_name} does not exist!" echo "ERROR: You must checkout from remote repository first!" exit 1 else cd $module_lib_path echo "INFO: =====Begin to $make_mode module [$module_name]=====" command="./$make_shell" if [ ${#extend_options} -gt 0 ]; then command="$command $extend_options" fi if [ $make_mode != "make" ]; then command="$command $make_mode" fi echo "INFO: Execute command ($command) in path ($PWD)" result=`$command` echo "INFO: Execute result=(" echo "$result" echo ")" cd - fi } sed_replace() { sed_cmd=$1 filename=$2 if [ "$uname" = "FreeBSD" ] || [ "$uname" = "Darwin" ]; then sed -i "" "$sed_cmd" $filename else sed -i "$sed_cmd" $filename fi } split_to_array() { if ! [ -z $2 ]; then IFS=',' read -ra $2 <<< "$1" fi } placeholder_replace() { filename=$1 placeholder=$2 value=$3 sed_replace "s#\${$placeholder}#$value#g" $filename } parse_config_arguments() { for arg do case "$arg" in --force) force_reconfig=1 ;; esac done } check_paths_infile() { # Check all paths in this config file, if not exist, will create it. check_conf_file=$1 check_result=`sed -n \ -e '/^mountpoint *=/ p' \ -e '/^base_path *=/ p' \ -e '/^path *=/ p' \ $check_conf_file|sed 's/\([^=]*\)=\([^=]\)/\2/g'` for check_path in ${check_result[@]}; do if ! [ -d $check_path ]; then if ! mkdir -p $check_path; then echo "ERROR: Create target path in file $check_conf_file failed:$check_path!" exit 1 else echo "INFO: Created target path in file $check_conf_file successfully:$check_path!" fi fi done } replace_host() { # Replace host with ip of current host. replace_conf_file=$1 target_ip=$2 sed_replace "s#^host *=.*#host = $target_ip#g" $replace_conf_file } copy_config_to() { config_file_array=$1 src_path=$2 dest_path=$3 if ! [ -d $dest_path ]; then if ! mkdir -p $dest_path; then echo "ERROR: Create target conf path failed:$dest_path!" exit 1 fi fi for CONF_FILE in ${config_file_array[@]}; do tmp_src_file=$src_path/$CONF_FILE tmp_dest_file=${dest_path}$CONF_FILE if [ -f $tmp_src_file ]; then if [ -f $tmp_dest_file ]; then if ! [ $force_reconfig -eq 1 ]; then echo "ERROR: file $tmp_dest_file exist, " echo 'you must specify --force to force reconfig, it will overwrite the exists files!' echo "Usage: $this_shell_name config --force" exit 1 else # Backup exists file cp -f $tmp_dest_file $tmp_dest_file.bak fi fi echo "INFO: Copy file $CONF_FILE to $dest_path" cp -f $tmp_src_file $tmp_dest_file elif ! [ -f $tmp_dest_file ]; then echo "ERROR: file $tmp_dest_file not exist" exit 1 fi file_extension="${tmp_dest_file##*.}" if [ $file_extension = "conf" ]; then check_paths_infile $tmp_dest_file replace_host $tmp_dest_file "$IP" fi done } config_faststore() { # Copy faststore's config file to target config path echo "INFO: Config faststore:" store_src_conf_path=$BUILD_PATH/$STORE_LIB/conf copy_config_to "${STORE_CONF_FILES[*]}" $store_src_conf_path $STORE_CONF_PATH } config_fastdir() { # Copy fastDIR's config file to target config path echo "INFO: Config fastDIR:" fdir_src_conf_path=$BUILD_PATH/$FDIR_LIB/conf copy_config_to "${FDIR_CONF_FILES[*]}" $fdir_src_conf_path $FDIR_CONF_PATH } config_auth() { # Copy auth's config file to target config path echo "INFO: Config auth:" auth_src_conf_path=src/auth/conf copy_config_to "${AUTH_CONF_FILES[*]}" $auth_src_conf_path $AUTH_CONF_PATH auth_src_keys_path=src/auth/conf/keys copy_config_to "${AUTH_KEYS_FILES[*]}" $auth_src_keys_path $AUTH_KEYS_PATH } config_fuse() { # Copy fuse's config file to target config path echo "INFO: Config fuse:" fuse_src_conf_path=conf copy_config_to "${FUSE_CONF_FILES[*]}" $fuse_src_conf_path $FUSE_CONF_PATH } get_first_local_ip() { ip_cmd=`which ip` if [ -z "$ip_cmd" ]; then ipconfig_cmd=`which ifconfig` if [ -z "$ipconfig_cmd" ]; then echo "ERROR: Command ip or ifconfig not found, please install one first." 1>&2 exit else CMD="$ipconfig_cmd -a | grep -w inet | grep -v 127.0.0.1 | awk '{print \$2}' | tr -d 'addr:' | head -n 1" fi else CMD="$ip_cmd addr | grep -w inet | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1 | head -n 1" fi sh -c "$CMD" } pull_source_codes() { # Clone or pull source code from github.com if not exists. echo "INFO: Begin to pull source codes..." # Pull fastCFS self. git pull # Create build path if not exists. if ! [ -d $BUILD_PATH ]; then mkdir -m 775 $BUILD_PATH echo "INFO: Build path {$BUILD_PATH} not exist, created." fi cd $BUILD_PATH # Pull libfastcommon pull_source_code $COMMON_LIB $COMMON_LIB_URL # Pull libserverframe pull_source_code $FRAME_LIB $FRAME_LIB_URL # Pull libdiskallocator pull_source_code $DISK_ALLOCATOR_LIB $DISK_ALLOCATOR_LIB_URL # Pull fastDIR pull_source_code $FDIR_LIB $FDIR_LIB_URL # Pull faststore pull_source_code $STORE_LIB $STORE_LIB_URL cd .. } make_install() { lib_name=$1 lib_src_path=$2 op_extend_param=$3 make_op $lib_name $lib_src_path clean make_op $lib_name $lib_src_path make $op_extend_param make_op $lib_name $lib_src_path install $op_extend_param } make_installs() { # Make and install all lib repositories sequentially. make_install $COMMON_LIB $COMMON_LIB make_install $FRAME_LIB $FRAME_LIB make_install $DISK_ALLOCATOR_LIB $DISK_ALLOCATOR_LIB make_install vote_client $VOTECLIENT_LIB --module=vote_client make_install auth_client $AUTHCLIENT_LIB --module=auth_client make_install $FDIR_LIB $FDIR_LIB make_install $STORE_LIB $STORE_LIB make_install FastCFS $FASTCFS_LIB --exclude=auth_client echo "INFO: Congratulation, setup complete successfully!" echo "INFO: Then, you can execute command [fastcfs.sh config] next step." } service_op() { operate_mode=$1 fs_serverd ${STORE_CONF_PATH}server.conf $operate_mode fdir_serverd ${FDIR_CONF_PATH}server.conf $operate_mode if [ $operate_mode != 'stop' ]; then sleep 3 fi fcfs_authd ${AUTH_CONF_PATH}server.conf $operate_mode if [ $operate_mode != 'stop' ]; then sleep 1 fi fcfs_fused ${FUSE_CONF_PATH}fuse.conf $operate_mode } uname=$(uname) if [ $uname = 'Linux' ]; then osname=$(cat /etc/os-release | grep -w NAME | awk -F '=' '{print $2;}' | \ awk -F '"' '{if (NF==3) {print $2} else {print $1}}' | awk '{print $1}') if [ $osname = 'CentOS' ]; then osversion=$(cat /etc/system-release | awk '{print $4}') fi if [ -z $osversion ]; then osversion=$(cat /etc/os-release | grep -w VERSION_ID | awk -F '=' '{print $2;}' | \ awk -F '"' '{if (NF==3) {print $2} else {print $1}}') fi os_major_version=$(echo $osversion | awk -F '.' '{print $1}') if [ $osname = 'Fedora' ]; then if [ $os_major_version -lt 20 ]; then os_major_version=6 elif [ $os_major_version -lt 28 ]; then os_major_version=7 elif [ $os_major_version -lt 34 ]; then os_major_version=8 else os_major_version=9 fi elif [ $osname = 'Alibaba' ]; then if [ $os_major_version -lt 3 ]; then os_major_version=7 else os_major_version=8 fi elif [ $osname = 'Amazon' ]; then if [ $os_major_version -le 2 ]; then os_major_version=7 else os_major_version=8 fi elif [ $osname = 'openEuler' ] || [ $osname = 'Kylin' ] || [ $osname = 'UOS' ] || [ $osname = 'BigCLoud' ]; then os_major_version=8 fi else echo "Unsupport OS: $uname" 1>&2 exit 1 fi check_install_fastos_repo() { if [ $osname = 'Ubuntu' ] || [ $osname = 'Debian' ] || [ $osname = 'Deepin' ]; then if [ ! -f /etc/apt/sources.list.d/fastos.list ]; then apt install curl gpg -y curl http://www.fastken.com/aptrepo/packages.fastos.pub | gpg --dearmor > /tmp/fastos-archive-keyring.gpg install -D -o root -g root -m 644 /tmp/fastos-archive-keyring.gpg /usr/share/keyrings/fastos-archive-keyring.gpg sh -c 'echo "deb [signed-by=/usr/share/keyrings/fastos-archive-keyring.gpg] http://www.fastken.com/aptrepo/fastos/ fastos main" > /etc/apt/sources.list.d/fastos.list' sh -c 'echo "deb [signed-by=/usr/share/keyrings/fastos-archive-keyring.gpg] http://www.fastken.com/aptrepo/fastos-debug/ fastos-debug main" > /etc/apt/sources.list.d/fastos-debug.list' rm -f /tmp/fastos-archive-keyring.gpg fi else repo=$(rpm -q FastOSrepo 2>/dev/null) if [ $? -ne 0 ]; then if [ $os_major_version -eq 7 ]; then rpm -ivh http://www.fastken.com/yumrepo/el7/noarch/FastOSrepo-1.0.1-1.el7.noarch.rpm else rpm -ivh http://www.fastken.com/yumrepo/el8/noarch/FastOSrepo-1.0.1-1.el8.noarch.rpm fi fi fi } install_all_softwares() { if [ $osname = 'Ubuntu' -a $os_major_version -ge 18 ] || \ [ $osname = 'Debian' -a $os_major_version -ge 10 ] || \ [ $osname = 'Deepin' -a $os_major_version -ge 20 ]; then check_install_fastos_repo apt update apt install fastcfs-auth-server fastcfs-vote-server fastdir-server faststore-server fastcfs-fused -y elif [[ " ${YUM_OS_ARRAY[@]} " =~ " ${osname} " ]] && [ $os_major_version -ge 7 ]; then check_install_fastos_repo yum install FastCFS-auth-server FastCFS-vote-server fastDIR-server faststore-server FastCFS-fused -y else ./libfuse_setup.sh pull_source_codes make_installs fi } config_all_modules() { IP=$(get_first_local_ip) parse_config_arguments $* config_faststore config_fastdir config_auth config_fuse } case "$mode" in 'setup') install_all_softwares config_all_modules $* service_op restart ;; 'install') install_all_softwares ;; 'config') config_all_modules $* ;; 'start') # Start services. service_op start ;; 'restart') # Restart services. service_op restart ;; 'stop') # Start services. service_op stop ;; *) # usage echo "Usage: $0 {setup|install|config|start|restart|stop}" exit 1 ;; esac exit 0 ================================================ FILE: helloWorld.sh ================================================ #!/bin/bash echo "just for FastCFS demo: 1 fdir instance and 1 fstore instance" mounted_path=/opt/fastcfs/fuse ./fastcfs.sh install || exit 1 ./fastcfs.sh config --force ./fastcfs.sh restart echo "waiting services ready ..." sleep 3 if [ -d $mounted_path ]; then df -h $mounted_path | grep fuse if [ $? -eq 0 ]; then echo "" echo "the mounted path is: $mounted_path" echo "have a nice day!" exit 0 fi fi tail_cmd='tail -n 20 /opt/fastcfs/fcfs/logs/fcfs_fused.log' echo 'some mistake happen!' echo $tail_cmd sh -c "$tail_cmd" exit 1 ================================================ FILE: libfuse_setup.sh ================================================ #!/bin/bash YUM_OS_ARRAY=(Red Rocky Oracle Fedora CentOS AlmaLinux Alibaba Anolis Amazon) get_gcc_version() { old_lang=$LANG LANG=en_US.UTF-8 && gcc -v 2>&1 | grep 'gcc version' | \ awk -F 'version' '{print $2}' | awk '{print $1}' | \ awk -F '.' '{print $1}' LANG=$old_lang } apt_check_install_gcc() { version=$1 pkg_gcc="gcc-$version" pkg_cpp="g++-$version" apt list $pkg_gcc 2>&1 | grep $pkg_gcc && apt install $pkg_gcc $pkg_cpp -y } apt_install_gcc() { apt_check_install_gcc 7 if [ $? -ne 0 ]; then apt_check_install_gcc 8 if [ $? -ne 0 ]; then apt_check_install_gcc 9 fi fi } apt_install_required_gcc() { os_major_version=$1 apt install gcc g++ -y if [ $? -ne 0 ]; then apt_install_gcc $os_major_version else gcc_version=$(get_gcc_version) if [ -z "$gcc_version" ] || [ "$gcc_version" -lt 4 ]; then apt_install_gcc $os_major_version fi fi } yum_check_install_gcc() { prefix=$1 version=$2 pkg_prefix="${prefix}toolset-${version}" pkg_gcc="${pkg_prefix}-gcc" pkg_cpp="${pkg_prefix}-gcc-c++" yum list $pkg_gcc 2>&1 | grep "$pkg_gcc" && \ yum install $pkg_gcc $pkg_cpp -y && \ source /opt/rh/$pkg_prefix/enable } yum_install_gcc() { os_major_version=$1 if [ $os_major_version -lt 8 ]; then yum install centos-release-scl scl-utils-build -y prefix='dev' else prefix='gcc-' fi yum_check_install_gcc "$prefix" 7 if [ $? -ne 0 ]; then yum_check_install_gcc "$prefix" 8 if [ $? -ne 0 ]; then yum_check_install_gcc "$prefix" 9 fi fi } yum_install_required_gcc() { os_major_version=$1 yum install gcc gcc-c++ -y if [ $? -ne 0 ]; then yum_install_gcc $os_major_version else gcc_version=$(get_gcc_version) if [ -z "$gcc_version" ] || [ "$gcc_version" -lt 4 ]; then yum_install_gcc $os_major_version fi fi } if [ -z "$LANG" ]; then export LANG=en_US.UTF-8 fi uname=$(uname) if [ $uname = 'Linux' ]; then osname=$(cat /etc/os-release | grep -w NAME | awk -F '=' '{print $2;}' | \ awk -F '"' '{if (NF==3) {print $2} else {print $1}}' | awk '{print $1}') if [ $osname = 'CentOS' ]; then osversion=$(cat /etc/system-release | awk '{print $4}') fi if [ -z $osversion ]; then osversion=$(cat /etc/os-release | grep -w VERSION_ID | awk -F '=' '{print $2;}' | \ awk -F '"' '{if (NF==3) {print $2} else {print $1}}') fi os_major_version=$(echo $osversion | awk -F '.' '{print $1}') if [ $osname = 'Fedora' ]; then if [ $os_major_version -lt 20 ]; then os_major_version=6 elif [ $os_major_version -lt 28 ]; then os_major_version=7 elif [ $os_major_version -lt 34 ]; then os_major_version=8 else os_major_version=9 fi elif [ $osname = 'Alibaba' ]; then if [ $os_major_version -lt 3 ]; then os_major_version=7 else os_major_version=8 fi elif [ $osname = 'Amazon' ]; then if [ $os_major_version -le 2 ]; then os_major_version=7 else os_major_version=8 fi fi else echo "Unsupport OS: $uname" 1>&2 exit 1 fi if [[ " ${YUM_OS_ARRAY[@]} " =~ " ${osname} " ]] && [ $os_major_version -ge 7 ]; then repo=$(rpm -q FastOSrepo 2>/dev/null) if [ $? -ne 0 ]; then if [ $os_major_version -eq 7 ]; then rpm -ivh http://www.fastken.com/yumrepo/el7/x86_64/FastOSrepo-1.0.0-1.el7.centos.x86_64.rpm else rpm -ivh http://www.fastken.com/yumrepo/el8/x86_64/FastOSrepo-1.0.0-1.el8.x86_64.rpm fi fi rpm -q fuse-devel >/dev/null && yum remove fuse-devel -y rpm -q fuse >/dev/null && yum remove fuse -y yum install fuse3-devel -y exit 0 else git_version=$(git --version 2>&1 | grep 'version' | awk '{print $3}') make_version=$(make --version 2>&1 | grep 'Make' | awk '{print $3}') PKG_CONFIG_PRG=$(which pkg-config 2>&1) if [ $? -ne 0 ]; then PKG_CONFIG_PRG='' fi gcc_version=$(get_gcc_version) python_version=$(python3 --version 2>&1 | grep Python | awk '{print $2}') pip3_version=$(pip3 --version 2>&1 | awk '{print $2}') if [ $osname = 'Ubuntu' -o $osname = 'Debian' ]; then if [ -z "$git_version" ]; then apt install git -y fi if [ -z "$make_version" ]; then apt install make -y fi dpkg -s libaio-dev >/dev/null 2>&1 || apt install libaio-dev -y if [ -z "$PKG_CONFIG_PRG" ]; then apt install pkg-config -y fi if [ -z "$python_version" ]; then apt install python3 -y fi if [ -z "$pip3_version" ]; then apt install python3-pip -y fi if [ -z "$gcc_version" ] || [ "$gcc_version" -lt 4 ]; then apt_install_required_gcc fi else if [ -z "$git_version" ]; then yum install git -y fi if [ -z "$make_version" ]; then yum install make -y fi if [ -z "$PKG_CONFIG_PRG" ]; then yum install pkgconfig -y || yum install pkgconf-pkg-config -y fi if [ -z "$python_version" ]; then yum install python3 -y || yum install python36 -y || yum install python38 -y || exit 2 fi if [ -z "$pip3_version" ]; then yum install python3-pip -y fi if [ -z "$gcc_version" ] || [ "$gcc_version" -lt 4 ]; then yum_install_required_gcc fi fi fi MESON_PRG=$(which meson 2>&1) if [ $? -ne 0 ]; then pip3 install meson || exit fi NINJA_PRG=$(which ninja 2>&1) if [ $? -ne 0 ]; then pip3 install ninja || exit fi mkdir -p build; cd build git clone https://gitee.com/mirrors/libfuse.git cd libfuse/ git checkout fuse-3.10.1 rm -rf build/ && mkdir build/ && cd build/ meson .. meson configure -D prefix=/usr meson configure -D examples=false ninja ninja install sed -i 's/#user_allow_other/user_allow_other/g' /etc/fuse.conf cd .. ================================================ FILE: make.sh ================================================ ENABLE_STATIC_LIB=0 ENABLE_SHARED_LIB=1 TARGET_PREFIX=$DESTDIR/usr TARGET_CONF_PATH=$DESTDIR/etc/fdir module='' exclude='' jni_file='' start=1 for arg do case "$arg" in --module=*) module=${arg#--module=} start=$(expr $start + 1) ;; --exclude=*) exclude=${arg#--exclude=} start=$(expr $start + 1) ;; --jni=*) jni_file=${arg#--jni=} start=$(expr $start + 1) ;; esac done index=$start eval param1="\$$index" index=$(expr $index + 1) eval param2="\$$index" DEBUG_FLAG=0 PRELOAD_WITH_CAPI=1 arch=$(uname -r | awk -F '.' '{print $NF;}') if [ "$arch" = "x86_64" ]; then PRELOAD_WITH_PAPI=1 else PRELOAD_WITH_PAPI=0 fi export CC=gcc CFLAGS='-Wall' GCC_VERSION=$(gcc -dM -E - < /dev/null | grep -w __GNUC__ | awk '{print $NF;}') if [ -n "$GCC_VERSION" ] && [ $GCC_VERSION -ge 7 ]; then CFLAGS="$CFLAGS -Wformat-truncation=0 -Wformat-overflow=0" fi CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE" if [ "$PRELOAD_WITH_CAPI" = "1" ]; then CFLAGS="$CFLAGS -DFCFS_PRELOAD_WITH_CAPI" fi if [ "$PRELOAD_WITH_PAPI" = "1" ]; then CFLAGS="$CFLAGS -DFCFS_PRELOAD_WITH_PAPI" fi if [ "$DEBUG_FLAG" = "1" ]; then CFLAGS="$CFLAGS -g -DDEBUG_FLAG" else CFLAGS="$CFLAGS -g -O3" fi if [ -f /usr/include/fastcommon/_os_define.h ]; then OS_BITS=$(grep -F OS_BITS /usr/include/fastcommon/_os_define.h | awk '{print $NF;}') elif [ -f /usr/local/include/fastcommon/_os_define.h ]; then OS_BITS=$(grep -F OS_BITS /usr/local/include/fastcommon/_os_define.h | awk '{print $NF;}') else OS_BITS=64 fi uname=$(uname) LIB_VERSION= if [ "$OS_BITS" -eq 64 ]; then if [ $uname = 'Linux' ]; then osname=$(cat /etc/os-release | grep -w NAME | awk -F '=' '{print $2;}' | \ awk -F '"' '{if (NF==3) {print $2} else {print $1}}' | awk '{print $1}') if [ $osname = 'Ubuntu' -o $osname = 'Debian' ]; then LIB_VERSION=lib else LIB_VERSION=lib64 fi else LIB_VERSION=lib fi else LIB_VERSION=lib fi LIBS='' if [ "$uname" = "Linux" ]; then if [ "$OS_BITS" -eq 64 ]; then LIBS="$LIBS -L/usr/lib64" else LIBS="$LIBS -L/usr/lib" fi CFLAGS="$CFLAGS" elif [ "$uname" = "FreeBSD" ] || [ "$uname" = "Darwin" ]; then LIBS="$LIBS -L/usr/lib" CFLAGS="$CFLAGS" if [ "$uname" = "Darwin" ]; then CFLAGS="$CFLAGS -DDARWIN" TARGET_PREFIX=$TARGET_PREFIX/local fi elif [ "$uname" = "SunOS" ]; then LIBS="$LIBS -L/usr/lib" CFLAGS="$CFLAGS -D_THREAD_SAFE" LIBS="$LIBS -lsocket -lnsl -lresolv" elif [ "$uname" = "AIX" ]; then LIBS="$LIBS -L/usr/lib" CFLAGS="$CFLAGS -D_THREAD_SAFE" elif [ "$uname" = "HP-UX" ]; then LIBS="$LIBS -L/usr/lib" CFLAGS="$CFLAGS" fi have_pthread=0 if [ -f /usr/lib/libpthread.so ] || [ -f /usr/local/lib/libpthread.so ] || [ -f /lib64/libpthread.so ] || [ -f /usr/lib64/libpthread.so ] || [ -f /usr/lib/libpthread.a ] || [ -f /usr/local/lib/libpthread.a ] || [ -f /lib64/libpthread.a ] || [ -f /usr/lib64/libpthread.a ]; then LIBS="$LIBS -lpthread" have_pthread=1 elif [ "$uname" = "HP-UX" ]; then lib_path="/usr/lib/hpux$OS_BITS" if [ -f $lib_path/libpthread.so ]; then LIBS="-L$lib_path -lpthread" have_pthread=1 fi elif [ "$uname" = "FreeBSD" ]; then if [ -f /usr/lib/libc_r.so ]; then line=$(nm -D /usr/lib/libc_r.so | grep -F pthread_create | grep -w T) if [ $? -eq 0 ]; then LIBS="$LIBS -lc_r" have_pthread=1 fi elif [ -f /lib64/libc_r.so ]; then line=$(nm -D /lib64/libc_r.so | grep -F pthread_create | grep -w T) if [ $? -eq 0 ]; then LIBS="$LIBS -lc_r" have_pthread=1 fi elif [ -f /usr/lib64/libc_r.so ]; then line=$(nm -D /usr/lib64/libc_r.so | grep -F pthread_create | grep -w T) if [ $? -eq 0 ]; then LIBS="$LIBS -lc_r" have_pthread=1 fi fi fi if [ $have_pthread -eq 0 ] && [ "$uname" != "Darwin" ]; then /sbin/ldconfig -p | grep -F libpthread.so > /dev/null if [ $? -eq 0 ]; then LIBS="$LIBS -lpthread" else echo -E 'Require pthread lib, please check!' exit 2 fi fi sed_replace() { sed_cmd=$1 filename=$2 if [ "$uname" = "FreeBSD" ] || [ "$uname" = "Darwin" ]; then sed -i "" "$sed_cmd" $filename else sed -i "$sed_cmd" $filename fi } replace_makefile() { cp Makefile.in Makefile sed_replace "s#\\\$(CFLAGS)#$CFLAGS#g" Makefile sed_replace "s#\\\$(LIBS)#$LIBS#g" Makefile sed_replace "s#\\\$(TARGET_PREFIX)#$TARGET_PREFIX#g" Makefile sed_replace "s#\\\$(LIB_VERSION)#$LIB_VERSION#g" Makefile sed_replace "s#\\\$(TARGET_CONF_PATH)#$TARGET_CONF_PATH#g" Makefile sed_replace "s#\\\$(ENABLE_STATIC_LIB)#$ENABLE_STATIC_LIB#g" Makefile sed_replace "s#\\\$(ENABLE_SHARED_LIB)#$ENABLE_SHARED_LIB#g" Makefile } base_path=$(pwd) cd src/include/fastcfs/ for subdir in api vote; do link=$(readlink $subdir) if [ $? -ne 0 ] || [ "$link" != "../../$subdir" -a "$link" != "../../$subdir/" ]; then ln -sf ../../$subdir $subdir fi done cd $base_path if [ -z $module ] || [ "$module" = 'auth_server' -o "$module" = 'authserver' ]; then if [ "x$exclude" != 'xauth_server' -a "x$exclude" != 'xauthserver' ]; then cd $base_path/src/auth/server replace_makefile make $param1 $param2 fi fi if [ -z $module ] || [ "$module" = 'auth_client' -o "$module" = 'authclient' ]; then if [ "x$exclude" != 'xauth_client' -a "x$exclude" != 'xauthclient' ] && [ "x$exclude" != 'xclient' ]; then cd $base_path/src/auth/client replace_makefile make $param1 $param2 cd $base_path/src/auth/client/tools replace_makefile make $param1 $param2 fi fi if [ -z $module ] || [ "$module" = 'vote_server' -o "$module" = 'voteserver' ]; then if [ "x$exclude" != 'xvote_server' -a "x$exclude" != 'xvoteserver' ]; then cd $base_path/src/vote/server replace_makefile make $param1 $param2 fi fi if [ -z $module ] || [ "$module" = 'vote_client' -o "$module" = 'voteclient' ]; then if [ "x$exclude" != 'xvote_client' -a "x$exclude" != 'xvoteclient' ] && [ "x$exclude" != 'xclient' ]; then cd $base_path/src/vote/client replace_makefile make $param1 $param2 cd $base_path/src/vote/client/tools replace_makefile make $param1 $param2 fi fi if [ -z $module ] || [ "$module" = 'api' ] || [ "$module" = 'fuseclient' -o "$module" = 'fuse_client' ]; then if [ "x$exclude" != 'xapi' ]; then cd $base_path/src/api replace_makefile make $param1 $param2 cd tests || exit replace_makefile make $param1 $param2 fi fi if [ "$uname" = "Linux" ]; then if [ -z $module ] || [ "$module" = 'preload' ]; then if [ "x$exclude" != 'xpreload' ]; then cd $base_path/src/preload replace_makefile make $param1 $param2 fi fi fi if [ -z $module ] || [ "$module" = 'tools' ]; then if [ "x$exclude" != 'xtools' ]; then cd $base_path/src/tools replace_makefile make $param1 $param2 fi fi if [ "$uname" = "Linux" ]; then if [ -z $module ] || [ "$module" = 'fuse_client' -o "$module" = 'fuseclient' ]; then if [ "x$exclude" != 'xfuse_client' -a "x$exclude" != 'xfuseclient' ]; then cd $base_path/src/fuse replace_makefile make $param1 $param2 fi fi fi if [ "$module" = 'jni' ]; then if [ ! -z $JAVA_HOME ]; then path=$JAVA_HOME elif [ ! -z $jni_file ]; then if [ -d $jni_file ]; then path=$jni_file else path=$(dirname $jni_file) fi else if [ -d /Library ]; then path=/Library else path=/usr/lib fi fi files=$(find $path -name jni.h 2>/dev/null | grep '/include/jni.h$') if [ -z "$files" ]; then files=$(locate jni.h | grep '/include/jni.h$') if [ -z "$files" ]; then echo "can't locate jni.h, please install java SDK first." exit 2 fi fi count=$(echo "$files" | wc -l) if [ $count -eq 1 ]; then filename=$files else i=0 for file in $files; do i=$(expr $i + 1) echo "$i. $file" done printf "please input the correct file no.: " read n if [ -z "$n" ]; then echo "invalid file no." exit 2 fi filename=$(echo "$files" | head -n $n | tail -n 1) fi INCLUDES= path=$(dirname $filename) for d in $(find $path -type d); do INCLUDES="$INCLUDES -I$d" done cd $base_path/src/java/jni replace_makefile sed_replace "s#\\\$(INCLUDES)#$INCLUDES#g" Makefile make $param1 $param2 fi ================================================ FILE: mkdocs.yml ================================================ site_name: FastCFS Docs site_description: 'FastCFS Documents,Version: v3.2.0' repo_name: GitHub repo_url: https://github.com/happyfish100/FastCFS/ plugins: - search - mike: version_selector: true nav: - Introduction: - FastCFS Introduction: "index.md" - Release Notes: "ReleaseNotes.md" - Benchmarks: "benchmark.md" - Concepts: - Architecture: - FastDir: "concepts/fastDir.md" - FastStore: "concepts/fast-store.md" - FastAuth: "concepts/fastDir.md" - UserGuide: - Quickstart: "Easy-install-detail-zh_CN.md" - Install: "INSTALL.md" markdown_extensions: - toc: permalink: True - footnotes - tables - meta # 定义元数据,通过文章上下文控制,如disqus - pymdownx.caret # 下划线上标 - pymdownx.tilde # 删除线下标 - pymdownx.critic # 增加删除修改高亮注释,可修饰行内或段落 - pymdownx.details # 提示块可折叠 - pymdownx.inlinehilite # 行内代码高亮 - pymdownx.mark # 文本高亮 - pymdownx.smartsymbols # 符号转换 - pymdownx.superfences # 代码嵌套在列表里 - codehilite: # 代码高亮,显示行号 guess_lang: false linenums: true - pymdownx.tasklist: # 复选框checklist custom_checkbox: true theme: name: material logo: img/logo.png favicon: img/logo.png palette: - scheme: default toggle: icon: material/toggle-switch-off-outline name: Switch to dark mode - scheme: slate primary: deep orange accent: deep orange toggle: icon: material/toggle-switch name: Switch to light mode extra: version: provider: mike alternate: - name: English link: / lang: en - name: 中文 link: /zh/ lang: zh ================================================ FILE: shell/Dockerfile ================================================ FROM nginx:mainline-alpine # COPY dependency.2.0.1.settings /usr/share/nginx/html/ COPY conf.2.3.0.tpl.tar.gz /usr/share/nginx/html/ ================================================ FILE: shell/conf_tpl_tar.sh ================================================ #!/bin/bash # # conf_tpl_tar.sh is a tool for quickly pack FastCFS cluster config files to template tar. # this is for FastCFS developer. # BUILD_PATH="build" FDIR_LIB="fastDIR" STORE_LIB="faststore" shell_name=$0 conf_file_version=$1 update_sources=$2 #---Usage info section begin---# print_usage() { # Print usage to console. echo "" echo "Usage: $shell_name [update]" echo "" echo "A self-sufficient config file templates pack shell for FastCFS cluster" echo "" echo "version:" echo " Version of the tar file, exp: 3.4.0." echo " Tar file example: conf.3.4.0.tpl.tar.gz" echo "" echo "update: Pull source code from remote before execute tar command." echo "" } if [ -z $conf_file_version ]; then print_usage exit 1 fi #---Usage info section end---# pull_source_code() { local module_name=$1 local source_path=$2 if ! [ -d $source_path ]; then echo "ERROR: source code path {$source_path} for $module_name not exist." exit 1 fi cd $source_path echo "INFO: =====Begin to pull $module_name, it will take some time...=====" git checkout master git pull cd - } pull_source_codes() { # Update source code from github.com. echo "INFO: Begin to pull source codes..." # Pull fastCFS self. pull_source_code "FastCFS" ../ # Pull fastDIR pull_source_code fastDIR ../../fastDIR # Pull faststore pull_source_code faststore ../../faststore } if [ "$update_sources" = "update" ]; then pull_source_codes fi config_file_template_path="conf.$conf_file_version.tpl" config_file_template_tar="$config_file_template_path.tar.gz" # Create build path if not exists. if ! [ -d $config_file_template_path ]; then mkdir -m 775 $config_file_template_path echo "INFO: temp tpl path {$config_file_template_path} not exist, created." else rm -rf "$config_file_template_path/auth" rm -rf "$config_file_template_path/fcfs" rm -rf "$config_file_template_path/fdir" rm -rf "$config_file_template_path/fstore" rm -rf "$config_file_template_path/vote" fi # Copy config files from source to temp tpl path mkdir -p "$config_file_template_path/auth/keys" mkdir "$config_file_template_path/fcfs" mkdir "$config_file_template_path/fdir" mkdir "$config_file_template_path/fstore" mkdir "$config_file_template_path/vote" `cp ../src/auth/conf/keys/*.key $config_file_template_path/auth/keys/` `cp ../src/auth/conf/*.conf $config_file_template_path/auth/` `cp ../src/vote/conf/*.conf $config_file_template_path/vote/` `cp ../conf/*.conf $config_file_template_path/fcfs/` `cp ../../fastDIR/conf/*.conf $config_file_template_path/fdir/` `cp ../../faststore/conf/*.conf $config_file_template_path/fstore/` `tar -czvf $config_file_template_tar $config_file_template_path` exit 0 ================================================ FILE: shell/fcfs.settings ================================================ # FastCFS cluster ops settings be used with shell fcfs.sh # This settings file must be placed at same path with fcfs.sh # You can edit it, or download it from http://www.fastcfs.com/ops. fastcfs_version=5.3.0 fuseclient_ips=192.168.142.9 ================================================ FILE: shell/fcfs.sh ================================================ #!/bin/bash # # fcfs.sh is a ops tool for quickly deploy FastCFS clusters. # It only relying on SSH access to the servers and sudo. # It runs fully on your workstation, requiring no servers, databases, or anything like that. # # If you set up and tear down FastCFS clusters a lot, and want minimal extra repeating works, # this is for you. # #---1. Config path section begin---# declare -ir MIN_VERSION_OF_Ubuntu=18 declare -ir MIN_VERSION_OF_Debian=10 declare -ir MIN_VERSION_OF_Deepin=20 declare -ir MIN_VERSION_OF_CentOS=7 declare -ir MIN_VERSION_OF_Red=8 declare -ir MIN_VERSION_OF_Rocky=8 declare -ir MIN_VERSION_OF_Oracle=8 declare -ir MIN_VERSION_OF_Fedora=20 declare -ir MIN_VERSION_OF_AlmaLinux=7 declare -ir MIN_VERSION_OF_Alibaba=2 declare -ir MIN_VERSION_OF_Anolis=7 declare -ir MIN_VERSION_OF_Amazon=2 declare -ir MIN_VERSION_OF_openEuler=20 declare -ir MIN_VERSION_OF_UOS=20 declare -ir MIN_VERSION_OF_BigCLoud=21 YUM_OS_ARRAY=(Red Rocky Oracle Fedora CentOS AlmaLinux Alibaba Anolis Amazon openEuler Kylin UOS BigCLoud) APT_OS_ARRAY=(Ubuntu Debian Deepin) repo_affix="" fcfs_settings_file="fcfs.settings" fcfs_dependency_file_server="http://www.fastken.cn/fastcfs/ops/dependency" fcfs_cache_path=".fcfs" fcfs_installed_file="installed.settings" STORE_CONF_FILES=(client.conf server.conf cluster.conf storage.conf dbstore.conf) STORE_CONF_PATH="/etc/fastcfs/fstore/" STORE_LOG_FILE="/opt/fastcfs/fstore/logs/fs_serverd.log" FDIR_CONF_FILES=(client.conf cluster.conf server.conf dbstore.conf) FDIR_CONF_PATH="/etc/fastcfs/fdir/" FDIR_LOG_FILE="/opt/fastcfs/fdir/logs/fdir_serverd.log " VOTE_CONF_FILES=(client.conf cluster.conf server.conf) VOTE_CONF_PATH="/etc/fastcfs/vote/" VOTE_LOG_FILE="/opt/fastcfs/vote/logs/fcfs_voted.log " AUTH_CONF_FILES=(auth.conf client.conf cluster.conf server.conf session.conf) AUTH_CONF_PATH="/etc/fastcfs/auth/" AUTH_KEYS_FILES=(session_validate.key) AUTH_KEYS_PATH="/etc/fastcfs/auth/keys/" AUTH_LOG_FILE="/opt/fastcfs/auth/logs/fcfs_authd.log" FUSE_CONF_FILES=(fuse.conf) FUSE_CONF_PATH="/etc/fastcfs/fcfs/" FUSE_LOG_FILE="/opt/fastcfs/fcfs/logs/fcfs_fused.log" #---1. Config path section end---# #---2. Get shell and command section begin---# shell_name=$0 shell_command=$1 local_os_uname=$(uname) #---2. Get shell and command section end---# #---3. Usage info section begin---# print_usage() { # Print usage to console. echo "" echo "Usage: $shell_name [module] [node]" echo "" echo "A self-sufficient operation shell for FastCFS cluster" echo "" echo "Commands:" echo " setup Setup FastCFS softwares, a shortcut command combines install, config, and restart" echo " install Install FastCFS softwares" echo " reinstall Reinstall FastCFS softwares, only used for yum" echo " erase Erase FastCFS softwares" echo " remove Remove FastCFS softwares, same as erase" echo " config Copy cluster config files to target host path" echo " start Start all or one module service in cluster" echo " stop Stop all or one module service in cluster" echo " restart Restart all or one module service in cluster" echo " tail Display the last part of the specified module's log" echo " status Display the service processes status" echo " help Show the detail of commands and examples" echo "" } print_detail_usage() { # Print usage to console. print_usage echo "Modules:" echo " fvote FastCFS vote server" echo " fdir fastDIR server" echo " fstore faststore server" echo " fauth FastCFS auth server" echo " fuseclient FastCFS fuse client" echo "" echo "Node:" echo " You can specify a single cluster IP, or command will be executed on all nodes." echo "" echo "Tail command options:" echo " -n number" echo " The location is number lines" echo "" echo " -number" echo " The location is number lines" echo "" echo "Examples:" echo " $shell_name setup" echo " Setup all FastCFS softwares on all nodes, then config and start module services." echo "" echo " $shell_name install" echo " Install all FastCFS softwares on all nodes." echo "" echo " $shell_name erase" echo " Erase all FastCFS softwares on all nodes." echo "" echo " $shell_name config fdir 172.16.168.128" echo " Copy fdir config file to host 172.16.168.128." echo "" echo " $shell_name start fdir" echo " Start all fdir servers." echo "" echo " $shell_name start fdir 172.16.168.128" echo " Start fdir server on host 172.16.168.128." echo "" echo " $shell_name tail fdir 172.16.168.128 -n 100" echo " Display the last 100 lines of fdir server log." echo "" echo " $shell_name status" echo " Display all modules service process status." echo "" echo " $shell_name status fdir" echo " Display all the status of fdir service processes status." echo "" echo "" } case "$shell_command" in 'setup' | 'install' | 'reinstall' | 'erase' | 'remove' | 'config' | 'start' | 'restart' | 'stop' | 'tail' | 'status') ;; 'help') print_detail_usage exit 1 ;; *) print_usage exit 1 ;; esac #---3. Usage info section end---# #---4. Tool functions section begin---# sed_replace() { local sed_cmd=$1 local file_name=$2 local current_uname=$(uname) if [ "$current_uname" = "FreeBSD" ] || [ "$current_uname" = "Darwin" ]; then sed -i "" "$sed_cmd" $file_name else sed -i "$sed_cmd" $file_name fi } split_to_array() { if ! [ -z $2 ]; then IFS=',' read -ra $2 <<< "$1" fi } # Check preceding version less than or equals following. function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } # Execute command on a host over SSH execute_remote_command() { local remote_host=$1 local remote_command=$2 local result=$(ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -T $remote_host $remote_command) echo $result } # Execute command on a host over SSH without return execute_remote_command_no_return() { local remote_host=$1 local remote_command=$2 ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -T $remote_host $remote_command } # Get remote server's uname get_remote_uname() { local remote_host=$1 local remote_uname=$(execute_remote_command $remote_host "uname") echo $remote_uname } # Get remote server's os name get_remote_osname() { local remote_host=$1 local osname_command='cat /etc/os-release | grep -w NAME | awk -F '\''='\'' '\''{print $2;}'\'' | awk -F '\''"'\'' '\''{if (NF==3) {print $2} else {print $1}}'\'' | awk '\''{print $1}'\''' local remote_osname=$(execute_remote_command $remote_host "$osname_command") echo "$remote_osname" } # Get remote Ubuntu server's version get_os_version() { local remote_host=$1 local os_version_command='cat /etc/os-release | grep -w VERSION_ID | awk -F '\''='\'' '\''{print $2;}'\'' | awk -F '\''"'\'' '\''{if (NF==3) {print $2} else {print $1}}'\''' local remote_os_version=$(execute_remote_command $remote_host "$os_version_command") echo "$remote_os_version" } # Get remote CentOS server's version get_centos_version() { local remote_host=$1 local centos_version_command='cat /etc/system-release | awk '\''{print $4}'\''' local remote_centos_version=$(execute_remote_command $remote_host "$centos_version_command") echo "$remote_centos_version" } #---4. Tool functions section end---# #---5. Parse cluster servers section begin---# INT_REGEXP='^[0-9]+$' RANGE_REGEXP='^\[[0-9]*, *[0-9]*\]$' parse_value_in_section() { local conf_file=$1 local section_name=$2 local field_name=$3 local value=$(\ sed -n "/^\\[$section_name\\]/,/^\[/ p" $conf_file | \ sed -n "/^$field_name *=/ p" | \ sed 's/\([^=]*\)=\([^=]\)/\2/g' | \ sed 's/ *^//g' | sed 's/ *$//g') echo $value } parse_fstore_servers() { local conf_file=$1 if ! [ -f $conf_file ]; then echo "ERROR: fstore cluster config file $conf_file does not exist." exit 1 fi local server_groups=`sed -n '/^\[server-group/ p' $conf_file | sed 's/\[//' | sed 's/\]//'` for server_group in ${server_groups[@]}; do let fstore_group_count=$fstore_group_count+1 local fstore_server_hosts="" local server_ids=$(parse_value_in_section $conf_file $server_group "server_ids") # echo "server_ids in $server_group is:==$server_ids==" if [[ $server_ids =~ $INT_REGEXP ]]; then #单个整数,只有一个节点 # echo "$server_ids is integer" local server_host=$(parse_value_in_section $conf_file "server-$server_ids" "host") server_host=${server_host%%:*} if [ -n server_host ]; then fstore_server_hosts=$server_host fi elif [[ $server_ids =~ $RANGE_REGEXP ]]; then # echo "[$server_ids] is range" local src_str=`echo $server_ids | sed 's/\[//' | sed 's/\]//' | sed 's/, */../'` local dest="echo {$src_str}" for server_id in $(eval $dest); do local server_host=$(parse_value_in_section $conf_file "server-$server_id" "host") server_host=${server_host%%:*} if [ -n server_host ]; then fstore_server_hosts="$fstore_server_hosts $server_host" fi done else echo "ERROR: Invalid server_ids format:[$server_ids] in config file $conf_file" exit 1 fi fstore_server_hosts=`echo $fstore_server_hosts | sed 's/ *^//g'` if [[ -n $fstore_server_hosts ]]; then local array_str="fstore_group_$fstore_group_count=($fstore_server_hosts)" eval $array_str else echo "ERROR: Parse fstore server hosts failed, $conf_file must conform to format below:" echo " [server-group-1]" echo " server_ids = [1, 3]" echo " # Or server_ids = 1" echo " [server-1]" echo " host = 10.0.1.11" exit 1 fi done } parse_fdir_servers() { local conf_file=$1 if ! [ -f $conf_file ]; then echo "ERROR: fdir cluster config file $conf_file does not exist." exit 1 fi local fdir_servers=`sed -n '/^\[server-/ p' $conf_file | sed 's/\[//' | sed 's/\]//'` local fdir_server_hosts="" for fdir_server in ${fdir_servers[@]}; do local server_host=$(parse_value_in_section $conf_file $fdir_server "host") server_host=${server_host%%:*} if [ -n server_host ]; then fdir_server_hosts="$fdir_server_hosts $server_host" fi done fdir_server_hosts=`echo $fdir_server_hosts | sed 's/ *^//g'` if [[ -n $fdir_server_hosts ]]; then local array_str="fdir_group=($fdir_server_hosts)" eval $array_str else echo "ERROR: Parse fdir server hosts failed, $conf_file must conform to format below:" echo " [server-1]" echo " host = 10.0.1.11" echo " ..." exit 1 fi } parse_fauth_servers() { local conf_file=$1 if ! [ -f $conf_file ]; then echo "ERROR: fauth cluster config file $conf_file does not exist." exit 1 fi local fauth_servers=`sed -n '/^\[server-/ p' $conf_file | sed 's/\[//' | sed 's/\]//'` local fauth_server_hosts="" for fauth_server in ${fauth_servers[@]}; do local server_host=$(parse_value_in_section $conf_file $fauth_server "host") server_host=${server_host%%:*} if [ -n server_host ]; then fauth_server_hosts="$fauth_server_hosts $server_host" fi done fauth_server_hosts=`echo $fauth_server_hosts | sed 's/ *^//g'` if [[ -n $fauth_server_hosts ]]; then local array_str="fauth_group=($fauth_server_hosts)" eval $array_str else echo "ERROR: Parse fauth server hosts failed, $conf_file must conform to format below:" echo " [server-1]" echo " host = 10.0.1.11" echo " ..." exit 1 fi } parse_fvote_servers() { local conf_file=$1 if ! [ -f $conf_file ]; then echo "ERROR: fvote cluster config file $conf_file does not exist." exit 1 fi local fvote_servers=`sed -n '/^\[server-/ p' $conf_file | sed 's/\[//' | sed 's/\]//'` local fvote_server_hosts="" for fvote_server in ${fvote_servers[@]}; do local server_host=$(parse_value_in_section $conf_file $fvote_server "host") server_host=${server_host%%:*} if [ -n server_host ]; then fvote_server_hosts="$fvote_server_hosts $server_host" fi done fvote_server_hosts=`echo $fvote_server_hosts | sed 's/ *^//g'` if [[ -n $fvote_server_hosts ]]; then local array_str="fvote_group=($fvote_server_hosts)" eval $array_str else echo "ERROR: Parse fvote server hosts failed, $conf_file must conform to format below:" echo " [server-1]" echo " host = 10.0.1.11" echo " ..." exit 1 fi } #---5. Parse cluster servers section end---# #---6. Settings and cluster info section begin---# check_fcfs_cache_path() { if ! [ -d $fcfs_cache_path ]; then if ! mkdir -p $fcfs_cache_path; then echo "ERROR: Create fcfs cache path failed, $fcfs_cache_path!" exit 1 else echo "INFO: Created fcfs cache path successfully, $fcfs_cache_path." fi fi } # Load cluster settings from file fcfs.settings load_fcfs_settings() { if ! [ -f $fcfs_settings_file ]; then echo "ERROR: File $fcfs_settings_file does not exist" exit 1 else fcfs_settings=`sed -e 's/#.*//' -e '/^$/ d' $fcfs_settings_file` eval $fcfs_settings split_to_array $fuseclient_ips fuseclient_ip_array if [ -z $fastcfs_version ]; then echo "ERROR: param fastcfs_version has no value in $fcfs_settings_file" exit 1 fi if [ -z $fuseclient_ips ]; then echo "ERROR: param fuseclient_ips has no value in $fcfs_settings_file" exit 1 fi fi } # Load installed settings from file .fcfs/installed.settings load_installed_settings() { local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file if [ -f $installed_mark_file ]; then local installed_marks=`sed -e 's/#.*//' -e '/^$/ d' $installed_mark_file` eval $installed_marks fi } # Load cluster dependent lib version settings from file dependency.[FastCFS-version].settings load_dependency_settings() { dependency_file_version=$1 if [ -z $dependency_file_version ]; then echo "ERROR: Dependency file version cannot be empty." exit 1 fi dependency_settings_file="$fcfs_cache_path/dependency.$dependency_file_version.settings" if ! [ -f $dependency_settings_file ]; then # File not exist in local path, will get it from remote server match the version echo "WARN: File $dependency_settings_file does not exist, getting it from remote server $fcfs_dependency_file_server." check_fcfs_cache_path remote_file_url="$fcfs_dependency_file_server/$dependency_file_version" download_res=`curl -f -o $dependency_settings_file $remote_file_url` if ! [ -f $dependency_settings_file ]; then echo "ERROR: Cannot download file $dependency_settings_file from $remote_file_url." echo " Please make sure that remote file exists and network is accessible." exit 1 fi fi dependency_settings=`sed -e 's/#.*//' -e '/^$/ d' $dependency_settings_file` eval $dependency_settings if [ -z $libfastcommon ]; then echo "WARN: Dependency libfastcommon has no version value in $dependency_settings_file." fi if [ -z $libserverframe ]; then echo "WARN: Dependency libserverframe has no version value in $dependency_settings_file." fi if [ -z $fvote ]; then echo "WARN: Dependency fvote has no version value in $dependency_settings_file." fi if [ -z $fauth ]; then echo "WARN: Dependency fauth has no version value in $dependency_settings_file." fi if [ -z $fdir ]; then echo "WARN: Dependency fdir has no version value in $dependency_settings_file." fi if [ -z $fstore ]; then echo "WARN: Dependency fstore has no version value in $dependency_settings_file." fi } # Get module name from command args, if not specify, will handle all modules. fdir_need_execute=0 fstore_need_execute=0 fauth_need_execute=0 fvote_need_execute=0 fuseclient_need_execute=0 has_module_param=1 check_module_param() { if [ $# -gt 1 ]; then module_name=$2 case "$module_name" in 'fdir') fdir_need_execute=1 ;; 'fstore') fstore_need_execute=1 ;; 'fauth') fauth_need_execute=1 ;; 'fvote') fvote_need_execute=1 ;; 'fuseclient') fuseclient_need_execute=1 ;; *) has_module_param=0 if ! [ $shell_command = 'tail' ] || ! [[ $module_name =~ ^- ]]; then echo "ERROR: Module name invalid, $module_name." echo " Allowed module names: fdir, fstore, fauth, fvote, fuseclient." exit 1 else fdir_need_execute=1 fstore_need_execute=1 fauth_need_execute=1 fvote_need_execute=1 fuseclient_need_execute=1 fi ;; esac else has_module_param=0 fdir_need_execute=1 fstore_need_execute=1 fauth_need_execute=1 fvote_need_execute=1 fuseclient_need_execute=1 fi } # Get node host from command args, if not specify, will handle all nodes. # It must specify after module name. has_node_param=0 check_node_param() { if [ $# -gt 2 ] && ! [[ $3 =~ ^- ]] && [ $has_module_param = 1 ]; then node_host_need_execute=$3 has_node_param=1 fi } # Load cluster groups from cluster config files. # fdir_group=(172.16.168.128) # fstore_group_count=1 # fstore_group_1=(172.16.168.128) # fauth_group=(172.16.168.128) load_cluster_groups() { if [ $fdir_need_execute -eq 1 ]; then parse_fdir_servers "conf/fdir/cluster.conf" fi if [ $fstore_need_execute -eq 1 ]; then parse_fstore_servers "conf/fstore/cluster.conf" fi if [ $fauth_need_execute -eq 1 ]; then parse_fauth_servers "conf/auth/cluster.conf" fi if [ $fvote_need_execute -eq 1 ]; then parse_fvote_servers "conf/vote/cluster.conf" fi } replace_cluster_osname_mark() { local mark_file=$1 local remote_osname=$2 sed_replace "s#^cluster_host_osname=.*#cluster_host_osname=$remote_osname#g" $mark_file } save_cluster_osname_mark() { check_fcfs_cache_path local remote_osname=$1 local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file if [ -f $installed_mark_file ]; then local cluster_osname_marks=`grep cluster_host_osname $installed_mark_file` if [ -z $cluster_osname_marks ]; then echo "cluster_host_osname=$remote_osname" >> $installed_mark_file else # replace old value replace_cluster_osname_mark $installed_mark_file "$remote_osname" fi else echo "cluster_host_osname=$remote_osname" >> $installed_mark_file fi } # Check remote host os and version. # The whole cluster's host must have same os type. # The hosts os version must great than or equal xxx_MIN_VERSION check_remote_os_and_version() { pkg_manage_tool="" cluster_host_osname="" declare -a all_server_ips all_server_ips+=(${fdir_group[@]}) if [ ! -z $fstore_group_count ]; then for ((i=1; i <= $fstore_group_count; i++)); do local fstore_group="fstore_group_$i[@]" all_server_ips+=(${!fstore_group}) done fi all_server_ips+=(${fauth_group[@]}) all_server_ips+=(${fvote_group[@]}) all_server_ips+=(${fuseclient_ip_array[@]}) declare -a distinct_server_ips for server_ip in ${all_server_ips[@]}; do if ! [[ ${distinct_server_ips[*]} =~ (^|[[:space:]])"$server_ip"($|[[:space:]]) ]]; then distinct_server_ips+=("$server_ip") echo "INFO: Begin check os name and version on server $server_ip" local remote_uname=$(get_remote_uname $server_ip) if [ $remote_uname = 'Linux' ]; then local remote_osname=$(get_remote_osname $server_ip) if [ "$cluster_host_osname" != '' ]; then if [ "$cluster_host_osname" != "$remote_osname" ]; then echo "Error: Cluster's servers must have same OS, $cluster_host_osname and $remote_osname cannot be mixed use" exit 1 fi else cluster_host_osname=$remote_osname if [[ " ${YUM_OS_ARRAY[@]} " =~ " ${remote_osname} " ]]; then pkg_manage_tool="yum" elif [[ " ${APT_OS_ARRAY[@]} " =~ " ${remote_osname} " ]]; then pkg_manage_tool="apt" fi fi local remote_osversion if [ $remote_osname = 'CentOS' ]; then remote_osversion=$(get_centos_version $server_ip) elif [[ " ${YUM_OS_ARRAY[@]} " =~ " ${remote_osname} " ]] || [[ " ${APT_OS_ARRAY[@]} " =~ " ${remote_osname} " ]]; then remote_osversion=$(get_os_version $server_ip) else echo "Error: Unsupport OS, $remote_osname on server $server_ip" exit 1 fi declare -i remote_os_major_version=$(echo $remote_osversion | awk -F '.' '{print $1}') local min_version_name="MIN_VERSION_OF_$remote_osname" local min_version=${!min_version_name} if [ ! -z $min_version ] && [ $remote_os_major_version -lt $min_version ]; then echo "$remote_osname's version must be great than or equal $min_version, but was $remote_os_major_version on server $server_ip" exit 1 fi else echo "ERROR: Unsupport OS, $remote_uname on server $server_ip" exit 1 fi fi done if [ "$cluster_host_osname" != '' ]; then save_cluster_osname_mark "$cluster_host_osname" else echo "ERROR: Check remote host os failed." exit 1 fi } fuseclient_share_fdir=0 fuseclient_share_fstore=0 fuseclient_share_fauth=0 fuseclient_share_fvote=0 check_if_client_share_servers() { if ! [ ${#fuseclient_ip_array[@]} -eq 0 ]; then for fuseclient_server_ip in ${fuseclient_ip_array[@]}; do for fdir_server_ip in ${fdir_group[@]}; do if [ $fdir_server_ip = "$fuseclient_server_ip" ]; then fuseclient_share_fdir=1 fi done if [ ! -z $fstore_group_count ]; then for ((i=1; i <= $fstore_group_count; i++)); do local fstore_group="fstore_group_$i[@]" for fstore_server_ip in ${!fstore_group}; do if [ $fstore_server_ip = "$fuseclient_server_ip" ]; then fuseclient_share_fstore=1 fi done done fi for fauth_server_ip in ${fauth_group[@]}; do if [ $fauth_server_ip = "$fuseclient_server_ip" ]; then fuseclient_share_fauth=1 fi done for fvote_server_ip in ${fvote_group[@]}; do if [ $fvote_server_ip = "$fuseclient_server_ip" ]; then fuseclient_share_fvote=1 fi done done fi } #---6. Settings and cluster info section end---# #---7. Iterate hosts for execute command section begin---# execute_command_on_fdir_servers() { local command_name=$1 local function_name=$2 local module_name=$3 if [ $fdir_need_execute -eq 1 ]; then local fdir_node_match_setting=0 for fdir_server_ip in ${fdir_group[@]}; do if [ -z $node_host_need_execute ] || [ $fdir_server_ip = "$node_host_need_execute" ]; then if [ $command_name = 'status' ]; then echo "" echo "INFO: Begin display $module_name status on server $fdir_server_ip." else echo "INFO: Begin $command_name $module_name on server $fdir_server_ip." fi $function_name $fdir_server_ip "$module_name" $command_name fi if [ $fdir_server_ip = "$node_host_need_execute" ]; then fdir_node_match_setting=1 fi done if ! [ -z $node_host_need_execute ] && [ $fdir_node_match_setting -eq 0 ]; then echo "ERROR: The node $node_host_need_execute not match host in fdir cluster.conf." fi fi } execute_command_on_fstore_servers() { local command_name=$1 local function_name=$2 local module_name=$3 if [ $fstore_need_execute -eq 1 ]; then local fstore_node_match_setting=0 for ((i=1; i <= $fstore_group_count; i++)); do local fstore_group="fstore_group_$i[@]" for fstore_server_ip in ${!fstore_group}; do if [ -z $node_host_need_execute ] || [ $fstore_server_ip = "$node_host_need_execute" ]; then if [ $command_name = 'status' ]; then echo "" echo "INFO: Begin display $module_name status on server $fstore_server_ip." else echo "INFO: Begin $command_name $module_name on server $fstore_server_ip." fi $function_name $fstore_server_ip "$module_name" $command_name fi if [ $fstore_server_ip = "$node_host_need_execute" ]; then fstore_node_match_setting=1 fi done done if ! [ -z $node_host_need_execute ] && [ $fstore_node_match_setting -eq 0 ]; then echo "ERROR: The node $node_host_need_execute not match host in fstore cluster.conf." fi fi } execute_command_on_fauth_servers() { local command_name=$1 local function_name=$2 local module_name=$3 if [ $fauth_need_execute -eq 1 ]; then local fauth_node_match_setting=0 for fauth_server_ip in ${fauth_group[@]}; do if [ -z $node_host_need_execute ] || [ $fauth_server_ip = "$node_host_need_execute" ]; then if [ $command_name = 'status' ]; then echo "" echo "INFO: Begin display $module_name status on server $fauth_server_ip." else echo "INFO: Begin $command_name $module_name on server $fauth_server_ip." fi $function_name $fauth_server_ip "$module_name" $command_name fi if [ $fauth_server_ip = "$node_host_need_execute" ]; then fauth_node_match_setting=1 fi done if ! [ -z $node_host_need_execute ] && [ $fauth_node_match_setting -eq 0 ]; then echo "ERROR: The node $node_host_need_execute not match host in fauth cluster.conf." fi fi } execute_command_on_fvote_servers() { local command_name=$1 local function_name=$2 local module_name=$3 if [ $fvote_need_execute -eq 1 ]; then local fvote_node_match_setting=0 for fvote_server_ip in ${fvote_group[@]}; do if [ -z $node_host_need_execute ] || [ $fvote_server_ip = "$node_host_need_execute" ]; then if [ $command_name = 'status' ]; then echo "" echo "INFO: Begin display $module_name status on server $fvote_server_ip." else echo "INFO: Begin $command_name $module_name on server $fvote_server_ip." fi $function_name $fvote_server_ip "$module_name" $command_name fi if [ $fvote_server_ip = "$node_host_need_execute" ]; then fvote_node_match_setting=1 fi done if ! [ -z $node_host_need_execute ] && [ $fvote_node_match_setting -eq 0 ]; then echo "ERROR: The node $node_host_need_execute not match host in fvote cluster.conf." fi fi } execute_command_on_fuseclient_servers() { local command_name=$1 local function_name=$2 local module_name=$3 if [ $fuseclient_need_execute -eq 1 ]; then if [ ${#fuseclient_ip_array[@]} -eq 0 ]; then echo "ERROR: Param fuseclient_ips has no value in $fcfs_settings_file." exit 1 else local fuseclient_node_match_setting=0 for fuseclient_server_ip in ${fuseclient_ip_array[@]}; do if [ -z $node_host_need_execute ] || [ $fuseclient_server_ip = "$node_host_need_execute" ]; then if [ $command_name = 'status' ]; then echo "" echo "INFO: Begin display $module_name status on server $fuseclient_server_ip." else echo "INFO: begin $command_name $module_name on server $fuseclient_server_ip." fi $function_name $fuseclient_server_ip "$module_name" $command_name fi if [ $fuseclient_server_ip = "$node_host_need_execute" ]; then fuseclient_node_match_setting=1 fi done if ! [ -z $node_host_need_execute ] && [ $fuseclient_node_match_setting -eq 0 ]; then echo "ERROR: The node $node_host_need_execute not match param fuseclient_ips in $fcfs_settings_file." exit 1 fi fi fi } #---7. Iterate hosts for execute command section end---# #---8. Install section begin---# check_remote_osname() { uname=$(uname) if [ $uname = 'Linux' ]; then osname=$(cat /etc/os-release | grep -w NAME | awk -F '=' '{print $2;}' | \ awk -F '"' '{if (NF==3) {print $2} else {print $1}}' | awk '{print $1}') if [ $osname = 'CentOS' ]; then osversion=$(cat /etc/system-release | awk '{print $4}') fi if [ -z $osversion ]; then osversion=$(cat /etc/os-release | grep -w VERSION_ID | awk -F '=' '{print $2;}' | \ awk -F '"' '{if (NF==3) {print $2} else {print $1}}') fi os_major_version=$(echo $osversion | awk -F '.' '{print $1}') if [ $osname = 'Fedora' ]; then if [ $os_major_version -lt 20 ]; then os_major_version=6 elif [ $os_major_version -lt 28 ]; then os_major_version=7 elif [ $os_major_version -lt 38 ]; then os_major_version=8 else os_major_version=9 fi elif [ $osname = 'Alibaba' ]; then if [ $os_major_version -lt 3 ]; then os_major_version=7 elif [ $os_major_version -lt 4 ]; then os_major_version=8 else os_major_version=9 fi elif [ $osname = 'Amazon' ]; then if [ $os_major_version -lt 3 ]; then os_major_version=7 elif [ $os_major_version -lt 2023 ]; then os_major_version=8 else os_major_version=9 fi elif [ $osname = 'openEuler' ]; then if [ $os_major_version -lt 24 ]; then os_major_version=8 else os_major_version=9 fi elif [ $osname = 'Kylin' ]; then os_major_version=$(echo $os_major_version | awk '{print $1}') if [ $os_major_version = 'V10' ]; then os_major_version=8 else os_major_version=9 fi elif [ $osname = 'UOS' ] || [ $osname = 'BigCLoud' ]; then os_major_version=8 fi else echo "Error: Unsupport OS, $uname" 1>&2 exit 1 fi } check_yum_install_fastos_repo() { repo=$(rpm -q FastOSrepo 2>/dev/null) if [ $? -ne 0 ]; then sudo rpm -ivh http://www.fastken.cn/yumrepo${repo_affix}/el${os_major_version}/noarch/FastOSrepo-1.0.1-1.el${os_major_version}.noarch.rpm if [ $? -ne 0 ]; then echo "ERROR: FastOSrepo rpm install failed." exit 1 fi fi } check_apt_install_fastos_repo() { if [ ! -f /etc/apt/sources.list.d/fastos.list ]; then sudo apt-get install curl gpg -y sudo curl http://www.fastken.cn/aptrepo${repo_affix}/packages.fastos.pub | sudo gpg --dearmor > /tmp/fastos-archive-keyring.gpg sudo install -D -o root -g root -m 644 /tmp/fastos-archive-keyring.gpg /usr/share/keyrings/fastos-archive-keyring.gpg sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/fastos-archive-keyring.gpg] http://www.fastken.cn/aptrepo${repo_affix}/fastos/ fastos main" > /etc/apt/sources.list.d/fastos.list' sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/fastos-archive-keyring.gpg] http://www.fastken.cn/aptrepo${repo_affix}/fastos-debug/ fastos-debug main" > /etc/apt/sources.list.d/fastos-debug.list' sudo rm -f /tmp/fastos-archive-keyring.gpg fi } execute_yum() { local yum_command=$1 local program_name=$2 repo_affix=$3 if [ $os_major_version -ge 7 ]; then check_yum_install_fastos_repo echo "INFO: yum $yum_command $program_name -y." sudo yum $yum_command $program_name -y if [ $? -ne 0 ]; then echo "ERROR: \"yum $yum_command $program_name -y\" execute failed." exit 1 fi else echo "ERROR: Unsupport OS, $uname" 1>&2 echo " Command setup and install can only be used for CentOS 7 or higher." exit 1 fi } execute_apt() { local apt_command=$1 local program_name=$2 repo_affix=$3 if [ $apt_command = 'install' ]; then check_apt_install_fastos_repo sudo apt-get update fi echo "INFO: apt $apt_command $program_name -y." sudo apt-get $apt_command $program_name -y if [ $? -ne 0 ]; then echo "ERROR: \"apt $apt_command $program_name -y\" execute failed." exit 1 fi } execute_yum_on_remote() { local remote_host=$1 local program_name=$2 local yum_command=$3 execute_remote_command_no_return $remote_host " $(declare -f check_remote_osname); check_remote_osname; $(declare -f check_yum_install_fastos_repo); $(declare -f execute_yum); execute_yum $yum_command \"$program_name\" \"$repo_affix\"" if [ $? -ne 0 ]; then exit 1 fi } execute_apt_on_remote() { local remote_host=$1 local program_name=$2 local apt_command=$3 execute_remote_command_no_return $remote_host " $(declare -f check_apt_install_fastos_repo); $(declare -f execute_apt); execute_apt $apt_command \"$program_name\" \"$repo_affix\"" if [ $? -ne 0 ]; then exit 1 fi } replace_installed_version_mark() { # Replace host with ip of current host. local mark_file=$1 local installed_version=$2 sed_replace "s#^fastcfs_version_installed=.*#fastcfs_version_installed=$installed_version#g" $mark_file } save_installed_version_mark() { check_fcfs_cache_path local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file if [ -f $installed_mark_file ]; then local installed_marks=`grep fastcfs_version_installed $installed_mark_file` if [ -z $installed_marks ]; then echo "fastcfs_version_installed=$fastcfs_version" >> $installed_mark_file else # replace old value replace_installed_version_mark $installed_mark_file "$fastcfs_version" fi else echo "fastcfs_version_installed=$fastcfs_version" >> $installed_mark_file fi } remove_installed_mark() { local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file if [ -f $installed_mark_file ]; then if ! rm -rf $installed_mark_file; then echo "ERROR: Delete installed mark file failed, $installed_mark_file!" exit 1 fi fi } save_installed_mark() { check_fcfs_cache_path local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file if [ -f $installed_mark_file ]; then local installed_marks=`grep fastcfs_installed $installed_mark_file` if [ -z $installed_marks ]; then echo "fastcfs_installed=1" >> $installed_mark_file fi else echo "fastcfs_installed=1" >> $installed_mark_file fi } check_installed_version() { # First install cannot specify module if [ -z $fastcfs_version_installed ] && [ $has_module_param = 1 ]; then echo "ERROR: First execute setup or install cannot specify module." exit 1 elif ! [ -z $fastcfs_version_installed ] && [ $fastcfs_version_installed = $fastcfs_version ]; then if [ "$shell_command" = "reinstall" ]; then # Before reinstall programm, need user to reconfirm for ((;;)) do echo -n "WARN: Reinstall the installed program confirm[y/N]" read var if ! [ "$var" = "y" ] && ! [ "$var" = "Y" ] && ! [ "$var" = "yes" ] && ! [ "$var" = "YES" ]; then exit 1 fi break; done else if ! [ $has_module_param = 1 -a $has_node_param = 1 ]; then echo "ERROR: FastCFS $fastcfs_version have installed." exit 1 fi fi elif ! [ -z $fastcfs_version_installed ] && ! [ $fastcfs_version_installed = $fastcfs_version ]; then if version_le $fastcfs_version_installed $fastcfs_version; then # Upgrade cannot specify module if [ $has_module_param = 1 ]; then echo "ERROR: Upgrade cannot specify module." exit 1 fi for ((;;)) do echo -n "WARN: Upgrade installed program confirm, from $fastcfs_version_installed to $fastcfs_version ? [y/N]" read var if ! [ "$var" = "y" ] && ! [ "$var" = "Y" ] && ! [ "$var" = "yes" ] && ! [ "$var" = "YES" ]; then exit 1 fi break; done else # Downgrade must remove old version first echo "ERROR: Downgrade install must remove old version first." exit 1 fi fi } # Install packages to target by yum manage nodes. install_packages_by_yum() { check_installed_version fdir_programs="fastDIR-server-$fdir libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth" execute_command_on_fdir_servers install execute_yum_on_remote "$fdir_programs" fstore_programs="faststore-server-$fstore libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth" execute_command_on_fstore_servers install execute_yum_on_remote "$fstore_programs" fauth_programs="FastCFS-auth-server-$fauth libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth fastDIR-client-$fdir" execute_command_on_fauth_servers install execute_yum_on_remote "$fauth_programs" fvote_programs="FastCFS-vote-server-$fvote libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-vote-client-$fvote" execute_command_on_fvote_servers install execute_yum_on_remote "$fvote_programs" fuseclient_programs="FastCFS-fused-$fuseclient libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth FastCFS-api-libs-$fcfsapi faststore-client-$fstore fastDIR-client-$fdir" execute_command_on_fuseclient_servers install execute_yum_on_remote "$fuseclient_programs" save_installed_mark save_installed_version_mark } # Remove packages from target by yum manage nodes. erase_packages_by_yum() { # Before remove programm, need user to reconfirm for ((;;)) do echo -n "WARN: Delete the installed program confirm[y/N]" read var if ! [ "$var" = "y" ] && ! [ "$var" = "Y" ] && ! [ "$var" = "yes" ] && ! [ "$var" = "YES" ]; then exit 1 fi break; done fdir_programs="fastDIR-server libfastcommon libserverframe FastCFS-auth-client FastOSrepo" execute_command_on_fdir_servers erase execute_yum_on_remote "$fdir_programs" fstore_programs="faststore-server libfastcommon libserverframe FastCFS-auth-client FastOSrepo" execute_command_on_fstore_servers erase execute_yum_on_remote "$fstore_programs" fauth_programs="FastCFS-auth-server libfastcommon libserverframe FastCFS-auth-client fastDIR-client FastOSrepo" execute_command_on_fauth_servers erase execute_yum_on_remote "$fauth_programs" fvote_programs="FastCFS-vote-server libfastcommon libserverframe FastCFS-vote-client FastOSrepo" execute_command_on_fvote_servers erase execute_yum_on_remote "$fvote_programs" fuseclient_programs="FastCFS-fused libfastcommon libserverframe FastCFS-auth-client FastCFS-api-libs faststore-client fastDIR-client FastOSrepo" execute_command_on_fuseclient_servers erase execute_yum_on_remote "$fuseclient_programs" remove_installed_mark } # Reinstall packages to target by yum manage nodes. reinstall_packages_by_yum() { check_installed_version fdir_programs="fastDIR-server-$fdir libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth" execute_command_on_fdir_servers reinstall execute_yum_on_remote "$fdir_programs" fstore_programs="faststore-server-$fstore libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth" execute_command_on_fstore_servers reinstall execute_yum_on_remote "$fstore_programs" fauth_programs="FastCFS-auth-server-$fauth libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth fastDIR-client-$fdir" execute_command_on_fauth_servers reinstall execute_yum_on_remote "$fauth_programs" fvote_programs="FastCFS-vote-server-$fauth libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-vote-client-$fauth" execute_command_on_fvote_servers reinstall execute_yum_on_remote "$fvote_programs" fuseclient_programs="FastCFS-fused-$fuseclient libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth FastCFS-api-libs-$fcfsapi faststore-client-$fstore fastDIR-client-$fdir" execute_command_on_fuseclient_servers reinstall execute_yum_on_remote "$fuseclient_programs" save_installed_mark save_installed_version_mark } # Install packages to target by apt manage nodes. install_packages_by_apt() { execute_command_on_fdir_servers install execute_apt_on_remote "fastdir-server" execute_command_on_fstore_servers install execute_apt_on_remote "faststore-server" execute_command_on_fauth_servers install execute_apt_on_remote "fastcfs-auth-server" execute_command_on_fvote_servers install execute_apt_on_remote "fastcfs-vote-server" execute_command_on_fuseclient_servers install execute_apt_on_remote "fastcfs-fused" save_installed_mark } # Remove packages from target by apt manage nodes. erase_packages_by_apt() { # Before remove programm, need user to reconfirm for ((;;)) do echo -n "WARN: Delete the installed program confirm[y/N]" read var if ! [ "$var" = "y" ] && ! [ "$var" = "Y" ] && ! [ "$var" = "yes" ] && ! [ "$var" = "YES" ]; then exit 1 fi break; done execute_command_on_fdir_servers remove execute_apt_on_remote "fastdir-server" execute_command_on_fstore_servers remove execute_apt_on_remote "faststore-server" execute_command_on_fauth_servers remove execute_apt_on_remote "fastcfs-auth-server" execute_command_on_fvote_servers remove execute_apt_on_remote "fastcfs-vote-server" execute_command_on_fuseclient_servers remove execute_apt_on_remote "fastcfs-fused" remove_installed_mark } #---8. Install section end---# #---9. Config section begin---# check_remote_path() { dest_path=$1 if ! [ -d $dest_path ]; then if ! sudo mkdir -p $dest_path; then echo "ERROR: Create target conf path failed, $dest_path!" exit 1 fi fi } check_path_on_remote() { remote_host=$1 remote_target_path=$2 execute_remote_command_no_return $remote_host " $(declare -f check_remote_path); check_remote_path $remote_target_path" } check_paths_infile_remote() { # Check all paths in this config file, if not exist, will create it. check_conf_file=$1 check_result=`sed -n \ -e '/^mountpoint *=/ p' \ -e '/^base_path *=/ p' \ -e '/^path *=/ p' \ $check_conf_file|sed 's/\([^=]*\)=\([^=]\)/\2/g'` for check_path in ${check_result[@]}; do if ! [ -d $check_path ]; then if ! sudo mkdir -p $check_path; then echo "ERROR: Create target path in file $check_conf_file failed, $check_path!" exit 1 else echo "INFO: Created target path in file $check_conf_file successfully, $check_path." fi fi done } check_paths_infile() { remote_host=$1 remote_target_file=$2$3 execute_remote_command_no_return $remote_host " $(declare -f check_paths_infile_remote); check_paths_infile_remote $remote_target_file" } copy_config_to_remote() { local target_server_ip=$1 local target_module=$2 local src_path="conf/$target_module" local config_files="" local dest_path="" case "$target_module" in 'fdir') config_file_array="${FDIR_CONF_FILES[*]}" dest_path=$FDIR_CONF_PATH ;; 'fstore') config_file_array="${STORE_CONF_FILES[*]}" dest_path=$STORE_CONF_PATH ;; 'fauth') config_file_array="${AUTH_CONF_FILES[*]}" src_path="conf/auth" dest_path=$AUTH_CONF_PATH ;; 'keys') config_file_array="${AUTH_KEYS_FILES[*]}" src_path="conf/auth/keys" dest_path=$AUTH_KEYS_PATH ;; 'fvote') config_file_array="${VOTE_CONF_FILES[*]}" src_path="conf/vote" dest_path=$VOTE_CONF_PATH ;; 'fuseclient') config_file_array="${FUSE_CONF_FILES[*]}" src_path="conf/fcfs" dest_path=$FUSE_CONF_PATH ;; *) echo "ERROR: Target module name is invalid, $target_module." exit 1 ;; esac check_path_on_remote $target_server_ip $dest_path for CONF_FILE in ${config_file_array[@]}; do local tmp_src_file=$src_path/$CONF_FILE if [ -f $tmp_src_file ]; then echo "INFO: Copy file $tmp_src_file to $dest_path of server $target_server_ip." scp $tmp_src_file $target_server_ip:/tmp/ execute_remote_command_no_return $target_server_ip "sudo cp /tmp/$CONF_FILE $dest_path" execute_remote_command_no_return $target_server_ip "sudo rm /tmp/$CONF_FILE" if [ $? -ne 0 ]; then exit 1 fi local file_extension="${CONF_FILE##*.}" if [ $file_extension = "conf" ]; then check_paths_infile $target_server_ip $dest_path $CONF_FILE fi else echo "WARN: File $tmp_src_file does not exist." fi done } save_configed_mark() { check_fcfs_cache_path local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file if [ -f $installed_mark_file ]; then local configed_marks=`grep fastcfs_configed $installed_mark_file` if [ -z $configed_marks ]; then echo "fastcfs_configed=1" >> $installed_mark_file fi else echo "fastcfs_configed=1" >> $installed_mark_file fi } deploy_config_files() { if [ -z $fastcfs_configed ] && [ $has_module_param = 1 ]; then echo "ERROR: First execute config cannot specify module." exit 1 fi execute_command_on_fvote_servers config copy_config_to_remote fvote execute_command_on_fdir_servers config copy_config_to_remote fdir execute_command_on_fstore_servers config copy_config_to_remote fstore execute_command_on_fauth_servers config copy_config_to_remote fauth execute_command_on_fauth_servers config copy_config_to_remote keys execute_command_on_fuseclient_servers config copy_config_to_remote fuseclient if [ $fuseclient_share_fvote -eq 0 ]; then execute_command_on_fuseclient_servers config copy_config_to_remote fvote fi if [ $fuseclient_share_fdir -eq 0 ]; then execute_command_on_fuseclient_servers config copy_config_to_remote fdir fi if [ $fuseclient_share_fstore -eq 0 ]; then execute_command_on_fuseclient_servers config copy_config_to_remote fstore fi if [ $fuseclient_share_fauth -eq 0 ]; then execute_command_on_fuseclient_servers config copy_config_to_remote fauth execute_command_on_fuseclient_servers config copy_config_to_remote keys fi # Save configed mark into installed.settings save_configed_mark } #---9. Config section end---# #---10. Service op section begin---# service_op_on_remote() { service_name=$1 operate_mode=$2 conf_file=$3 sudo $service_name $conf_file $operate_mode if [ $? -ne 0 ] && [ $operate_mode != "stop" ] && [ $operate_mode != "status" ]; then echo "ERROR: Service $service_name $operate_mode failed." exit 1 fi } service_op() { target_server_ip=$1 target_module=$2 operate_mode=$3 case "$target_module" in 'fvote') service_name="fcfs_voted" conf_file="${VOTE_CONF_PATH}server.conf" ;; 'fdir') service_name="fdir_serverd" conf_file="${FDIR_CONF_PATH}server.conf" ;; 'fstore') service_name="fs_serverd" conf_file="${STORE_CONF_PATH}server.conf" ;; 'fauth') service_name="fcfs_authd" conf_file="${AUTH_CONF_PATH}server.conf" ;; 'fuseclient') service_name="fcfs_fused" conf_file="${FUSE_CONF_PATH}fuse.conf" ;; *) echo "ERROR: Target module name is invalid, $target_module." exit 1 ;; esac execute_remote_command_no_return $target_server_ip " $(declare -f service_op_on_remote); service_op_on_remote $service_name $operate_mode $conf_file" if [ $? -ne 0 ] && [ $operate_mode != "stop" ]; then exit 1 fi } cluster_service_op() { operate_mode=$1 execute_command_on_fvote_servers $operate_mode service_op fvote if [ $operate_mode != 'stop' ] && [ $operate_mode != 'status' ]; then sleep 1 fi execute_command_on_fdir_servers $operate_mode service_op fdir execute_command_on_fstore_servers $operate_mode service_op fstore if [ $operate_mode != 'stop' ] && [ $operate_mode != 'status' ]; then sleep 3 fi execute_command_on_fauth_servers $operate_mode service_op fauth if [ $operate_mode != 'stop' ] && [ $operate_mode != 'status' ]; then sleep 1 fi execute_command_on_fuseclient_servers $operate_mode service_op fuseclient } #---10. Service op section end---# #---11. Tail log section begin---# tail_remote_log() { local tail_server=$1 local tail_args=$2 execute_remote_command_no_return $tail_server "tail $tail_args" echo "========================================================" echo "========Log boundary====================================" echo "========================================================" } # View last N lines log of the cluster with system command tail; tail_log() { shift if [ $has_module_param = 1 ]; then shift if [ $has_node_param = 1 ]; then shift fi fi local tail_args="$*" execute_command_on_fdir_servers tail tail_remote_log "$tail_args $FDIR_LOG_FILE" execute_command_on_fstore_servers tail tail_remote_log "$tail_args $STORE_LOG_FILE" execute_command_on_fauth_servers tail tail_remote_log "$tail_args $AUTH_LOG_FILE" execute_command_on_fvote_servers tail tail_remote_log "$tail_args $VOTE_LOG_FILE" execute_command_on_fuseclient_servers tail tail_remote_log "$tail_args $FUSE_LOG_FILE" } #---11. Tail log section end---# #---12. Command execute section begin---# check_module_param $* check_node_param $* load_fcfs_settings load_cluster_groups load_installed_settings case "$shell_command" in 'setup' | 'install' | 'reinstall') check_remote_os_and_version if [[ " ${YUM_OS_ARRAY[@]} " =~ " ${cluster_host_osname} " ]]; then if [ -z $fastcfs_version ]; then echo "ERROR: Param fastcfs_version in $fcfs_settings_file cannot be empty." exit 1 fi load_dependency_settings $fastcfs_version fi ;; 'erase' | 'remove') if [[ " ${YUM_OS_ARRAY[@]} " =~ " ${cluster_host_osname} " ]]; then pkg_manage_tool="yum" elif [[ " ${APT_OS_ARRAY[@]} " =~ " ${cluster_host_osname} " ]]; then pkg_manage_tool="apt" fi ;; esac case "$shell_command" in 'setup' | 'config') check_if_client_share_servers ;; esac if [ -z $fastcfs_installed ]; then case "$shell_command" in 'reinstall' | 'erase' | 'remove' | 'config' | 'start' | 'restart' | 'stop' | 'tail' | 'status') echo "ERROR: The FastCFS softwares have not been installed, you must execute setup or install first." exit 1 ;; esac fi if [ -z $fastcfs_configed ]; then case "$shell_command" in 'start' | 'restart' | 'stop' | 'tail' | 'status') echo "ERROR: The FastCFS softwares have not been configed, you must execute config first." exit 1 ;; esac fi case "$shell_command" in 'setup') ("install_packages_by_$pkg_manage_tool";deploy_config_files;cluster_service_op restart) ;; 'install') ("install_packages_by_$pkg_manage_tool") ;; 'reinstall') if [ "$pkg_manage_tool" = "yum" ]; then ("reinstall_packages_by_$pkg_manage_tool") else echo "ERROR: The reinstall command can only be used for yum." exit 1 fi ;; 'erase' | 'remove') ("erase_packages_by_$pkg_manage_tool") ;; 'config') deploy_config_files ;; 'start') cluster_service_op start ;; 'restart') cluster_service_op restart ;; 'stop') cluster_service_op stop ;; 'tail') tail_log $* ;; 'status') cluster_service_op status ;; *) print_usage exit 1 ;; esac exit 0 #---12. Command execute section end---# ================================================ FILE: shell/fcfs_conf.settings ================================================ # FastCFS cluster ops settings be used with shell fcfs_conf.sh # This settings file must be placed at same path with fcfs_conf.sh # You can edit it, or download it from http://www.fastcfs.com/ops. fastcfs_version=5.3.0 vote_ips=10.0.1.11,10.0.1.12,10.0.1.13 auth_ips=10.0.1.11,10.0.1.12,10.0.1.13 fdir_ips=10.0.1.11,10.0.1.12,10.0.1.13 fstore_group_count=1 fstore_group_1=10.0.1.11,10.0.1.12,10.0.1.13 data_group_count=64 ================================================ FILE: shell/fcfs_conf.sh ================================================ #!/bin/bash # # fcfs_conf.sh is a ops tool for quickly generate FastCFS cluster config files. # It only relying on bash shell access to the local fcfs_conf.settings file. # It runs fully on your workstation, requiring no servers, databases, or anything like that. # # If you want to generate cluster config files with specify server ips quickly, # this is for you. # fcfs_settings_file="fcfs_conf.settings" # fcfs_tpl_file_server="http://www.fastken.cn/fastcfs/ops/dependency" #conf.2.3.0.tpl.tar.gz fcfs_tpl_file_server="http://www.fastken.cn/fastcfs/ops/config" fcfs_cache_path=".fcfs" LOCAL_CONF_PATH="conf" STORE_CONF_FILES=(client.conf server.conf cluster.conf storage.conf dbstore.conf) FDIR_CONF_FILES=(client.conf cluster.conf server.conf dbstore.conf) AUTH_CONF_FILES=(auth.conf client.conf cluster.conf server.conf session.conf) AUTH_KEYS_FILES=(session_validate.key) VOTE_CONF_FILES=(cluster.conf server.conf client.conf) FUSE_CONF_FILES=(fuse.conf) TRUNCATE_MARK_LINE="## Important:server group mark, don't modify this line." shell_name=$0 shell_command=$1 uname=$(uname) #---Usage info section begin---# print_usage() { # Print usage to console. echo "" echo "Usage: $shell_name [module]" echo "" echo "A self-sufficient config file generator shell for FastCFS cluster" echo "" echo "Commands:" echo " create Create config files for FastCFS cluster with specify ips in $fcfs_settings_file" echo " help Show the detail of commands and examples" echo "" } print_detail_usage() { # Print usage to console. print_usage echo "Modules:" echo " fvote FastCFS vote server" echo " fdir fastDIR server" echo " fstore faststore server" echo " fauth FastCFS auth server" echo " fuseclient FastCFS fuse client" echo "" echo "Node:" echo " You can specify a single module, or command will be executed on modules." echo "" echo "Examples:" echo " $shell_name create" echo " Create config files for all modules." echo "" echo " $shell_name create fdir" echo " Create config files for fastDIR cluster." echo "" echo "" } case "$shell_command" in 'create') ;; 'help') print_detail_usage exit 1 ;; *) print_usage exit 1 ;; esac #---Usage info section end---# #---Tool functions begin---# sed_replace() { sed_cmd=$1 filename=$2 if [ "$uname" = "FreeBSD" ] || [ "$uname" = "Darwin" ]; then sed -i "" "$sed_cmd" $filename else sed -i "$sed_cmd" $filename fi } split_to_array() { if ! [ -z $2 ]; then IFS=',' read -ra $2 <<< "$1" fi } create_path_not_exist() { local target_path=$1 if ! [ -d $target_path ]; then if ! mkdir -p $target_path; then echo "ERROR: Create path failed, $target_path!" exit 1 else echo "INFO: Created path successfully, $target_path." fi fi } #---Tool functions end---# #---Settings and cluster info section begin---# # Load cluster settings from file fcfs_conf.settings load_fcfs_settings() { if ! [ -f $fcfs_settings_file ]; then echo "ERROR: File $fcfs_settings_file does not exist" exit 1 else fcfs_settings=`sed -e 's/#.*//' -e '/^$/ d' $fcfs_settings_file` eval $fcfs_settings split_to_array $auth_ips auth_ip_array split_to_array $fdir_ips fdir_ip_array split_to_array $vote_ips vote_ip_array split_to_array $fstore_groups fstore_group_array fi } # Check config file templates in local cache path. check_conf_template() { conf_file_version=$1 if [ -z $conf_file_version ]; then echo "ERROR: Config file version cannot be empty." exit 1 fi local conf_tpl_dir="conf.$conf_file_version.tpl" config_file_template_path="$fcfs_cache_path/$conf_tpl_dir" if ! [ -d $config_file_template_path ]; then # Template path not exist in local cache path, # will create it and download templates from remote server match the version echo "WARN: Template path $config_file_template_path does not exist, getting it from remote server $fcfs_tpl_file_server." create_path_not_exist $fcfs_cache_path cd $fcfs_cache_path local template_tar_file="$conf_tpl_dir.tar.gz" remote_template_url="$fcfs_tpl_file_server/$template_tar_file" download_res=`curl -f -o $template_tar_file $remote_template_url` if ! [ -f $template_tar_file ]; then echo "ERROR: Cannot download file $template_tar_file from $remote_template_url." echo " Please make sure that remote template file exists and network is accessible." exit 1 fi # 解压模版文件 tar -xzvf $template_tar_file if ! [ -d $conf_tpl_dir ]; then echo "ERROR: dir $conf_tpl_dir still not exist after unzip file $template_tar_file." exit 1 fi cd - fi } # Get module name from command args, if not specify, will handle all modules. fvote_need_execute=0 fdir_need_execute=0 fstore_need_execute=0 fauth_need_execute=0 fuseclient_need_execute=0 has_module_param=1 check_module_param() { if [ $# -gt 1 ]; then module_name=$2 case "$module_name" in 'fvote') fvote_need_execute=1 ;; 'fdir') fdir_need_execute=1 ;; 'fstore') fstore_need_execute=1 ;; 'fauth') fauth_need_execute=1 ;; 'fuseclient') fuseclient_need_execute=1 ;; *) has_module_param=0 if ! [[ $module_name =~ ^- ]]; then echo "ERROR: Module name invalid, $module_name." echo " Allowed module names: fdir, fstore, fauth, fvote, fuseclient." exit 1 else fdir_need_execute=1 fstore_need_execute=1 fauth_need_execute=1 fvote_need_execute=1 fuseclient_need_execute=1 fi ;; esac else has_module_param=0 fdir_need_execute=1 fstore_need_execute=1 fauth_need_execute=1 fvote_need_execute=1 fuseclient_need_execute=1 fi } #---Settings and cluster info section end---# check_module_param $* load_fcfs_settings if [ -z $fastcfs_version ]; then echo "ERROR: Param fastcfs_version in $fcfs_settings_file cannot be empty." exit 1 fi # Check local template file, if not exist, download it from server. check_conf_template $fastcfs_version copy_config_from_template() { local config_file_array=$1 local src_path=$2 local dest_path=$3 for CONF_FILE in ${config_file_array[@]}; do local tmp_src_file=$src_path/$CONF_FILE if [ -f $tmp_src_file ]; then echo "INFO: Copy template file $CONF_FILE to $dest_path." cp $tmp_src_file $dest_path else echo "WARN: File $tmp_src_file does not exist." fi done } create_fdir_conf_files() { if [ ${#fdir_ip_array[@]} -gt 0 ]; then echo "INFO: Begin create fdir config files." local fdir_path="$LOCAL_CONF_PATH/fdir" local fdir_tpl_path="$config_file_template_path/fdir" create_path_not_exist $fdir_path copy_config_from_template "${FDIR_CONF_FILES[*]}" $fdir_tpl_path $fdir_path local fdir_cluster_file="$fdir_path/cluster.conf" if ! [ -f $fdir_cluster_file ]; then echo "ERROR: fdir cluster file not exist, $fdir_cluster_file, can't append server to it" exit 1 fi # Trancate cluster.conf with mark line sed_replace "/$TRUNCATE_MARK_LINE/,$ d" $fdir_cluster_file # Replace server ip let server_id=1 for fdir_server_ip in ${fdir_ip_array[@]}; do echo "[server-$server_id]" >> $fdir_cluster_file echo "host = $fdir_server_ip" >> $fdir_cluster_file echo "" >> $fdir_cluster_file let server_id=$server_id+1 done else echo "ERROR: Param fdir_ips in $fcfs_settings_file cannot be empty." exit 1 fi } create_fstore_conf_files() { if [ -z $data_group_count ]; then echo "ERROR: Param data_group_count in $fcfs_settings_file must not empty." exit 1 fi if [ -z $fstore_group_count ]; then echo "ERROR: Param fstore_group_count in $fcfs_settings_file must not empty." exit 1 fi let data_group_count_int=$data_group_count let fstore_group_count_int=$fstore_group_count if [ $fstore_group_count_int -gt 0 ]; then # Check params setting valid. if [ $data_group_count_int -lt $fstore_group_count_int ]; then echo "ERROR: Param data_group_count in $fcfs_settings_file must be great than fstore_group_count." exit 1 fi for (( i=1 ; i <= $fstore_group_count_int ; i++ )); do local fstore_group="fstore_group_$i" if [ -z ${!fstore_group} ]; then echo "ERROR: Param $fstore_group in $fcfs_settings_file cannot be empty." exit 1 fi done echo "INFO: Begin create fstore config files." local fstore_path="$LOCAL_CONF_PATH/fstore" local fstore_tpl_path="$config_file_template_path/fstore" create_path_not_exist $fstore_path copy_config_from_template "${STORE_CONF_FILES[*]}" $fstore_tpl_path $fstore_path local fstore_cluster_file="$fstore_path/cluster.conf" if ! [ -f $fstore_cluster_file ]; then echo "ERROR: fstore cluster file not exist, $fstore_cluster_file, can't append server to it" exit 1 fi # Trancate cluster.conf with mark line sed_replace "/$TRUNCATE_MARK_LINE/,$ d" $fstore_cluster_file # Replace server_group_count and data_group_count, and append server ips let data_groups_per_fstore_group=$data_group_count_int/$fstore_group_count_int let remainder_count=$data_group_count_int-$data_groups_per_fstore_group*$fstore_group_count_int let data_group_ids_start=1 let data_group_ids_end=$data_groups_per_fstore_group sed_replace "s#^server_group_count *=.*#server_group_count = $fstore_group_count_int#g" $fstore_cluster_file sed_replace "s#^data_group_count *=.*#data_group_count = $data_group_count_int#g" $fstore_cluster_file let server_id_start=1 let store_server_id=1 # for fstore_group in ${fstore_group_array[@]}; do for (( i=1 ; i <= $fstore_group_count_int ; i++ )); do local fstore_group="fstore_group_$i" split_to_array ${!fstore_group} fstore_ips_array let server_ids=${#fstore_ips_array[@]} if [ $server_ids -gt 0 ]; then # Append server group and server ids echo "[server-group-$i]" >> $fstore_cluster_file if [ $server_ids -eq 1 ]; then echo "server_ids = $server_id_start" >> $fstore_cluster_file else let server_id_end=$server_id_start+$server_ids-1 echo "server_ids = [$server_id_start, $server_id_end]" >> $fstore_cluster_file fi let server_id_start=$server_id_start+$server_ids let next_start_add_one=0 # Append data group ids if [ $remainder_count -gt 0 ]; then let data_group_ids_end=$data_group_ids_end+1 let remainder_count=$remainder_count-1 let next_start_add_one=1 fi echo "data_group_ids = [$data_group_ids_start, $data_group_ids_end]" >> $fstore_cluster_file echo "" >> $fstore_cluster_file let data_group_ids_start=$data_group_ids_end+1 let data_group_ids_end=$data_group_ids_end+$data_groups_per_fstore_group # Append server hosts for fstore_server_ip in ${fstore_ips_array[@]}; do echo "[server-$store_server_id]" >> $fstore_cluster_file echo "host = $fstore_server_ip" >> $fstore_cluster_file echo "" >> $fstore_cluster_file let store_server_id=$store_server_id+1 done else echo "ERROR: Param $fstore_group in $fcfs_settings_file cannot be empty." exit 1 fi done else echo "ERROR: Param fstore_group_count in $fcfs_settings_file must be great than 0." exit 1 fi } create_fauth_conf_files() { if [ ${#auth_ip_array[@]} -gt 0 ]; then echo "INFO: Begin create fauth config files." local auth_path="$LOCAL_CONF_PATH/auth" local auth_tpl_path="$config_file_template_path/auth" create_path_not_exist $auth_path copy_config_from_template "${AUTH_CONF_FILES[*]}" $auth_tpl_path $auth_path local auth_cluster_file="$auth_path/cluster.conf" if ! [ -f $auth_cluster_file ]; then echo "ERROR: auth cluster file not exist, $auth_cluster_file, can't append server to it" exit 1 fi # Trancate cluster.conf with mark line sed_replace "/$TRUNCATE_MARK_LINE/,$ d" $auth_cluster_file # Replace server ip let server_id=1 for auth_server_ip in ${auth_ip_array[@]}; do echo "[server-$server_id]" >> $auth_cluster_file echo "host = $auth_server_ip" >> $auth_cluster_file echo "" >> $auth_cluster_file let server_id=$server_id+1 done # Copy auth keys local auth_keys_path="$LOCAL_CONF_PATH/auth/keys" local auth_keys_tpl_path="$config_file_template_path/auth/keys" create_path_not_exist $auth_keys_path copy_config_from_template "${AUTH_KEYS_FILES[*]}" $auth_keys_tpl_path $auth_keys_path else echo "ERROR: Param auth_ips in $fcfs_settings_file cannot be empty." exit 1 fi } create_fvote_conf_files() { if [ ${#vote_ip_array[@]} -gt 0 ]; then echo "INFO: Begin create vote config files." local vote_path="$LOCAL_CONF_PATH/vote" local vote_tpl_path="$config_file_template_path/vote" create_path_not_exist $vote_path copy_config_from_template "${VOTE_CONF_FILES[*]}" $vote_tpl_path $vote_path local vote_cluster_file="$vote_path/cluster.conf" if ! [ -f $vote_cluster_file ]; then echo "ERROR: vote cluster file not exist, $vote_cluster_file, can't append server to it" exit 1 fi # Trancate cluster.conf with mark line sed_replace "/$TRUNCATE_MARK_LINE/,$ d" $vote_cluster_file # Replace server ip let server_id=1 for vote_server_ip in ${vote_ip_array[@]}; do echo "[server-$server_id]" >> $vote_cluster_file echo "host = $vote_server_ip" >> $vote_cluster_file echo "" >> $vote_cluster_file let server_id=$server_id+1 done else echo "ERROR: Param vote_ips in $fcfs_settings_file cannot be empty." exit 1 fi } create_fuseclient_conf_files() { echo "INFO: Begin create fuseclient config files." local fuseclient_path="$LOCAL_CONF_PATH/fcfs" local fuseclient_tpl_path="$config_file_template_path/fcfs" create_path_not_exist $fuseclient_path copy_config_from_template "${FUSE_CONF_FILES[*]}" $fuseclient_tpl_path $fuseclient_path } create_config_files() { if [ -d $LOCAL_CONF_PATH/fdir -a -d $LOCAL_CONF_PATH/fstore ]; then printf 'config files already exist, make sure to overwrite [y/N]: ' read var if ! [ "$var" = "y" -o "$var" = "Y" -o "$var" = "yes" -o "$var" = "YES" ]; then echo -e '\ncreating config files aborted!\n' exit 1 fi fi create_path_not_exist $LOCAL_CONF_PATH if [ $fdir_need_execute -eq 1 ]; then create_fdir_conf_files fi if [ $fstore_need_execute -eq 1 ]; then create_fstore_conf_files fi if [ $fauth_need_execute -eq 1 ]; then create_fauth_conf_files fi if [ $fvote_need_execute -eq 1 ]; then create_fvote_conf_files fi if [ $fuseclient_need_execute -eq 1 ]; then create_fuseclient_conf_files fi } case "$shell_command" in 'create') create_config_files ;; *) print_usage exit 1 ;; esac exit 0 ================================================ FILE: shell/template/dependency.2.0.1.settings ================================================ # FastCFS cluster's dependent libs version settings. # Ops shell fcfs.sh will use it to install appropriate version libs FastCFS depends. # This file download from http://www.fastcfs.com/ops/dependency.[FastCFS-version].settings. # Do not change libs and versions below. libfastcommon=1.0.50 libserverframe=1.1.7 fauth=2.0.1 fdir=2.0.1 fstore=2.0.1 ================================================ FILE: src/api/Makefile.in ================================================ .SUFFIXES: .c .o .lo COMPILE = $(CC) $(CFLAGS) INC_PATH = -I../common -I../include LIB_PATH = $(LIBS) -lfsclient -lfsapi -lfdirclient -lfastcommon -lserverframe -lfcfsauthclient TARGET_LIB = $(TARGET_PREFIX)/$(LIB_VERSION) FAST_SHARED_OBJS = ../common/fcfs_global.lo fcfs_api.lo fcfs_api_file.lo \ fcfs_api_util.lo fcfs_api_allocator.lo async_reporter.lo \ inode_htable.lo std/posix_api.lo std/fd_manager.lo \ std/papi.lo std/capi.lo FAST_STATIC_OBJS = ../common/fcfs_global.o fcfs_api.o fcfs_api_file.o \ fcfs_api_util.o fcfs_api_allocator.o async_reporter.o \ inode_htable.o std/posix_api.o std/fd_manager.o \ std/papi.o std/capi.o API_HEADER_FILES = ../common/fcfs_global.h fcfs_api.h fcfs_api_types.h \ fcfs_api_file.h fcfs_api_util.h fcfs_api_allocator.h \ async_reporter.h inode_htable.h STD_HEADER_FILES = std/posix_api.h std/api_types.h std/fd_manager.h \ std/papi.h std/capi.h ALL_OBJS = $(FAST_STATIC_OBJS) $(FAST_SHARED_OBJS) ALL_PRGS = SHARED_LIBS = libfcfsapi.so STATIC_LIBS = libfcfsapi.a ALL_LIBS = $(SHARED_LIBS) $(STATIC_LIBS) all: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) libfcfsapi.so: $(FAST_SHARED_OBJS) $(COMPILE) -o $@ -shared $(FAST_SHARED_OBJS) $(LIB_PATH) libfcfsapi.a: $(FAST_STATIC_OBJS) ar rcs $@ $(FAST_STATIC_OBJS) .o: $(COMPILE) -o $@ $< $(FAST_STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c: $(COMPILE) -o $@ $< $(FAST_STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c.o: $(COMPILE) -c -o $@ $< $(INC_PATH) .c.lo: $(COMPILE) -c -fPIC -o $@ $< $(INC_PATH) install: mkdir -p $(TARGET_LIB) mkdir -p $(TARGET_PREFIX)/lib mkdir -p $(TARGET_PREFIX)/include/fastcfs/api/std install -m 755 $(SHARED_LIBS) $(TARGET_LIB) install -m 644 $(API_HEADER_FILES) $(TARGET_PREFIX)/include/fastcfs/api/ install -m 644 $(STD_HEADER_FILES) $(TARGET_PREFIX)/include/fastcfs/api/std/ @BUILDROOT=$$(echo "$(TARGET_PREFIX)" | grep BUILDROOT); \ if [ -z "$$BUILDROOT" ] && [ "$(TARGET_LIB)" != "$(TARGET_PREFIX)/lib" ]; then ln -sf $(TARGET_LIB)/libfcfsapi.so $(TARGET_PREFIX)/lib/libfcfsapi.so; fi clean: rm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) ================================================ FILE: src/api/async_reporter.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "fastcommon/shared_func.h" #include "fastcommon/pthread_func.h" #include "sf/sf_global.h" #include "sf/sf_func.h" #include "async_reporter.h" AsyncReporterContext g_async_reporter_ctx; #define FCFS_API_CTX g_async_reporter_ctx.fcfs_api_ctx #define SORTED_EVENT_PTR_ARRAY g_async_reporter_ctx.event_ptr_arrays.sorted #define MERGED_EVENT_PTR_ARRAY g_async_reporter_ctx.event_ptr_arrays.merged #define NOTIFY_EVENT_COUNT g_async_reporter_ctx.event_ptr_arrays.notify_count static inline int batch_set_dentry_size(const int count) { int result; while (1) { result = fdir_client_batch_set_dentry_size(FCFS_API_CTX->contexts. fdir, &FCFS_API_CTX->ns, g_async_reporter_ctx.dsizes, count); if (result == 0 || result == ENOENT || result == EINVAL) { break; } sleep(1); } return result; } static int int_event_ptr_array(FCFSAPIAsyncReportEventPtrArray *event_ptr_array) { event_ptr_array->alloc = 1024; event_ptr_array->events = (FCFSAPIAsyncReportEvent **) fc_malloc(sizeof(FCFSAPIAsyncReportEvent *) * event_ptr_array->alloc); if (event_ptr_array->events == NULL) { return ENOMEM; } return 0; } static FCFSAPIAsyncReportEvent **realloc_event_ptrs( FCFSAPIAsyncReportEventPtrArray *event_ptr_array, const int count) { int alloc; FCFSAPIAsyncReportEvent **events; alloc = event_ptr_array->alloc * 2; events = (FCFSAPIAsyncReportEvent **)fc_malloc( sizeof(FCFSAPIAsyncReportEvent *) * alloc); if (events == NULL) { return NULL; } if (count > 0) { memcpy(events, event_ptr_array->events, sizeof(FCFSAPIAsyncReportEvent *) * count); } free(event_ptr_array->events); event_ptr_array->alloc = alloc; event_ptr_array->events = events; return events + count; } static int to_event_ptr_array(FCFSAPIAsyncReportEvent *head, FCFSAPIAsyncReportEventPtrArray *event_ptr_array) { FCFSAPIAsyncReportEvent **event; int result; int event_count; result = 0; event_count = 0; event = event_ptr_array->events; do { if (event_count >= event_ptr_array->alloc) { if ((event=realloc_event_ptrs(event_ptr_array, event_count)) == NULL) { result = ENOMEM; break; } } head->id = ++event_count; *event++ = head; head = head->next; } while (head != NULL); event_ptr_array->count = event_count; return result; } static int compare_event_ptr(const FCFSAPIAsyncReportEvent **t1, const FCFSAPIAsyncReportEvent **t2) { int sub; if ((sub=fc_compare_int64((*t1)->dsize.inode, (*t2)->dsize.inode)) != 0) { return sub; } return (int)(*t1)->id - (int)(*t2)->id; } static inline void merge_event(FCFSAPIAsyncReportEvent *dest, const FCFSAPIAsyncReportEvent *src) { if ((src->dsize.flags & (FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE | FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END)) != 0) { if ((dest->dsize.flags & (FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE | FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END)) == 0) { dest->dsize.file_size = src->dsize.file_size; } else if (dest->dsize.file_size < src->dsize.file_size) { dest->dsize.file_size = src->dsize.file_size; } } if ((src->dsize.flags & FDIR_DENTRY_FIELD_MODIFIED_FLAG_INC_ALLOC) != 0) { dest->dsize.inc_alloc += src->dsize.inc_alloc; } dest->dsize.flags |= src->dsize.flags; } static int merge_events(FCFSAPIAsyncReportEvent *head) { int result; //int merge_count; FCFSAPIAsyncReportEvent **ts; FCFSAPIAsyncReportEvent **send; FCFSAPIAsyncReportEvent **merge; FCFSAPIAsyncReportEvent *current; if ((result=to_event_ptr_array(head, &SORTED_EVENT_PTR_ARRAY)) != 0) { return result; } if (SORTED_EVENT_PTR_ARRAY.count > 1) { qsort(SORTED_EVENT_PTR_ARRAY.events, SORTED_EVENT_PTR_ARRAY.count, sizeof(FCFSAPIAsyncReportEvent *), (int (*)(const void *, const void *))compare_event_ptr); } NOTIFY_EVENT_COUNT = 0; send = SORTED_EVENT_PTR_ARRAY.events + SORTED_EVENT_PTR_ARRAY.count; ts = SORTED_EVENT_PTR_ARRAY.events; merge = MERGED_EVENT_PTR_ARRAY.events; while (ts < send) { if ((*ts)->type == fcfs_api_event_type_notify) { NOTIFY_EVENT_COUNT++; ts++; continue; } if (merge - MERGED_EVENT_PTR_ARRAY.events >= MERGED_EVENT_PTR_ARRAY.alloc) { if ((merge=realloc_event_ptrs(&MERGED_EVENT_PTR_ARRAY, merge - MERGED_EVENT_PTR_ARRAY.events)) == NULL) { result = ENOMEM; break; } } //merge_count = 1; current = *ts; *merge++ = *ts++; if (current->dsize.force) { continue; } if (ts == send) { break; } while ((ts < send) && (!(*ts)->dsize.force) && ((*ts)->dsize.inode == current->dsize.inode)) { if ((*ts)->type == fcfs_api_event_type_notify) { NOTIFY_EVENT_COUNT++; } else { //merge_count++; merge_event(current, *ts); } ts++; } /* logInfo("event oid: %"PRId64", merged count: %d", current->dsize.inode, merge_count); */ } MERGED_EVENT_PTR_ARRAY.count = merge - MERGED_EVENT_PTR_ARRAY.events; return 0; } static inline void notify_waiting_tasks(FCFSAPIAsyncReportEvent *event) { FCFSAPIWaitingTask *task; if (event->type == fcfs_api_event_type_report) { PTHREAD_MUTEX_LOCK(&event->inode_hentry->hentry.sharding->lock); } while (event->waitings.head != NULL) { task = event->waitings.head; event->waitings.head = event->waitings.head->next; fcfs_api_notify_waiting_task(task); } if (event->type == fcfs_api_event_type_report) { fc_list_del_init(&event->dlink); PTHREAD_MUTEX_UNLOCK(&event->inode_hentry->hentry.sharding->lock); } } static inline void notify_waiting_tasks_and_free_events( FCFSAPIAsyncReportEvent *head) { FCFSAPIAsyncReportEvent *event; do { event = head; head = head->next; notify_waiting_tasks(event); fast_mblock_free_object(event->allocator, event); } while (head != NULL); } static inline int deal_events(FCFSAPIAsyncReportEvent *head) { FCFSAPIAsyncReportEvent **event; FCFSAPIAsyncReportEvent **tend; FDIRSetDEntrySizeInfo *dsize; int result; int current_count; if ((result=merge_events(head)) != 0) { notify_waiting_tasks_and_free_events(head); return result; } dsize = g_async_reporter_ctx.dsizes; tend = MERGED_EVENT_PTR_ARRAY.events + MERGED_EVENT_PTR_ARRAY.count; for (event=MERGED_EVENT_PTR_ARRAY.events; eventdsize; if ((current_count=dsize - g_async_reporter_ctx.dsizes) == FDIR_BATCH_SET_MAX_DENTRY_COUNT) { batch_set_dentry_size(current_count); dsize = g_async_reporter_ctx.dsizes; } } if ((current_count=dsize - g_async_reporter_ctx.dsizes) > 0) { batch_set_dentry_size(current_count); } __sync_fetch_and_sub(&g_async_reporter_ctx.waiting_count, SORTED_EVENT_PTR_ARRAY.count - NOTIFY_EVENT_COUNT); notify_waiting_tasks_and_free_events(head); /* logInfo("total (input) event count: %d, report (output) count: %d, " "notify event count: %d", SORTED_EVENT_PTR_ARRAY.count, MERGED_EVENT_PTR_ARRAY.count, NOTIFY_EVENT_COUNT); */ return 0; } void async_reporter_terminate() { FCFSAPIAsyncReportEvent *head; int count; if (!g_async_reporter_ctx.fcfs_api_ctx->async_report.enabled) { return; } head = (FCFSAPIAsyncReportEvent *)fc_queue_try_pop_all( &g_async_reporter_ctx.queue); if (head != NULL) { count = deal_events(head); } else { count = 0; } logInfo("file: "__FILE__", line: %d, " "async_reporter_terminate, remain count: %d", __LINE__, count); } static inline void check_and_set_stage() { int old_stage; if (__sync_bool_compare_and_swap(&g_async_reporter_ctx.stage, ASYNC_REPORTER_STAGE_DEALING, ASYNC_REPORTER_STAGE_SLEEPING)) { fc_timedwait_ms(&g_async_reporter_ctx.lcp.lock, &g_async_reporter_ctx.lcp.cond, g_async_reporter_ctx. fcfs_api_ctx->async_report.interval_ms); if (__sync_bool_compare_and_swap( &g_async_reporter_ctx.stage, ASYNC_REPORTER_STAGE_SLEEPING, ASYNC_REPORTER_STAGE_DEALING)) { return; } } old_stage = __sync_fetch_and_add(&g_async_reporter_ctx.stage, 0); __sync_bool_compare_and_swap(&g_async_reporter_ctx.stage, old_stage, ASYNC_REPORTER_STAGE_DEALING); } static void *async_reporter_thread_func(void *arg) { FCFSAPIAsyncReportEvent *head; #ifdef OS_LINUX prctl(PR_SET_NAME, "dir-async-reporter"); #endif while (SF_G_CONTINUE_FLAG) { head = (FCFSAPIAsyncReportEvent *)fc_queue_pop_all( &g_async_reporter_ctx.queue); if (head != NULL) { deal_events(head); } if (g_async_reporter_ctx.fcfs_api_ctx->async_report.interval_ms > 0) { check_and_set_stage(); } } return NULL; } int async_reporter_init(FCFSAPIContext *fcfs_api_ctx) { int result; pthread_t tid; g_async_reporter_ctx.fcfs_api_ctx = fcfs_api_ctx; g_async_reporter_ctx.dsizes = (FDIRSetDEntrySizeInfo *) fc_malloc(sizeof(FDIRSetDEntrySizeInfo) * FDIR_BATCH_SET_MAX_DENTRY_COUNT); if (g_async_reporter_ctx.dsizes == NULL) { return ENOMEM; } if ((result=int_event_ptr_array(&SORTED_EVENT_PTR_ARRAY)) != 0) { return result; } if ((result=int_event_ptr_array(&MERGED_EVENT_PTR_ARRAY)) != 0) { return result; } if ((result=init_pthread_lock_cond_pair(&g_async_reporter_ctx.lcp)) != 0) { return result; } if ((result=fc_queue_init(&g_async_reporter_ctx.queue, (long) (&((FCFSAPIAsyncReportEvent *)NULL)->next))) != 0) { return result; } g_async_reporter_ctx.stage = ASYNC_REPORTER_STAGE_DEALING; return fc_create_thread(&tid, async_reporter_thread_func, NULL, SF_G_THREAD_STACK_SIZE); } ================================================ FILE: src/api/async_reporter.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_API_ASYNC_REPORTER_H #define _FCFS_API_ASYNC_REPORTER_H #include "fastcommon/fc_queue.h" #include "fastcommon/fc_atomic.h" #include "fcfs_api_types.h" #include "fcfs_api_allocator.h" #include "inode_htable.h" #define ASYNC_REPORTER_STAGE_DEALING 0 #define ASYNC_REPORTER_STAGE_SLEEPING 1 #define ASYNC_REPORTER_STAGE_KEEPING 2 typedef struct { FCFSAPIContext *fcfs_api_ctx; struct fc_queue queue; volatile int waiting_count; volatile int stage; pthread_lock_cond_pair_t lcp; //for timed wait FDIRSetDEntrySizeInfo *dsizes; struct { int notify_count; //notify event count FCFSAPIAsyncReportEventPtrArray sorted; //for sort FCFSAPIAsyncReportEventPtrArray merged; } event_ptr_arrays; } AsyncReporterContext; #ifdef __cplusplus extern "C" { #endif extern AsyncReporterContext g_async_reporter_ctx; int async_reporter_init(FCFSAPIContext *fcfs_api_ctx); void async_reporter_terminate(); static inline int async_reporter_push(const FDIRSetDEntrySizeInfo *dsize) { int result; FCFSAPIAsyncReportEvent *event; FCFSAPIAllocatorContext *allocator_ctx; allocator_ctx = fcfs_api_allocator_get(dsize->inode); event = (FCFSAPIAsyncReportEvent *)fast_mblock_alloc_object( &allocator_ctx->async_report_event); if (event == NULL) { return ENOMEM; } event->type = fcfs_api_event_type_report; event->dsize = *dsize; result = inode_htable_insert(event); __sync_fetch_and_add(&g_async_reporter_ctx.waiting_count, 1); fc_queue_push(&g_async_reporter_ctx.queue, event); return result; } static inline int async_reporter_push_notify(const uint64_t inode, FCFSAPIWaitingTask **waiting_task) { FCFSAPIAllocatorContext *allocator_ctx; FCFSAPIAsyncReportEvent *event; allocator_ctx = fcfs_api_allocator_get(inode); event = (FCFSAPIAsyncReportEvent *)fast_mblock_alloc_object( &allocator_ctx->async_report_event); if (event == NULL) { return ENOMEM; } *waiting_task = (FCFSAPIWaitingTask *)fast_mblock_alloc_object( &allocator_ctx->waiting_task); if (*waiting_task == NULL) { return ENOMEM; } (*waiting_task)->next = event->waitings.head; event->waitings.head = *waiting_task; event->type = fcfs_api_event_type_notify; event->dsize.inode = inode; event->dsize.flags = 0; event->dsize.force = false; fc_queue_push(&g_async_reporter_ctx.queue, event); return 0; } static inline void async_reporter_notify() { if (g_async_reporter_ctx.fcfs_api_ctx->async_report.interval_ms <= 0) { return; } switch (__sync_fetch_and_add(&g_async_reporter_ctx.stage, 0)) { case ASYNC_REPORTER_STAGE_DEALING: __sync_bool_compare_and_swap( &g_async_reporter_ctx.stage, ASYNC_REPORTER_STAGE_DEALING, ASYNC_REPORTER_STAGE_KEEPING); break; case ASYNC_REPORTER_STAGE_SLEEPING: pthread_cond_signal(&g_async_reporter_ctx.lcp.cond); break; case ASYNC_REPORTER_STAGE_KEEPING: //do nothing break; default: break; } } static inline int async_reporter_wait_all(const uint64_t inode) { FCFSAPIWaitingTask *waiting_task; int result; if (FC_ATOMIC_GET(g_async_reporter_ctx.waiting_count) == 0) { return 0; } if ((result=async_reporter_push_notify(inode, &waiting_task)) != 0) { return result; } async_reporter_notify(); fcfs_api_wait_report_done_and_release(waiting_task); return 0; } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/fcfs_api.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "fastcommon/common_define.h" #ifdef OS_LINUX #include #include #include #endif #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "sf/idempotency/client/client_channel.h" #include "sf/idempotency/client/receipt_handler.h" #include "async_reporter.h" #include "fcfs_api.h" #define FCFS_API_MIN_SHARED_ALLOCATOR_COUNT 1 #define FCFS_API_MAX_SHARED_ALLOCATOR_COUNT 1000 #define FCFS_API_DEFAULT_SHARED_ALLOCATOR_COUNT 11 #define FCFS_API_MIN_HASHTABLE_SHARDING_COUNT 1 #define FCFS_API_MAX_HASHTABLE_SHARDING_COUNT 10000 #define FCFS_API_DEFAULT_HASHTABLE_SHARDING_COUNT 17 #define FCFS_API_MIN_HASHTABLE_TOTAL_CAPACITY 10949 #define FCFS_API_MAX_HASHTABLE_TOTAL_CAPACITY 100000000 #define FCFS_API_DEFAULT_HASHTABLE_TOTAL_CAPACITY 1403641 #define FCFS_API_INI_IDEMPOTENCY_SECTION_NAME "idempotency" #define FCFS_API_IDEMPOTENCY_DEFAULT_WORK_THREADS 1 #ifndef FUSE_SUPER_MAGIC #define FUSE_SUPER_MAGIC 0x65735546 #endif FCFSAPIContext g_fcfs_api_ctx; static int fcfs_api_load_owner_config(IniFullContext *ini_ctx, FCFSAPIContext *ctx); static int opendir_session_alloc_init(void *element, void *args) { int result; FCFSAPIOpendirSession *session; session = (FCFSAPIOpendirSession *)element; if ((result=fdir_client_dentry_array_init(&session->array)) != 0) { return result; } if ((result=fast_buffer_init1(&session->buffer, 64 * 1024)) != 0) { return result; } return 0; } int fcfs_api_client_session_create(FCFSAPIContext *ctx, const bool publish) { FCFSAuthClientFullContext *auth; if (ctx->contexts.fdir->auth.enabled) { auth = &ctx->contexts.fdir->auth; } else if (ctx->contexts.fsapi->fs->auth.enabled) { auth = &ctx->contexts.fsapi->fs->auth; } else { return 0; } return fcfs_auth_client_session_create_ex(auth, &ctx->ns, publish); } static inline bool fcfs_api_rdma_enabled(FCFSAPIContext *ctx) { FCServerGroupInfo *server_group; server_group = fc_server_get_group_by_index( &ctx->contexts.fdir->cluster.server_cfg, ctx->contexts.fdir->cluster.service_group_index); if (server_group->comm_type != fc_comm_type_sock) { return true; } server_group = fc_server_get_group_by_index( &FS_CLUSTER_SERVER_CFG(ctx->contexts.fsapi->fs), FS_CFG_SERVICE_INDEX(ctx->contexts.fsapi->fs)); return (server_group->comm_type != fc_comm_type_sock); } static int fcfs_api_common_init(FCFSAPIContext *ctx, FDIRClientContext *fdir, FSAPIContext *fsapi, const char *ns, IniFullContext *ini_ctx, const char *fdir_section_name, const char *fs_section_name, const bool need_lock, const bool persist_additional_gids) { int64_t element_limit = 1000 * 1000; const int64_t min_ttl_sec = 600; const int64_t max_ttl_sec = 86400; int result; ctx->persist_additional_gids = persist_additional_gids; ctx->use_sys_lock_for_append = iniGetBoolValue(fdir_section_name, "use_sys_lock_for_append", ini_ctx->context, false); ctx->async_report.enabled = iniGetBoolValue(fdir_section_name, "async_report_enabled", ini_ctx->context, true); ctx->async_report.interval_ms = iniGetIntValue(fdir_section_name, "async_report_interval_ms", ini_ctx->context, 10); ini_ctx->section_name = fdir_section_name; ctx->async_report.shared_allocator_count = iniGetIntCorrectValueEx( ini_ctx, "shared_allocator_count", FCFS_API_DEFAULT_SHARED_ALLOCATOR_COUNT, FCFS_API_MIN_SHARED_ALLOCATOR_COUNT, FCFS_API_MAX_SHARED_ALLOCATOR_COUNT, true); ctx->async_report.hashtable_sharding_count = iniGetIntCorrectValue( ini_ctx, "hashtable_sharding_count", FCFS_API_DEFAULT_HASHTABLE_SHARDING_COUNT, FCFS_API_MIN_HASHTABLE_SHARDING_COUNT, FCFS_API_MAX_HASHTABLE_SHARDING_COUNT); ctx->async_report.hashtable_total_capacity = iniGetInt64CorrectValue( ini_ctx, "hashtable_total_capacity", FCFS_API_DEFAULT_HASHTABLE_TOTAL_CAPACITY, FCFS_API_MIN_HASHTABLE_TOTAL_CAPACITY, FCFS_API_MAX_HASHTABLE_TOTAL_CAPACITY); if (ctx->async_report.enabled) { if ((result=fcfs_api_allocator_init(ctx)) != 0) { return result; } if ((result=inode_htable_init(ctx->async_report. hashtable_sharding_count, ctx->async_report.hashtable_total_capacity, ctx->async_report.shared_allocator_count, element_limit, min_ttl_sec, max_ttl_sec)) != 0) { return result; } } ini_ctx->section_name = fs_section_name; if ((result=fs_api_init_ex(fsapi, ini_ctx, fcfs_api_file_write_done_callback, sizeof(FCFSAPIWriteDoneCallbackExtraData))) != 0) { return result; } if ((result=fast_mblock_init_ex1(&ctx->opendir_session_pool, "opendir_session", sizeof(FCFSAPIOpendirSession), 64, 0, opendir_session_alloc_init, NULL, need_lock)) != 0) { return result; } fcfs_api_set_contexts_ex1(ctx, fdir, fsapi, ns); if ((ctx->rdma.enabled=fcfs_api_rdma_enabled(ctx))) { ctx->rdma.busy_polling = iniGetBoolValue(NULL, "busy_polling", ini_ctx->context, false); G_RDMA_CONNECTION_CALLBACKS.set_busy_polling(ctx->rdma.busy_polling); } else { ctx->rdma.busy_polling = false; } ini_ctx->section_name = fdir_section_name; return fcfs_api_load_owner_config(ini_ctx, ctx); } int fcfs_api_init_ex1(FCFSAPIContext *ctx, FDIRClientContext *fdir, FSAPIContext *fsapi, const char *ns, IniFullContext *ini_ctx, const char *fdir_section_name, const char *fs_section_name, const SFConnectionManager *fdir_conn_manager, const SFConnectionManager *fs_conn_manager, const bool need_lock, const bool persist_additional_gids) { const bool bg_thread_enabled = true; int result; ini_ctx->section_name = fdir_section_name; if ((result=fdir_client_init_ex1(fdir, &g_fcfs_auth_client_vars. client_ctx, ini_ctx, fdir_conn_manager)) != 0) { return result; } ini_ctx->section_name = fs_section_name; if ((result=fs_client_init_ex1(fsapi->fs, &g_fcfs_auth_client_vars. client_ctx, ini_ctx, fs_conn_manager, bg_thread_enabled)) != 0) { return result; } return fcfs_api_common_init(ctx, fdir, fsapi, ns, ini_ctx, fdir_section_name, fs_section_name, need_lock, persist_additional_gids); } int fcfs_api_init_ex(FCFSAPIContext *ctx, const char *ns, const char *config_filename, const char *fdir_section_name, const char *fs_section_name) { int result; IniContext iniContext; IniFullContext ini_ctx; if ((result=iniLoadFromFile(config_filename, &iniContext)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, error no: %d", __LINE__, config_filename, result); return result; } g_fs_api_ctx.fs = &g_fs_client_vars.client_ctx; FAST_INI_SET_FULL_CTX_EX(ini_ctx, config_filename, fdir_section_name, &iniContext); result = fcfs_api_init_ex1(ctx, &g_fdir_client_vars.client_ctx, &g_fs_api_ctx, ns, &ini_ctx, fdir_section_name, fs_section_name, NULL, NULL, false, true); iniFreeContext(&iniContext); return result; } int fcfs_api_pooled_init_ex(FCFSAPIContext *ctx, const char *ns, const char *config_filename, const char *fdir_section_name, const char *fs_section_name, const bool need_lock, const bool persist_additional_gids) { int result; IniContext iniContext; IniFullContext ini_ctx; if ((result=iniLoadFromFile(config_filename, &iniContext)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, error no: %d", __LINE__, config_filename, result); return result; } FAST_INI_SET_FULL_CTX_EX(ini_ctx, config_filename, fdir_section_name, &iniContext); result = fcfs_api_pooled_init_ex1(ctx, ns, &ini_ctx, fdir_section_name, fs_section_name, need_lock, persist_additional_gids); iniFreeContext(&iniContext); return result; } int fcfs_api_init_ex2(FCFSAPIContext *ctx, FDIRClientContext *fdir, FSAPIContext *fsapi, const char *ns, IniFullContext *ini_ctx, const char *fdir_section_name, const char *fs_section_name, const FDIRClientConnManagerType conn_manager_type, const SFConnectionManager *fs_conn_manager, const bool need_lock, const bool persist_additional_gids) { const bool bg_thread_enabled = true; const int max_count_per_entry = 0; const int max_idle_time = 3600; int result; ini_ctx->section_name = fdir_section_name; if (conn_manager_type == conn_manager_type_simple) { result = fdir_client_simple_init_ex1(fdir, &g_fcfs_auth_client_vars.client_ctx, ini_ctx); } else if (conn_manager_type == conn_manager_type_pooled) { result = fdir_client_pooled_init_ex1(fdir, &g_fcfs_auth_client_vars.client_ctx, ini_ctx, max_count_per_entry, max_idle_time, bg_thread_enabled); } else { result = EINVAL; } if (result != 0) { return result; } ini_ctx->section_name = fs_section_name; if ((result=fs_client_init_ex1(fsapi->fs, &g_fcfs_auth_client_vars. client_ctx, ini_ctx, fs_conn_manager, bg_thread_enabled)) != 0) { return result; } return fcfs_api_common_init(ctx, fdir, fsapi, ns, ini_ctx, fdir_section_name, fs_section_name, need_lock, persist_additional_gids); } void fcfs_api_destroy_ex(FCFSAPIContext *ctx) { if (ctx->contexts.fdir != NULL) { fdir_client_destroy_ex(ctx->contexts.fdir); ctx->contexts.fdir = NULL; } if (ctx->contexts.fsapi != NULL) { if (ctx->contexts.fsapi->fs != NULL) { fs_client_destroy_ex(ctx->contexts.fsapi->fs); ctx->contexts.fsapi->fs = NULL; } fs_api_destroy_ex(ctx->contexts.fsapi); ctx->contexts.fsapi = NULL; } } static int check_create_root_path(FCFSAPIContext *ctx) { int result; int64_t inode; FDIRClientOperFnamePair fname; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, ctx->owner.oper, "/"); if ((result=fcfs_api_lookup_inode_by_fullname_ex(ctx, &fname, LOG_DEBUG, &inode)) != 0) { if (result == ENOENT) { FDIRDEntryInfo dentry; FCFS_API_SET_OPERATOR(fname.oper, ctx->owner, geteuid(), getegid()); if ((result=fdir_client_create_dentry(ctx->contexts.fdir, &fname, ACCESSPERMS | S_IFDIR, &dentry)) == EEXIST) { /* check again */ result = fcfs_api_lookup_inode_by_fullname_ex(ctx, &fname, LOG_DEBUG, &inode); } } } return result; } int fcfs_api_start_ex(FCFSAPIContext *ctx) { int result; if ((result=fs_api_start_ex(ctx->contexts.fsapi)) != 0) { return result; } if ((result=sf_connection_manager_start(&ctx-> contexts.fdir->cm)) != 0) { return result; } if ((result=sf_connection_manager_start(&ctx-> contexts.fsapi->fs->cm)) != 0) { return result; } if (ctx->contexts.fdir->idempotency_enabled || ctx->contexts.fsapi->fs->idempotency_enabled) { FCServerGroupInfo *server_group; FCAddressPtrArray *address_array; FCServerInfo *first_server; address_array = NULL; if (ctx->contexts.fdir->idempotency_enabled) { server_group = fc_server_get_group_by_index( &ctx->contexts.fdir->cluster.server_cfg, ctx->contexts.fdir->cluster.service_group_index); if (server_group->comm_type != fc_comm_type_sock) { first_server = FC_SID_SERVERS(ctx->contexts. fdir->cluster.server_cfg); address_array = &first_server->group_addrs[ctx->contexts. fdir->cluster.service_group_index].address_array; } } if (ctx->contexts.fsapi->fs->idempotency_enabled && address_array == NULL) { server_group = fc_server_get_group_by_index( &FS_CLUSTER_SERVER_CFG(ctx->contexts.fsapi->fs), FS_CFG_SERVICE_INDEX(ctx->contexts.fsapi->fs)); if (server_group->comm_type != fc_comm_type_sock) { first_server = FC_SID_SERVERS(FS_CLUSTER_SERVER_CFG( ctx->contexts.fsapi->fs)); address_array = &first_server->group_addrs[ FS_CFG_SERVICE_INDEX(ctx->contexts.fsapi->fs)]. address_array; } } if ((result=receipt_handler_init(address_array)) != 0) { return result; } } if ((result=check_create_root_path(ctx)) != 0) { return result; } if ((result=fdir_client_init_node_id(ctx->contexts.fdir)) != 0) { return result; } if (ctx->async_report.enabled) { return async_reporter_init(ctx); } else { return 0; } } void fcfs_api_terminate_ex(FCFSAPIContext *ctx) { fs_api_terminate_ex(ctx->contexts.fsapi); if (ctx->async_report.enabled) { async_reporter_terminate(); } } void fcfs_api_async_report_config_to_string_ex(FCFSAPIContext *ctx, char *output, const int size) { int len; len = snprintf(output, size, "use_sys_lock_for_append: %d, " "async_report { enabled: %d", ctx->use_sys_lock_for_append, ctx->async_report.enabled); if (ctx->async_report.enabled) { len += snprintf(output + len, size - len, ", " "async_report_interval_ms: %d, " "shared_allocator_count: %d, " "hashtable_sharding_count: %d, " "hashtable_total_capacity: %"PRId64, ctx->async_report.interval_ms, ctx->async_report.shared_allocator_count, ctx->async_report.hashtable_sharding_count, ctx->async_report.hashtable_total_capacity); if (len > size) { len = size; } } snprintf(output + len, size - len, " } "); } static int fcfs_api_setgroups(FCFSAPIOwnerInfo *owner_info, const gid_t *groups, const int count) { int index; const gid_t *group; const gid_t *end; char *buff; if (count == 0 || (count == 1 && groups[0] == owner_info->oper.gid)) { owner_info->oper.additional_gids.count = 0; owner_info->oper.additional_gids.list = NULL; return 0; } owner_info->oper.additional_gids.count = count; owner_info->oper.additional_gids.list = fc_malloc( FDIR_ADDITIONAL_GROUP_BYTES(owner_info->oper)); if (owner_info->oper.additional_gids.list == NULL) { return ENOMEM; } index = 0; end = groups + count; for (group=groups; groupoper.gid) { buff = (char *)(owner_info->oper.additional_gids. list + 4 * index++); int2buff(*group, buff); } } owner_info->oper.additional_gids.count = index; return 0; } static int fcfs_api_getgroups(FCFSAPIOwnerInfo *owner_info) { int result; int count; gid_t groups[FDIR_MAX_USER_GROUP_COUNT]; count = getgroups(FDIR_MAX_USER_GROUP_COUNT, groups); if (count < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "getgroups fail, errno: %d, error info: %s", __LINE__, result, STRERROR(result)); return result; } return fcfs_api_setgroups(owner_info, groups, count); } static int fcfs_api_getgrouplist(FCFSAPIOwnerInfo *owner_info) { int result; int count; struct passwd *user; #ifdef OS_FREEBSD int i; int groups[FDIR_MAX_USER_GROUP_COUNT]; gid_t _groups[FDIR_MAX_USER_GROUP_COUNT]; #else gid_t groups[FDIR_MAX_USER_GROUP_COUNT]; #endif gid_t *ptr; errno = ENOENT; if ((user=getpwuid(owner_info->oper.uid)) == NULL) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "getpwuid fail, errno: %d, error info: %s", __LINE__, result, STRERROR(result)); return result; } count = FDIR_MAX_USER_GROUP_COUNT; if (getgrouplist(user->pw_name, owner_info-> oper.gid, groups, &count) < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "getgroups fail, errno: %d, error info: %s", __LINE__, result, STRERROR(result)); return result; } #ifdef OS_FREEBSD for (i=0; iowner.type == fcfs_api_owner_type_fixed) { return EINVAL; } ctx->owner.oper.uid = geteuid(); ctx->owner.oper.gid = getegid(); return fcfs_api_getgroups(&ctx->owner); } static int fcfs_api_load_owner_config(IniFullContext *ini_ctx, FCFSAPIContext *ctx) { int result; char *owner_type; char *owner_user; char *owner_group; struct group *group; struct passwd *user; owner_type = iniGetStrValue(ini_ctx->section_name, "owner_type", ini_ctx->context); if (owner_type == NULL || *owner_type == '\0') { ctx->owner.type = fcfs_api_owner_type_caller; } else if (strcasecmp(owner_type, FCFS_API_OWNER_TYPE_CALLER_STR) == 0) { ctx->owner.type = fcfs_api_owner_type_caller; } else if (strcasecmp(owner_type, FCFS_API_OWNER_TYPE_FIXED_STR) == 0) { ctx->owner.type = fcfs_api_owner_type_fixed; } else { ctx->owner.type = fcfs_api_owner_type_caller; } if (ctx->owner.type == fcfs_api_owner_type_caller) { return fcfs_api_set_owner(ctx); } owner_user = iniGetStrValue(ini_ctx->section_name, "owner_user", ini_ctx->context); if (owner_user == NULL || *owner_user == '\0') { ctx->owner.oper.uid = geteuid(); } else { user = getpwnam(owner_user); if (user == NULL) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "getpwnam %s fail, errno: %d, error info: %s", __LINE__, owner_user, result, STRERROR(result)); return result; } ctx->owner.oper.uid = user->pw_uid; } owner_group = iniGetStrValue(ini_ctx->section_name, "owner_group", ini_ctx->context); if (owner_group == NULL || *owner_group == '\0') { ctx->owner.oper.gid = getegid(); } else { group = getgrnam(owner_group); if (group == NULL) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "getgrnam %s fail, errno: %d, error info: %s", __LINE__, owner_group, result, STRERROR(result)); return result; } ctx->owner.oper.gid = group->gr_gid; } if (ctx->owner.oper.uid == geteuid() && ctx->owner.oper.gid == getegid()) { return fcfs_api_getgroups(&ctx->owner); } else { return fcfs_api_getgrouplist(&ctx->owner); } } int fcfs_api_load_idempotency_config_ex(const char *log_prefix_name, IniFullContext *ini_ctx, const char *fdir_section_name, const char *fs_section_name) { #define MIN_THREAD_STACK_SIZE (320 * 1024) const int task_buffer_extra_size = 0; const bool need_set_run_by = true; int fixed_buffer_size; int result; SFContextIniConfig config; FCServerGroupInfo *server_group; FCCommunicationType comm_type; ini_ctx->section_name = FCFS_API_INI_IDEMPOTENCY_SECTION_NAME; if ((result=client_channel_init(ini_ctx)) != 0) { return result; } g_sf_context.is_client = true; g_fdir_client_vars.client_ctx.idempotency_enabled = iniGetBoolValue(fdir_section_name, "idempotency_enabled", ini_ctx->context, g_idempotency_client_cfg.enabled); g_fs_client_vars.client_ctx.idempotency_enabled = iniGetBoolValue(fs_section_name, "idempotency_enabled", ini_ctx->context, g_idempotency_client_cfg.enabled); fixed_buffer_size = 0; comm_type = fc_comm_type_sock; if (g_fdir_client_vars.client_ctx.idempotency_enabled) { server_group = fc_server_get_group_by_index( &g_fdir_client_vars.client_ctx.cluster.server_cfg, g_fdir_client_vars.client_ctx.cluster.service_group_index); if (comm_type != server_group->comm_type) { comm_type = server_group->comm_type; fixed_buffer_size = g_fdir_client_vars.client_ctx. cluster.server_cfg.buffer_size; } } if (g_fs_client_vars.client_ctx.idempotency_enabled) { server_group = fc_server_get_group_by_index( &FS_CLUSTER_SERVER_CFG(&g_fs_client_vars.client_ctx), FS_CFG_SERVICE_INDEX(&g_fs_client_vars.client_ctx)); if (comm_type != server_group->comm_type) { comm_type = g_fdir_client_vars.client_ctx.idempotency_enabled ? fc_comm_type_both : server_group->comm_type; } if (fixed_buffer_size == 0) { fixed_buffer_size = FS_CLUSTER_SERVER_CFG(&g_fs_client_vars. client_ctx).buffer_size; } else if (FS_CLUSTER_SERVER_CFG(&g_fs_client_vars. client_ctx).buffer_size > 0) { fixed_buffer_size = FC_MIN(fixed_buffer_size, FS_CLUSTER_SERVER_CFG(&g_fs_client_vars. client_ctx).buffer_size); } } SF_SET_CONTEXT_INI_CONFIG(config, comm_type, ini_ctx->filename, ini_ctx->context, FCFS_API_INI_IDEMPOTENCY_SECTION_NAME, 0, 0, FCFS_API_IDEMPOTENCY_DEFAULT_WORK_THREADS); if ((result=sf_load_config_ex(log_prefix_name, &config, fixed_buffer_size, task_buffer_extra_size, need_set_run_by)) != 0) { return result; } if (SF_G_THREAD_STACK_SIZE < MIN_THREAD_STACK_SIZE) { logWarning("file: "__FILE__", line: %d, " "config file: %s, thread_stack_size: %d is too small, " "set to %d", __LINE__, ini_ctx->filename, SF_G_THREAD_STACK_SIZE, MIN_THREAD_STACK_SIZE); SF_G_THREAD_STACK_SIZE = MIN_THREAD_STACK_SIZE; } return 0; } static int load_mountpoint(IniFullContext *ini_ctx, string_t *mountpoint, const bool fuse_check) { struct statfs buf; int result; int ret; if (mountpoint->str == NULL) { mountpoint->str = iniGetStrValueEx(ini_ctx->section_name, "mountpoint", ini_ctx->context, true); if (mountpoint->str == NULL || *mountpoint->str == '\0') { logError("file: "__FILE__", line: %d, " "config file: %s, section: %s, item: mountpoint " "not exist or is empty", __LINE__, ini_ctx->filename, ini_ctx->section_name); return ENOENT; } mountpoint->len = strlen(mountpoint->str); } if (*mountpoint->str != '/') { logError("file: "__FILE__", line: %d, " "config file: %s, mountpoint: %s must start with \"/\"", __LINE__, ini_ctx->filename, mountpoint->str); return ENOENT; } while (mountpoint->len > 0 && mountpoint-> str[mountpoint->len - 1] == '/') { mountpoint->len--; } if (fuse_check && !fileExists(mountpoint->str)) { result = errno != 0 ? errno : ENOENT; if (result == ENOTCONN) { #ifdef OS_LINUX ret = umount2(mountpoint->str, MNT_FORCE); #else ret = unmount(mountpoint->str, 0); #endif if (ret == 0) { result = 0; } else { result = errno != 0 ? errno : EBUSY; if (result == EPERM) { logError("file: "__FILE__", line: %d, " "unmount %s fail, you should run " "\"sudo umount -f %s\" manually", __LINE__, mountpoint->str, mountpoint->str); } else { logError("file: "__FILE__", line: %d, " "unmount %s fail, errno: %d, error info: %s", __LINE__, mountpoint->str, result, STRERROR(result)); } return result; } } else if (result != 0) { logError("file: "__FILE__", line: %d, " "mountpoint: %s can't be accessed, errno: %d, " "error info: %s", __LINE__, mountpoint->str, result, STRERROR(result)); return result; } } if (!isDir(mountpoint->str)) { logError("file: "__FILE__", line: %d, " "mountpoint: %s is not a directory!", __LINE__, mountpoint->str); return ENOTDIR; } if (fuse_check) { if (statfs(mountpoint->str, &buf) != 0) { logError("file: "__FILE__", line: %d, " "statfs mountpoint: %s fail, error info: %s", __LINE__, mountpoint->str, STRERROR(errno)); return errno != 0 ? errno : ENOENT; } if ((buf.f_type & FUSE_SUPER_MAGIC) == FUSE_SUPER_MAGIC) { logError("file: "__FILE__", line: %d, " "mountpoint: %s already mounted by FUSE", __LINE__, mountpoint->str); return EEXIST; } } return 0; } int fcfs_api_check_mountpoint(const char *config_filename, const string_t *mountpoint) { int result; string_t base_path; FC_SET_STRING(base_path, SF_G_BASE_PATH_STR); if (fc_path_contains(&base_path, mountpoint, &result)) { logError("file: "__FILE__", line: %d, " "config file: %s, base path: %s contains mountpoint: %.*s, " "this case is not allowed", __LINE__, config_filename, SF_G_BASE_PATH_STR, mountpoint->len, mountpoint->str); return EINVAL; } else if (result != 0) { logError("file: "__FILE__", line: %d, " "config file: %s, base path: %s or mountpoint: %.*s " "is invalid", __LINE__, config_filename, SF_G_BASE_PATH_STR, mountpoint->len, mountpoint->str); } return result; } int fcfs_api_load_ns_mountpoint(IniFullContext *ini_ctx, const char *fdir_section_name, FCFSAPINSMountpointHolder *nsmp, string_t *mountpoint, const bool fuse_check) { string_t ns; int result; if ((result=load_mountpoint(ini_ctx, mountpoint, fuse_check)) != 0) { return result; } if (nsmp->ns == NULL) { ns.str = iniGetStrValue(fdir_section_name, "namespace", ini_ctx->context); if (ns.str == NULL || *ns.str == '\0') { logError("file: "__FILE__", line: %d, " "config file: %s, section: %s, item: namespace " "not exist or is empty", __LINE__, ini_ctx-> filename, fdir_section_name); return ENOENT; } } else { ns.str = nsmp->ns; } ns.len = strlen(ns.str); nsmp->ns = fc_malloc(ns.len + mountpoint->len + 2); if (nsmp->ns == NULL) { return ENOMEM; } memcpy(nsmp->ns, ns.str, ns.len + 1); nsmp->mountpoint = nsmp->ns + ns.len + 1; memcpy(nsmp->mountpoint, mountpoint->str, mountpoint->len); *(nsmp->mountpoint + mountpoint->len) = '\0'; mountpoint->str = nsmp->mountpoint; return 0; } void fcfs_api_free_ns_mountpoint(FCFSAPINSMountpointHolder *nsmp) { if (nsmp->ns != NULL) { free(nsmp->ns); nsmp->ns = NULL; } } void fcfs_api_log_client_common_configs(FCFSAPIContext *ctx, const char *fdir_section_name, const char *fs_section_name, BufferInfo *sf_idempotency_config, char *owner_config) { char auth_config[512]; char fsapi_config[1024]; char async_report_config[512]; int len; if (ctx->contexts.fdir->idempotency_enabled || ctx->contexts.fsapi->fs->idempotency_enabled) { len = snprintf(sf_idempotency_config->buff, sf_idempotency_config->alloc_size, "%s idempotency_enabled=%d, " "%s idempotency_enabled=%d, ", fdir_section_name, ctx->contexts. fdir->idempotency_enabled, fs_section_name, ctx->contexts. fsapi->fs->idempotency_enabled); idempotency_client_channel_config_to_string_ex( sf_idempotency_config->buff + len, sf_idempotency_config->alloc_size - len, true); sf_idempotency_config->length = strlen(sf_idempotency_config->buff); } else { *sf_idempotency_config->buff = '\0'; sf_idempotency_config->length = 0; } len = sprintf(owner_config, "owner_type: %s", fcfs_api_get_owner_type_caption(ctx->owner.type)); if (ctx->owner.type == fcfs_api_owner_type_fixed) { struct passwd *user; struct group *group; user = getpwuid(ctx->owner.oper.uid); group = getgrgid(ctx->owner.oper.gid); sprintf(owner_config + len, ", owner_user: %s, owner_group: %s", user->pw_name, group->gr_name); } fcfs_api_async_report_config_to_string_ex(ctx, async_report_config, sizeof(async_report_config)); fcfs_auth_config_to_string(&ctx->contexts.fdir->auth, auth_config, sizeof(auth_config)); snprintf(async_report_config + strlen(async_report_config), sizeof(async_report_config) - strlen(async_report_config), ", %s", auth_config); fdir_client_log_config_ex(ctx->contexts.fdir, async_report_config, false); fs_api_config_to_string(fsapi_config, sizeof(fsapi_config)); fcfs_auth_config_to_string(&ctx->contexts.fsapi->fs->auth, auth_config, sizeof(auth_config)); snprintf(fsapi_config + strlen(fsapi_config), sizeof(fsapi_config) - strlen(fsapi_config), ", %s", auth_config); fs_client_log_config_ex(ctx->contexts.fsapi->fs, fsapi_config, false); } ================================================ FILE: src/api/fcfs_api.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_API_H #define _FCFS_API_H #include "fastcommon/shared_func.h" #include "fcfs_api_types.h" #include "fcfs_api_file.h" #include "fcfs_api_util.h" #define FCFS_API_DEFAULT_FASTDIR_SECTION_NAME "FastDIR" #define FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME "FastStore" #define FCFS_API_OWNER_TYPE_CALLER_STR "caller" #define FCFS_API_OWNER_TYPE_FIXED_STR "fixed" #define fcfs_api_set_contexts(ns) fcfs_api_set_contexts_ex(&g_fcfs_api_ctx, ns) #define fcfs_api_init(ns, config_filename) \ fcfs_api_init_ex(&g_fcfs_api_ctx, ns, config_filename, \ FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, \ FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME) #define fcfs_api_pooled_init(ns, config_filename) \ fcfs_api_pooled_init_ex(&g_fcfs_api_ctx, ns, config_filename, \ FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, \ FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME, \ true, true) #define fcfs_api_pooled_init1(ns, ini_ctx) \ fcfs_api_pooled_init_ex1(&g_fcfs_api_ctx, ns, ini_ctx, \ FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, \ FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME, \ true, true) #define fcfs_api_destroy() fcfs_api_destroy_ex(&g_fcfs_api_ctx) #define fcfs_api_start() fcfs_api_start_ex(&g_fcfs_api_ctx) #define fcfs_api_terminate() fcfs_api_terminate_ex(&g_fcfs_api_ctx) #define fcfs_api_async_report_config_to_string(output, size) \ fcfs_api_async_report_config_to_string_ex(&g_fcfs_api_ctx, output, size) #ifdef __cplusplus extern "C" { #endif static inline void fcfs_api_set_contexts_ex1(FCFSAPIContext *ctx, FDIRClientContext *fdir, FSAPIContext *fsapi, const char *ns) { ctx->contexts.fdir = fdir; ctx->contexts.fsapi = fsapi; ctx->ns.str = ctx->ns_holder; ctx->ns.len = fc_safe_strcpy(ctx->ns_holder, ns); } static inline void fcfs_api_set_contexts_ex(FCFSAPIContext *ctx, const char *ns) { g_fs_api_ctx.fs = &g_fs_client_vars.client_ctx; return fcfs_api_set_contexts_ex1(ctx, &g_fdir_client_vars. client_ctx, &g_fs_api_ctx, ns); } int fcfs_api_init_ex1(FCFSAPIContext *ctx, FDIRClientContext *fdir, FSAPIContext *fsapi, const char *ns, IniFullContext *ini_ctx, const char *fdir_section_name, const char *fs_section_name, const SFConnectionManager *fdir_conn_manager, const SFConnectionManager *fs_conn_manager, const bool need_lock, const bool persist_additional_gids); int fcfs_api_init_ex(FCFSAPIContext *ctx, const char *ns, const char *config_filename, const char *fdir_section_name, const char *fs_section_name); int fcfs_api_init_ex2(FCFSAPIContext *ctx, FDIRClientContext *fdir, FSAPIContext *fsapi, const char *ns, IniFullContext *ini_ctx, const char *fdir_section_name, const char *fs_section_name, const FDIRClientConnManagerType conn_manager_type, const SFConnectionManager *fs_conn_manager, const bool need_lock, const bool persist_additional_gids); static inline int fcfs_api_pooled_init_ex1(FCFSAPIContext *ctx, const char *ns, IniFullContext *ini_ctx, const char * fdir_section_name, const char *fs_section_name, const bool need_lock, const bool persist_additional_gids) { g_fs_api_ctx.fs = &g_fs_client_vars.client_ctx; return fcfs_api_init_ex2(ctx, &g_fdir_client_vars.client_ctx, &g_fs_api_ctx, ns, ini_ctx, fdir_section_name, fs_section_name, conn_manager_type_pooled, NULL, need_lock, persist_additional_gids); } int fcfs_api_pooled_init_ex(FCFSAPIContext *ctx, const char *ns, const char *config_filename, const char *fdir_section_name, const char *fs_section_name, const bool need_lock, const bool persist_additional_gids); void fcfs_api_destroy_ex(FCFSAPIContext *ctx); int fcfs_api_start_ex(FCFSAPIContext *ctx); void fcfs_api_terminate_ex(FCFSAPIContext *ctx); int fcfs_api_client_session_create(FCFSAPIContext *ctx, const bool publish); static inline int fcfs_api_init_with_auth(const char *ns, const char *config_filename, const bool publish) { int result; if ((result=fcfs_api_init(ns, config_filename)) != 0) { return result; } return fcfs_api_client_session_create(&g_fcfs_api_ctx, publish); } static inline int fcfs_api_pooled_init_with_auth(const char *ns, const char *config_filename, const bool publish) { int result; if ((result=fcfs_api_pooled_init(ns, config_filename)) != 0) { return result; } return fcfs_api_client_session_create(&g_fcfs_api_ctx, publish); } static inline int fcfs_api_pooled_init1_with_auth(const char *ns, IniFullContext *ini_ctx, const bool publish) { int result; if ((result=fcfs_api_pooled_init1(ns, ini_ctx)) != 0) { return result; } return fcfs_api_client_session_create(&g_fcfs_api_ctx, publish); } int fcfs_api_load_idempotency_config_ex(const char *log_prefix_name, IniFullContext *ini_ctx, const char *fdir_section_name, const char *fs_section_name); static inline int fcfs_api_load_idempotency_config(const char *log_prefix_name, IniFullContext *ini_ctx) { return fcfs_api_load_idempotency_config_ex(log_prefix_name, ini_ctx, FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME); } int fcfs_api_load_ns_mountpoint(IniFullContext *ini_ctx, const char *fdir_section_name, FCFSAPINSMountpointHolder *nsmp, string_t *mountpoint, const bool fuse_check); void fcfs_api_free_ns_mountpoint(FCFSAPINSMountpointHolder *nsmp); int fcfs_api_check_mountpoint(const char *config_filename, const string_t *mountpoint); static inline int fcfs_api_check_mountpoint1(const char *config_filename, const char *mpoint) { string_t mountpoint; FC_SET_STRING(mountpoint, (char *)mpoint); return fcfs_api_check_mountpoint(config_filename, &mountpoint); } void fcfs_api_async_report_config_to_string_ex(FCFSAPIContext *ctx, char *output, const int size); void fcfs_api_log_client_common_configs(FCFSAPIContext *ctx, const char *fdir_section_name, const char *fs_section_name, BufferInfo *sf_idempotency_config, char *owner_config); int fcfs_api_set_owner(FCFSAPIContext *ctx); static inline const char *fcfs_api_get_owner_type_caption( const FCFSAPIOwnerType owner_type) { switch (owner_type) { case fcfs_api_owner_type_caller: return FCFS_API_OWNER_TYPE_CALLER_STR; case fcfs_api_owner_type_fixed: return FCFS_API_OWNER_TYPE_FIXED_STR; default: return ""; } } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/fcfs_api_allocator.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "fcfs_api_allocator.h" FCFSAPIAllocatorCtxArray g_fcfs_api_allocator_array; static int async_report_event_alloc_init(FCFSAPIAsyncReportEvent *event, struct fast_mblock_man *allocator) { event->allocator = allocator; return 0; } static int waiting_task_alloc_init(FCFSAPIWaitingTask *task, struct fast_mblock_man *allocator) { int result; if ((result=init_pthread_lock_cond_pair(&task->lcp)) != 0) { return result; } task->allocator = allocator; return 0; } static int init_allocator_context(FCFSAPIAllocatorContext *ctx) { int result; if ((result=fast_mblock_init_ex1(&ctx->async_report_event, "async_report_event", sizeof(FCFSAPIAsyncReportEvent), 4096, 0, (fast_mblock_object_init_func) async_report_event_alloc_init, &ctx->async_report_event, true)) != 0) { return result; } if ((result=fast_mblock_init_ex1(&ctx->waiting_task, "waiting_task", sizeof(FCFSAPIWaitingTask), 1024, 0, (fast_mblock_object_init_func)waiting_task_alloc_init, &ctx->waiting_task, true)) != 0) { return result; } return 0; } int fcfs_api_allocator_init(FCFSAPIContext *api_ctx) { int result; int bytes; FCFSAPIAllocatorContext *ctx; FCFSAPIAllocatorContext *end; g_fcfs_api_allocator_array.count = api_ctx-> async_report.shared_allocator_count; bytes = sizeof(FCFSAPIAllocatorContext) * g_fcfs_api_allocator_array.count; g_fcfs_api_allocator_array.allocators = (FCFSAPIAllocatorContext *) fc_malloc(bytes); if (g_fcfs_api_allocator_array.allocators == NULL) { return ENOMEM; } end = g_fcfs_api_allocator_array.allocators + g_fcfs_api_allocator_array.count; for (ctx=g_fcfs_api_allocator_array.allocators; ctx * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_API_ALLOCATOR_H #define _FCFS_API_ALLOCATOR_H #include "fastcommon/pthread_func.h" #include "fcfs_api_types.h" typedef struct fcfs_api_allocator_context { struct fast_mblock_man async_report_event; //element: FCFSAPIAsyncReportEvent struct fast_mblock_man waiting_task; //element: FCFSAPIWaitingTask } FCFSAPIAllocatorContext; typedef struct fcfs_api_allocator_ctx_array { int count; FCFSAPIAllocatorContext *allocators; } FCFSAPIAllocatorCtxArray; #ifdef __cplusplus extern "C" { #endif extern FCFSAPIAllocatorCtxArray g_fcfs_api_allocator_array; int fcfs_api_allocator_init(FCFSAPIContext *api_ctx); static inline FCFSAPIAllocatorContext *fcfs_api_allocator_get( const uint64_t id) { return g_fcfs_api_allocator_array.allocators + id % g_fcfs_api_allocator_array.count; } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/fcfs_api_file.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/sched_thread.h" #include "sf/sf_iov.h" #include "fcfs_api_util.h" #include "async_reporter.h" #include "fcfs_api_file.h" #define FCFS_API_MAGIC_NUMBER 1588076578 static int file_truncate(FCFSAPIContext *ctx, const int64_t oid, const int64_t new_size, const FDIRDentryOperator *oper, const int64_t tid); static int deal_open_flags(FCFSAPIFileInfo *fi, FDIRClientOperFnamePair *path, const FDIRDentryOperator *oper, const mode_t mode, const int64_t tid, int result) { int bytes; fi->tid = tid; fi->oper = *oper; if (fi->ctx->persist_additional_gids) { /* persist fi->oper.additional_gids.list for future usage */ if (oper->additional_gids.count > 0) { bytes = FDIR_ADDITIONAL_GROUP_BYTES(*oper); if (bytes <= sizeof(fi->fixed_groups_buff)) { fi->oper.additional_gids.list = fi->fixed_groups_buff; } else { fi->oper.additional_gids.list = fc_malloc(bytes); if (fi->oper.additional_gids.list == NULL) { return ENOMEM; } } memcpy((void *)fi->oper.additional_gids.list, oper->additional_gids.list, bytes); } else { fi->oper.additional_gids.list = fi->fixed_groups_buff; } } if (!((fi->flags & O_WRONLY) || (fi->flags & O_RDWR))) { fi->offset = 0; return (fi->flags & O_TRUNC) ? EACCES : result; } if ((fi->flags & O_CREAT)) { if (result == 0) { if ((fi->flags & O_EXCL)) { return EEXIST; } } else if (result == ENOENT) { if ((result=fdir_client_create_dentry(fi->ctx->contexts.fdir, path, mode, &fi->dentry)) != 0) { if (result == EEXIST) { if ((fi->flags & O_EXCL)) { return EEXIST; } if ((result=fcfs_api_access_dentry_by_path_ex(fi->ctx, path, FCFS_API_GET_ACCESS_MASK(fi->flags), FCFS_API_GET_ACCESS_FLAGS(fi->flags), &fi->dentry)) != 0) { return result; } } else { return result; } } } else { return result; } } else if (result != 0) { return result; } if (S_ISDIR(fi->dentry.stat.mode)) { return EISDIR; //flags O_WRONLY or O_RDWR are forbidden for directory } if ((fi->flags & O_NOFOLLOW) && S_ISLNK(fi->dentry.stat.mode)) { return ELOOP; } fi->write_notify.last_modified_time = fi->dentry.stat.mtime; if ((fi->flags & O_TRUNC)) { if (fi->dentry.stat.size > 0) { if (S_ISLNK(fi->dentry.stat.mode)) { return EPERM; } if ((result=file_truncate(fi->ctx, fi->dentry. inode, 0, &fi->oper, tid)) != 0) { return result; } fi->dentry.stat.size = 0; fi->dentry.stat.space_end = 0; } } if ((fi->flags & O_APPEND)) { fi->offset = fi->dentry.stat.size; } else { fi->offset = 0; } return 0; } #define SET_FILE_COMMON_FIELDS(fi, _ctx, _flags) \ fi->ctx = _ctx; \ fi->flags = _flags; \ fi->sessions.flock.mconn = NULL int fcfs_api_open_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi, const char *path, const int flags, const FCFSAPIFileContext *fctx) { FDIRClientOperFnamePair fname; mode_t mode; int result; if ((fctx->mode & S_IFMT)) { mode = (fctx->mode & (~S_IFMT)) | S_IFREG; } else { mode = fctx->mode | S_IFREG; } SET_FILE_COMMON_FIELDS(fi, ctx, flags); FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, fctx->oper, path); result = fcfs_api_access_dentry_by_path_ex(ctx, &fname, FCFS_API_GET_ACCESS_MASK(flags), FCFS_API_GET_ACCESS_FLAGS(flags), &fi->dentry); if ((result=deal_open_flags(fi, &fname, &fctx->oper, mode, fctx->tid, result)) != 0) { return result; } fi->magic = FCFS_API_MAGIC_NUMBER; return 0; } int fcfs_api_open_by_dentry_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi, const FDIRDEntryInfo *dentry, const int flags, const FCFSAPIFileContext *fctx) { int result; SET_FILE_COMMON_FIELDS(fi, ctx, flags); fi->dentry = *dentry; result = 0; if ((result=deal_open_flags(fi, NULL, &fctx->oper, fctx->mode, fctx->tid, result)) != 0) { return result; } fi->magic = FCFS_API_MAGIC_NUMBER; return 0; } int fcfs_api_open_by_inode_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi, const int64_t inode, const int flags, const FCFSAPIFileContext *fctx) { FDIRClientOperInodePair oino; int result; FCFSAPI_SET_OPER_INODE_PAIR(oino, fctx->oper, inode); if ((result=fcfs_api_access_dentry_by_inode_ex(ctx, &oino, FCFS_API_GET_ACCESS_MASK(flags), FCFS_API_GET_ACCESS_FLAGS(flags), &fi->dentry)) != 0) { return result; } SET_FILE_COMMON_FIELDS(fi, ctx, flags); if ((result=deal_open_flags(fi, NULL, &fctx->oper, fctx->mode, fctx->tid, result)) != 0) { return result; } fi->magic = FCFS_API_MAGIC_NUMBER; return 0; } int fcfs_api_close(FCFSAPIFileInfo *fi) { if (fi->magic != FCFS_API_MAGIC_NUMBER) { return EBADF; } if (fi->sessions.flock.mconn != NULL) { /* force close connection to unlock */ fdir_client_close_session(&fi->sessions.flock, true); } if (fi->ctx->persist_additional_gids) { if (fi->oper.additional_gids.list != fi->fixed_groups_buff) { free((void *)fi->oper.additional_gids.list); fi->oper.additional_gids.list = NULL; } } fi->ctx = NULL; fi->magic = 0; return 0; } static int report_size_and_time(FCFSAPIContext *ctx, const FDIRSetDEntrySizeInfo *dsize, FDIRDEntryInfo *dentry) { if (ctx->async_report.enabled) { if (dentry != NULL) { if ((dsize->flags & FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE)) { dentry->stat.size = dsize->file_size; } if ((dsize->flags & FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END)) { dentry->stat.space_end = dsize->file_size; } if ((dsize->flags & FDIR_DENTRY_FIELD_MODIFIED_FLAG_MTIME)) { dentry->stat.mtime = get_current_time(); } } return async_reporter_push(dsize); } else { FDIRDEntryInfo tmp; if (dentry == NULL) { dentry = &tmp; } return fdir_client_set_dentry_size(ctx->contexts.fdir, &ctx->ns, dsize, dentry); } } static int fcfs_api_file_report_size_and_time( FCFSAPIWriteDoneCallbackArg *callback_arg, int *flags, FDIRDEntryInfo *dentry) { FDIRSetDEntrySizeInfo dsize; int current_time; dsize.file_size = callback_arg->arg.bs_key->block.offset + callback_arg->arg.bs_key->slice.offset + callback_arg->arg.write_bytes; if (dsize.file_size > callback_arg->extra.file_size) { *flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE | FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END; } else { if (dsize.file_size > callback_arg->extra.space_end) { *flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END; } else { *flags = 0; } current_time = get_current_time(); if (current_time > callback_arg->extra.last_modified_time) { *flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_MTIME; } } if (callback_arg->arg.inc_alloc != 0) { if (!(*flags & (FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE | FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END))) { *flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE | FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END | FDIR_DENTRY_FIELD_MODIFIED_FLAG_INC_ALLOC; } else { *flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_INC_ALLOC; } } if (*flags == 0) { return 0; } else { dsize.inode = callback_arg->arg.bs_key->block.oid; dsize.inc_alloc = callback_arg->arg.inc_alloc; dsize.force = false; dsize.flags = *flags; return report_size_and_time(callback_arg->extra.ctx, &dsize, dentry); } } void fcfs_api_file_write_done_callback(FSAPIWriteDoneCallbackArg *callback_arg) { int flags; fcfs_api_file_report_size_and_time((FCFSAPIWriteDoneCallbackArg *) callback_arg, &flags, NULL); } /* static inline void print_block_slice_key(FSBlockSliceKeyInfo *bs_key) { logInfo("block {oid: %"PRId64", offset: %"PRId64"}, " "slice {offset: %d, length: %d}", bs_key->block.oid, bs_key->block.offset, bs_key->slice.offset, bs_key->slice.length); } */ static int do_pwrite(FCFSAPIFileInfo *fi, FSAPIWriteBuffer *wbuffer, const int size, const int64_t offset, int *written_bytes, int *total_inc_alloc, const bool need_report_modified, const int64_t tid) { FSAPIOperationContext op_ctx; FCFSAPIWriteDoneCallbackArg callback_arg; SFDynamicIOVArray iova; int64_t new_offset; int loop_flags; int result; int remain; FS_API_SET_CTX_AND_TID_EX(op_ctx, fi->ctx->contexts.fsapi, tid); wbuffer->extra_data = &callback_arg.extra; callback_arg.extra.ctx = fi->ctx; *total_inc_alloc = *written_bytes = 0; new_offset = offset; fs_set_block_slice(&op_ctx.bs_key, fi->dentry.inode, offset, size); loop_flags = 1; if (wbuffer->is_writev) { sf_iova_init(iova, wbuffer->iov, wbuffer->iovcnt); if (op_ctx.bs_key.slice.length < size) { if ((result=sf_iova_first_slice(&iova, op_ctx. bs_key.slice.length)) != 0) { loop_flags = 0; } } } while (loop_flags) { //print_block_slice_key(&op_ctx.bs_key); callback_arg.arg.bs_key = &op_ctx.bs_key; callback_arg.extra.file_size = fi->dentry.stat.size; callback_arg.extra.space_end = fi->dentry.stat.space_end; callback_arg.extra.last_modified_time = fi->write_notify.last_modified_time; if ((result=fs_api_slice_write(&op_ctx, wbuffer, &callback_arg. arg.write_bytes, &callback_arg.arg.inc_alloc)) != 0) { if (callback_arg.arg.write_bytes == 0) { break; } } new_offset += callback_arg.arg.write_bytes; *written_bytes += callback_arg.arg.write_bytes; *total_inc_alloc += callback_arg.arg.inc_alloc; if (need_report_modified) { int flags; fcfs_api_file_report_size_and_time(&callback_arg, &flags, &fi->dentry); if ((flags & FDIR_DENTRY_FIELD_MODIFIED_FLAG_MTIME) != 0) { fi->write_notify.last_modified_time = get_current_time(); } } remain = size - *written_bytes; if (remain <= 0) { break; } if (callback_arg.arg.write_bytes == op_ctx.bs_key.slice.length) { /* fully completed */ fs_next_block_slice_key(&op_ctx.bs_key, remain); } else { //partially completed, try again the remain part fs_set_slice_size(&op_ctx.bs_key, new_offset, remain); } if (wbuffer->is_writev) { if ((result=sf_iova_next_slice(&iova, *written_bytes, op_ctx.bs_key.slice.length)) != 0) { break; } } else { wbuffer->buff += *written_bytes; } } if (wbuffer->is_writev) { sf_iova_destroy(iova); } return (*written_bytes > 0) ? 0 : EIO; } static inline int check_writable(FCFSAPIFileInfo *fi) { if (fi->magic != FCFS_API_MAGIC_NUMBER || !((fi->flags & O_WRONLY) || (fi->flags & O_RDWR))) { return EBADF; } if (S_ISDIR(fi->dentry.stat.mode)) { return EISDIR; } return (S_ISREG(fi->dentry.stat.mode) ? 0 : EPERM); } static inline int check_readable(FCFSAPIFileInfo *fi) { if (fi->magic != FCFS_API_MAGIC_NUMBER || (fi->flags & O_WRONLY)) { return EBADF; } if (S_ISDIR(fi->dentry.stat.mode)) { return EISDIR; } return (S_ISREG(fi->dentry.stat.mode) ? 0 : EPERM); } static int pwrite_wrapper(FCFSAPIFileInfo *fi, FSAPIWriteBuffer *wbuffer, const int size, const int64_t offset, int *written_bytes, const int64_t tid) { int total_inc_alloc; int result; if (size == 0) { return 0; } else if (size < 0) { return EINVAL; } if ((result=check_writable(fi)) != 0) { return result; } return do_pwrite(fi, wbuffer, size, offset, written_bytes, &total_inc_alloc, true, tid); } int fcfs_api_pwrite_ex(FCFSAPIFileInfo *fi, const char *buff, const int size, const int64_t offset, int *written_bytes, const int64_t tid) { FSAPIWriteBuffer wbuffer; FS_API_SET_WBUFFER_BUFF(wbuffer, buff); return pwrite_wrapper(fi, &wbuffer, size, offset, written_bytes, tid); } static int do_write(FCFSAPIFileInfo *fi, FSAPIWriteBuffer *wbuffer, const int size, int *written_bytes, const int64_t tid) { FDIRClientSession session; FDIRSetDEntrySizeInfo dsize; bool use_sys_lock; int result; int total_inc_alloc; int64_t old_size; int64_t space_end; if (size == 0) { return 0; } else if (size < 0) { return EINVAL; } if ((result=check_writable(fi)) != 0) { return result; } use_sys_lock = fi->ctx->use_sys_lock_for_append && !fi->ctx->async_report.enabled && (fi->flags & O_APPEND); if (use_sys_lock) { if ((result=fcfs_api_dentry_sys_lock(&session, fi->dentry.inode, 0, &old_size, &space_end)) != 0) { return result; } fi->offset = old_size; } else { old_size = fi->dentry.stat.size; } if ((result=do_pwrite(fi, wbuffer, size, fi->offset, written_bytes, &total_inc_alloc, !use_sys_lock, tid)) == 0) { fi->offset += *written_bytes; } if (use_sys_lock) { string_t *ns; if (fi->offset > old_size) { ns = &fi->ctx->ns; //set ns for update file_size, space_end etc. } else { ns = NULL; //do NOT update } dsize.inode = fi->dentry.inode; dsize.file_size = fi->offset; dsize.inc_alloc = total_inc_alloc; dsize.force = false; dsize.flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE | FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END; fcfs_api_dentry_sys_unlock(&session, ns, old_size, &dsize); } return result; } int fcfs_api_write_ex(FCFSAPIFileInfo *fi, const char *buff, const int size, int *written_bytes, const int64_t tid) { FSAPIWriteBuffer wbuffer; FS_API_SET_WBUFFER_BUFF(wbuffer, buff); return do_write(fi, &wbuffer, size, written_bytes, tid); } int fcfs_api_pwritev_ex(FCFSAPIFileInfo *fi, const struct iovec *iov, const int iovcnt, const int64_t offset, int *written_bytes, const int64_t tid) { FSAPIWriteBuffer wbuffer; FS_API_SET_WBUFFER_IOV(wbuffer, iov, iovcnt); return pwrite_wrapper(fi, &wbuffer, fc_iov_get_bytes(iov, iovcnt), offset, written_bytes, tid); } int fcfs_api_writev_ex(FCFSAPIFileInfo *fi, const struct iovec *iov, const int iovcnt, int *written_bytes, const int64_t tid) { FSAPIWriteBuffer wbuffer; FS_API_SET_WBUFFER_IOV(wbuffer, iov, iovcnt); return do_write(fi, &wbuffer, fc_iov_get_bytes(iov, iovcnt), written_bytes, tid); } static int do_pread(FCFSAPIFileInfo *fi, const bool is_readv, const void *data, const int iovcnt, const int size, const int64_t offset, int *read_bytes, const int64_t tid) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; FSAPIOperationContext op_ctx; SFDynamicIOVArray iova; FDIRClientOperInodePair oino; int result; int current_read; int remain; int64_t current_offset; int64_t hole_bytes; int fill_bytes; *read_bytes = 0; if (size == 0) { return 0; } else if (size < 0) { return EINVAL; } if ((result=check_readable(fi)) != 0) { return result; } FS_API_SET_CTX_AND_TID_EX(op_ctx, fi->ctx->contexts.fsapi, tid); fs_set_block_slice(&op_ctx.bs_key, fi->dentry.inode, offset, size); if (is_readv) { sf_iova_init(iova, (struct iovec *)data, iovcnt); } while (1) { //print_block_slice_key(&op_ctx.bs_key); if (is_readv) { result = fs_api_slice_readv(&op_ctx, iova.iov, iova.cnt, ¤t_read); } else { result = fs_api_slice_read(&op_ctx, (char *)data + (*read_bytes), ¤t_read); } if (result != 0) { if (result == ENODATA) { result = 0; } else { break; } } while (current_read < op_ctx.bs_key.slice.length) { /* deal file hole caused by ftruncate and lseek */ current_offset = offset + *read_bytes + current_read; if (current_offset == fi->dentry.stat.size) { break; } if (current_offset > fi->dentry.stat.size) { FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode); if ((result=fcfs_api_stat_dentry_by_inode_ex(fi->ctx, &oino, flags, &fi->dentry)) != 0) { break; } } hole_bytes = fi->dentry.stat.size - current_offset; if (hole_bytes > 0) { if ((int64_t)current_read + hole_bytes > (int64_t) op_ctx.bs_key.slice.length) { fill_bytes = op_ctx.bs_key.slice.length - current_read; } else { fill_bytes = hole_bytes; } /* logInfo("=====offset: %"PRId64", current_read: %d, " "hole_bytes: %"PRId64", fill_bytes: %d, " "buff offset: %d =====", offset, current_read, hole_bytes, fill_bytes, *read_bytes + current_read); */ if (is_readv) { if ((result=sf_iova_memset(iova, 0, current_read, fill_bytes)) != 0) { break; } } else { memset((char *)data + (*read_bytes) + current_read, 0, fill_bytes); } current_read += fill_bytes; } break; } /* logInfo("current read: %d, total read: %d, slice length: %d", current_read, *read_bytes, op_ctx.bs_key.slice.length); */ *read_bytes += current_read; remain = size - *read_bytes; if (remain <= 0 || current_read < op_ctx.bs_key.slice.length) { break; } if (is_readv) { if ((result=sf_iova_consume(&iova, current_read)) != 0) { break; } } fs_next_block_slice_key(&op_ctx.bs_key, remain); } if (is_readv) { sf_iova_destroy(iova); } return result; } int fcfs_api_pread_ex(FCFSAPIFileInfo *fi, char *buff, const int size, const int64_t offset, int *read_bytes, const int64_t tid) { const bool is_readv = false; const int iovcnt = 0; return do_pread(fi, is_readv, buff, iovcnt, size, offset, read_bytes, tid); } int fcfs_api_read_ex(FCFSAPIFileInfo *fi, char *buff, const int size, int *read_bytes, const int64_t tid) { const bool is_readv = false; const int iovcnt = 0; int result; if ((result=do_pread(fi, is_readv, buff, iovcnt, size, fi->offset, read_bytes, tid)) != 0) { return result; } fi->offset += *read_bytes; return 0; } int fcfs_api_preadv_ex(FCFSAPIFileInfo *fi, const struct iovec *iov, const int iovcnt, const int64_t offset, int *read_bytes, const int64_t tid) { const bool is_readv = true; return do_pread(fi, is_readv, iov, iovcnt, fc_iov_get_bytes(iov, iovcnt), offset, read_bytes, tid); } int fcfs_api_readv_ex(FCFSAPIFileInfo *fi, const struct iovec *iov, const int iovcnt, int *read_bytes, const int64_t tid) { const bool is_readv = true; int result; if ((result=do_pread(fi, is_readv, iov, iovcnt, fc_iov_get_bytes(iov, iovcnt), fi->offset, read_bytes, tid)) != 0) { return result; } fi->offset += *read_bytes; return 0; } static int do_truncate(FCFSAPIContext *ctx, const int64_t oid, const int64_t old_space_end, const int64_t offset, const int64_t length, int64_t *total_dec_alloc, const int64_t tid) { FSAPIOperationContext op_ctx; int64_t remain; int dec_alloc; int result; *total_dec_alloc = 0; if (offset >= old_space_end) { return 0; } if (offset + length > old_space_end) { remain = old_space_end - offset; } else { remain = length; } FS_API_SET_CTX_AND_TID_EX(op_ctx, ctx->contexts.fsapi, tid); fs_set_block_slice(&op_ctx.bs_key, oid, offset, remain); while (1) { //print_block_slice_key(&op_ctx.bs_key); if (op_ctx.bs_key.slice.length == FS_FILE_BLOCK_SIZE) { result = fs_api_block_delete(&op_ctx, &dec_alloc); } else { result = fs_api_slice_delete(&op_ctx, &dec_alloc); } if (result == 0) { *total_dec_alloc -= dec_alloc; } else if (result == ENOENT) { result = 0; } else if (result != 0) { break; } remain -= op_ctx.bs_key.slice.length; if (remain <= 0) { break; } fs_next_block_slice_key(&op_ctx.bs_key, remain); } return result; } static int do_allocate(FCFSAPIContext *ctx, const int64_t oid, const int64_t offset, const int64_t length, int64_t *total_inc_alloc, const int64_t tid) { FSAPIOperationContext op_ctx; int64_t remain; int inc_alloc; int result; *total_inc_alloc = 0; if (length <= 0) { return 0; } FS_API_SET_CTX_AND_TID_EX(op_ctx, ctx->contexts.fsapi, tid); remain = length; fs_set_block_slice(&op_ctx.bs_key, oid, offset, remain); while (1) { //print_block_slice_key(&op_ctx.bs_key); if ((result=fs_api_slice_allocate(&op_ctx, &inc_alloc)) != 0) { break; } *total_inc_alloc += inc_alloc; remain -= op_ctx.bs_key.slice.length; if (remain <= 0) { break; } fs_next_block_slice_key(&op_ctx.bs_key, remain); } return result; } static inline int check_and_sys_lock(FCFSAPIContext *ctx, FDIRClientSession *session, const int64_t oid, const FDIRDentryOperator *oper, int64_t *old_size, int64_t *space_end) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; FDIRClientOperInodePair oino; FDIRDEntryInfo dentry; int result; if (ctx->use_sys_lock_for_append && !ctx->async_report.enabled) { result = fcfs_api_dentry_sys_lock(session, oid, 0, old_size, space_end); } else { FCFSAPI_SET_OPER_INODE_PAIR(oino, *oper, oid); if ((result=fcfs_api_stat_dentry_by_inode_ex(ctx, &oino, flags, &dentry)) == 0) { *old_size = dentry.stat.size; *space_end = dentry.stat.space_end; } } return result; } static inline int check_and_sys_unlock(FCFSAPIContext *ctx, FDIRClientSession *session, const int64_t old_size, const FDIRSetDEntrySizeInfo *dsize, const int result) { if (ctx->use_sys_lock_for_append && !ctx->async_report.enabled) { string_t *ns; int unlock_res; if (result == 0) { ns = &ctx->ns; //set ns for update file_size, space_end etc. } else { ns = NULL; //do NOT update } unlock_res = fcfs_api_dentry_sys_unlock(session, ns, old_size, dsize); return result == 0 ? unlock_res : result; } else { if (result == 0) { return report_size_and_time(ctx, dsize, NULL); } else { return result; } } } static int file_truncate(FCFSAPIContext *ctx, const int64_t oid, const int64_t new_size, const FDIRDentryOperator *oper, const int64_t tid) { FDIRClientSession session; FDIRSetDEntrySizeInfo dsize; int64_t old_size; int64_t space_end; int result; if (new_size < 0) { return EINVAL; } if ((result=check_and_sys_lock(ctx, &session, oid, oper, &old_size, &space_end)) != 0) { return result; } dsize.inode = oid; dsize.file_size = new_size; dsize.force = true; if (new_size >= old_size) { result = 0; dsize.inc_alloc = 0; if (new_size == old_size) { dsize.flags = 0; } else { dsize.flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE; } } else { result = do_truncate(ctx, oid, space_end, new_size, old_size - new_size, &dsize.inc_alloc, tid); dsize.flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE; if (new_size < space_end) { dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END; } if (dsize.inc_alloc != 0) { dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_INC_ALLOC; } } return check_and_sys_unlock(ctx, &session, old_size, &dsize, result); } int fcfs_api_ftruncate_ex(FCFSAPIFileInfo *fi, const int64_t new_size, const int64_t tid) { int result; if ((result=check_writable(fi)) != 0) { return result; } return file_truncate(fi->ctx, fi->dentry.inode, new_size, &fi->oper, tid); } static int get_regular_file_inode(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *fname, int64_t *inode) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; FDIRDEntryInfo dentry; int result; if ((result=fcfs_api_stat_dentry_by_fullname_ex(ctx, fname, flags, LOG_DEBUG, &dentry)) != 0) { return result; } if (S_ISDIR(dentry.stat.mode)) { return EISDIR; } *inode = dentry.inode; return 0; } int fcfs_api_truncate_ex(FCFSAPIContext *ctx, const char *path, const int64_t new_size, const FCFSAPIFileContext *fctx) { int result; FDIRClientOperFnamePair fname; int64_t inode; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, fctx->oper, path); if ((result=get_regular_file_inode(ctx, &fname, &inode)) != 0) { return result; } return file_truncate(ctx, inode, new_size, &fname.oper, fctx->tid); } int fcfs_api_file_truncate_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const int64_t new_size, const int64_t tid, FDIRDEntryInfo *dentry) { const int flags = FDIR_FLAGS_OUTPUT_DENTRY; int result; if ((result=fcfs_api_access_dentry_by_inode_ex(ctx, oino, W_OK, flags, dentry)) != 0) { return result; } if (S_ISDIR(dentry->stat.mode)) { return EISDIR; } return file_truncate(ctx, oino->inode, new_size, &oino->oper, tid); } #define calc_file_offset(fi, offset, whence, new_offset) \ calc_file_offset_ex(fi, offset, whence, false, new_offset) static inline int calc_file_offset_ex(FCFSAPIFileInfo *fi, const int64_t offset, const int whence, const bool refresh_fsize, int64_t *new_offset) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; int result; FDIRClientOperInodePair oino; switch (whence) { case SEEK_SET: if (offset < 0) { return EINVAL; } *new_offset = offset; break; case SEEK_CUR: *new_offset = fi->offset + offset; if (*new_offset < 0) { return EINVAL; } break; case SEEK_END: if (refresh_fsize) { FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode); if ((result=fcfs_api_stat_dentry_by_inode_ex(fi->ctx, &oino, flags, &fi->dentry)) != 0) { return result; } } *new_offset = fi->dentry.stat.size + offset; if (*new_offset < 0) { return EINVAL; } break; default: logError("file: "__FILE__", line: %d, " "invalid whence: %d", __LINE__, whence); return EINVAL; } return 0; } int fcfs_api_lseek(FCFSAPIFileInfo *fi, const int64_t offset, const int whence) { int64_t new_offset; int result; if (fi->magic != FCFS_API_MAGIC_NUMBER) { return EBADF; } if ((result=calc_file_offset_ex(fi, offset, whence, true, &new_offset)) != 0) { return result; } fi->offset = new_offset; return 0; } void fcfs_api_fill_stat(const FDIRDEntryInfo *dentry, struct stat *stat) { stat->st_ino = dentry->inode; stat->st_rdev = dentry->stat.rdev; stat->st_mode = dentry->stat.mode; stat->st_size = dentry->stat.size; stat->st_atime = (uint32_t)dentry->stat.atime; stat->st_mtime = (uint32_t)dentry->stat.mtime; stat->st_ctime = (uint32_t)dentry->stat.ctime; stat->st_uid = dentry->stat.uid; stat->st_gid = dentry->stat.gid; stat->st_nlink = dentry->stat.nlink; stat->st_blksize = 512; if (dentry->stat.alloc > 0) { stat->st_blocks = (dentry->stat.alloc + stat->st_blksize - 1) / stat->st_blksize; } } int fcfs_api_fstat(FCFSAPIFileInfo *fi, struct stat *buf) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; int result; FDIRClientOperInodePair oino; if (fi->magic != FCFS_API_MAGIC_NUMBER) { return EBADF; } FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode); if ((result=fcfs_api_stat_dentry_by_inode_ex(fi->ctx, &oino, flags, &fi->dentry)) != 0) { return result; } memset(buf, 0, sizeof(struct stat)); fcfs_api_fill_stat(&fi->dentry, buf); return 0; } static inline int fapi_stat(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, struct stat *buf, const int flags) { int result; FDIRDEntryInfo dentry; if ((result=fcfs_api_stat_dentry_by_fullname_ex(ctx, path, flags, LOG_DEBUG, &dentry)) != 0) { return result; } memset(buf, 0, sizeof(struct stat)); fcfs_api_fill_stat(&dentry, buf); return 0; } int fcfs_api_lstat_ex(FCFSAPIContext *ctx, const char *path, const FDIRDentryOperator *oper, struct stat *buf) { const int flags = 0; FDIRClientOperFnamePair fname; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path); return fapi_stat(ctx, &fname, buf, flags); } int fcfs_api_stat_ex(FCFSAPIContext *ctx, const char *path, const FDIRDentryOperator *oper, struct stat *buf, const int flags) { FDIRClientOperFnamePair fname; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path); return fapi_stat(ctx, &fname, buf, flags); } static inline int fcntl_lock(FCFSAPIFileInfo *fi, const int operation, const int64_t offset, const int64_t length, const int64_t owner_id, const pid_t pid) { int result; FDIRFlockOwner owner; FDIRClientOperInodePair oino; if (fi->sessions.flock.mconn == NULL) { if ((result=fdir_client_init_session(fi->ctx->contexts. fdir, &fi->sessions.flock)) != 0) { return result; } } FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode); owner.id = owner_id; owner.pid = pid; if ((result=fdir_client_flock_dentry_ex(&fi->sessions.flock, &fi->ctx->ns, &oino, operation, offset, length, &owner)) != 0) { return result; } return 0; } static inline int fcntl_unlock(FCFSAPIFileInfo *fi, const int operation, const int64_t offset, const int64_t length, const int64_t owner_id, const pid_t pid) { int result; FDIRFlockOwner owner; FDIRClientOperInodePair oino; if (fi->sessions.flock.mconn == NULL) { return 0; } FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode); owner.id = owner_id; owner.pid = pid; if ((result=fdir_client_flock_dentry_ex(&fi->sessions.flock, &fi->ctx->ns, &oino, operation, offset, length, &owner)) != 0) { return (result == ENOENT ? 0 : result); } return 0; } int fcfs_api_flock_ex2(FCFSAPIFileInfo *fi, const int operation, const int64_t owner_id, const pid_t pid) { const int64_t offset = 0; const int64_t length = 0; if (fi->magic != FCFS_API_MAGIC_NUMBER) { return EBADF; } if ((operation & LOCK_UN)) { return fcntl_unlock(fi, operation, offset, length, owner_id, pid); } else if ((operation & LOCK_SH) || (operation & LOCK_EX)) { return fcntl_lock(fi, operation, offset, length, owner_id, pid); } else { return EINVAL; } } static inline int fcntl_type_to_flock_op(const short type, int *operation) { switch (type) { case F_RDLCK: *operation = LOCK_SH; break; case F_WRLCK: *operation = LOCK_EX; break; case F_UNLCK: *operation = LOCK_UN; break; default: return EINVAL; } return 0; } static inline int flock_op_to_fcntl_type(const int operation, short *type) { switch (operation) { case LOCK_SH: *type = F_RDLCK; break; case LOCK_EX: *type = F_WRLCK; break; case LOCK_UN: *type = F_UNLCK; break; default: return EINVAL; } return 0; } int fcfs_api_setlk_ex(FCFSAPIFileInfo *fi, const struct flock *lock, const int64_t owner_id, const bool blocked) { int operation; int result; int64_t offset; if (fi->magic != FCFS_API_MAGIC_NUMBER) { return EBADF; } if ((result=fcntl_type_to_flock_op(lock->l_type, &operation)) != 0) { return result; } if ((result=calc_file_offset(fi, lock->l_start, lock->l_whence, &offset)) != 0) { return result; } if (operation == LOCK_UN) { return fcntl_unlock(fi, operation, offset, lock->l_len, owner_id, lock->l_pid); } else { if (!blocked) { operation |= LOCK_NB; } return fcntl_lock(fi, operation, offset, lock->l_len, owner_id, lock->l_pid); } } int fcfs_api_getlk_ex(FCFSAPIFileInfo *fi, struct flock *lock, int64_t *owner_id) { int operation; int result; int64_t offset; int64_t length; FDIRClientOperInodePair oino; FDIRFlockOwner owner; if (fi->magic != FCFS_API_MAGIC_NUMBER) { return EBADF; } if ((result=fcntl_type_to_flock_op(lock->l_type, &operation)) != 0) { return result; } if (operation == LOCK_UN) { return EINVAL; } if ((result=calc_file_offset(fi, lock->l_start, lock->l_whence, &offset)) != 0) { return result; } FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode); length = lock->l_len; owner.id = *owner_id; owner.pid = lock->l_pid; if ((result=fdir_client_getlk_dentry(fi->ctx->contexts.fdir, &fi->ctx->ns, &oino, &operation, &offset, &length, &owner)) == 0) { flock_op_to_fcntl_type(operation, &lock->l_type); lock->l_whence = SEEK_SET; lock->l_start = offset; lock->l_len = length; } return result; } int fcfs_api_fallocate_ex(FCFSAPIFileInfo *fi, const int mode, const int64_t offset, const int64_t length, const int64_t tid) { FDIRClientSession session; FDIRSetDEntrySizeInfo dsize; int64_t old_size; int64_t space_end; int op; int result; if (offset < 0 || length < 0) { return EINVAL; } if ((result=check_writable(fi)) != 0) { return result; } op = mode & (~FALLOC_FL_KEEP_SIZE); if (!(op == 0 || op == FALLOC_FL_PUNCH_HOLE)) { return EOPNOTSUPP; } if (length == 0) { return 0; } if (fi->ctx->use_sys_lock_for_append && !fi->ctx->async_report.enabled) { if ((result=fcfs_api_dentry_sys_lock(&session, fi->dentry. inode, 0, &old_size, &space_end)) != 0) { return result; } } else { old_size = fi->dentry.stat.size; space_end = fi->dentry.stat.space_end; } dsize.inode = fi->dentry.inode; dsize.force = true; dsize.flags = 0; if (op == 0) { //allocate space result = do_allocate(fi->ctx, fi->dentry.inode, offset, length, &dsize.inc_alloc, tid); dsize.file_size = offset + length; if (dsize.file_size > space_end) { dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END; } if (dsize.file_size > old_size) { dsize.flags |= (mode & FALLOC_FL_KEEP_SIZE) ? 0 : FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE; } } else { //deallocate space result = do_truncate(fi->ctx, fi->dentry.inode, space_end, offset, length, &dsize.inc_alloc, tid); if (offset + length >= old_size) { dsize.file_size = offset; dsize.flags |= (mode & FALLOC_FL_KEEP_SIZE) ? 0 : FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE; if (dsize.file_size < space_end) { dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END; } } else { if (offset < space_end && offset + length >= space_end) { dsize.file_size = offset; dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END; } else { dsize.file_size = old_size; } } } if (dsize.inc_alloc != 0) { dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_INC_ALLOC; } return check_and_sys_unlock(fi->ctx, &session, old_size, &dsize, result); } int fcfs_api_rename_ex(FCFSAPIContext *ctx, const char *old_path, const char *new_path, const int flags, const FCFSAPIFileContext *fctx) { FDIRDEntryFullName src_fullname; FDIRDEntryFullName dest_fullname; FDIRDEntryInfo dentry; FDIRDEntryInfo *pe; int result; src_fullname.ns = ctx->ns; FC_SET_STRING(src_fullname.path, (char *)old_path); dest_fullname.ns = ctx->ns; FC_SET_STRING(dest_fullname.path, (char *)new_path); pe = &dentry; if ((result=fdir_client_rename_dentry_ex(ctx->contexts.fdir, &src_fullname, &dest_fullname, &fctx->oper, flags, &pe)) != 0) { return result; } if (pe != NULL && S_ISREG(pe->stat.mode) && pe->stat.nlink == 0 && !ctx->contexts.fdir->trash_bin_enabled) { fs_api_unlink_file(ctx->contexts.fsapi, pe->inode, pe->stat.space_end, fctx->tid); } return result; } int fcfs_api_symlink_ex(FCFSAPIContext *ctx, const char *target, const char *path, const FDIRDentryOperator *oper, const mode_t mode) { FDIRClientOperFnamePair fname; string_t link; FDIRDEntryInfo dentry; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path); FC_SET_STRING(link, (char *)target); return fdir_client_symlink_dentry(ctx->contexts.fdir, &link, &fname, mode, &dentry); } int fcfs_api_readlink(FCFSAPIContext *ctx, const char *path, const FDIRDentryOperator *oper, char *buff, const int size) { FDIRClientOperFnamePair fname; string_t link; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path); link.str = buff; return fdir_client_readlink_by_path(ctx->contexts.fdir, &fname, &link, size); } int fcfs_api_link_ex(FCFSAPIContext *ctx, const char *old_path, const char *new_path, const FDIRDentryOperator *oper, const mode_t mode, const int flags) { FDIRDEntryFullName src_fullname; FDIRDEntryFullName dest_fullname; FDIRDEntryInfo dentry; src_fullname.ns = ctx->ns; FC_SET_STRING(src_fullname.path, (char *)old_path); dest_fullname.ns = ctx->ns; FC_SET_STRING(dest_fullname.path, (char *)new_path); return fdir_client_link_dentry(ctx->contexts.fdir, &src_fullname, &dest_fullname, oper, mode, flags, &dentry); } int fcfs_api_mknod_ex(FCFSAPIContext *ctx, const char *path, const FDIRDentryOperator *oper, const mode_t mode, const dev_t dev) { FDIRClientOperFnamePair fname; FDIRDEntryInfo dentry; if (!(S_ISCHR(mode) || S_ISBLK(mode))) { return EINVAL; } FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path); return fdir_client_create_dentry_ex(ctx->contexts.fdir, &fname, mode, dev, &dentry); } static inline int do_make_dentry(FCFSAPIContext *ctx, const char *path, FDIRDentryOperator *oper, const mode_t mode, const int mtype) { FDIRClientOperFnamePair fname; FDIRDEntryInfo dentry; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path); return fdir_client_create_dentry(ctx->contexts.fdir, &fname, ((mode & (~S_IFMT)) | mtype), &dentry); } int fcfs_api_mkfifo_ex(FCFSAPIContext *ctx, const char *path, FDIRDentryOperator *oper, const mode_t mode) { return do_make_dentry(ctx, path, oper, mode, S_IFIFO); } int fcfs_api_mkdir_ex(FCFSAPIContext *ctx, const char *path, FDIRDentryOperator *oper, const mode_t mode) { return do_make_dentry(ctx, path, oper, mode, S_IFDIR); } int fcfs_api_statvfs_ex(FCFSAPIContext *ctx, const char *path, struct statvfs *stbuf) { int result; FCFSAuthClientFullContext *auth; int64_t quota; int64_t total; int64_t avail; FDIRClientNamespaceStat nstat; if (ctx->contexts.fdir->auth.enabled) { auth = &ctx->contexts.fdir->auth; } else if (ctx->contexts.fsapi->fs->auth.enabled) { auth = &ctx->contexts.fsapi->fs->auth; } else { auth = NULL; } if (auth != NULL) { if ((result=fcfs_auth_client_spool_get_quota( auth->ctx, &ctx->ns, "a)) != 0) { return result; } } else { quota = FCFS_AUTH_UNLIMITED_QUOTA_VAL; } if ((result=fdir_client_namespace_stat(ctx->contexts. fdir, &ctx->ns, &nstat)) != 0) { return result; } if (quota == FCFS_AUTH_UNLIMITED_QUOTA_VAL) { FSClusterSpaceStat sstat; if ((result=fs_api_cluster_space_stat(ctx-> contexts.fsapi, &sstat)) != 0) { return result; } total = sstat.total; avail = total - sstat.used; } else { total = quota; avail = total - nstat.space.used; } if (avail < 0) { avail = 0; } stbuf->f_bsize = stbuf->f_frsize = 512; stbuf->f_blocks = total / stbuf->f_frsize; stbuf->f_bavail = stbuf->f_bfree = avail / stbuf->f_frsize; stbuf->f_files = nstat.inode.total; stbuf->f_ffree = nstat.inode.total - nstat.inode.used; stbuf->f_favail = nstat.inode.avail; stbuf->f_namemax = NAME_MAX; stbuf->f_fsid = 0; stbuf->f_flag = 0; return 0; } int fcfs_api_access_ex(FCFSAPIContext *ctx, const char *path, const int mask, const FDIRDentryOperator *oper, const int flags) { FDIRClientOperFnamePair fname; FDIRDEntryInfo dentry; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path); return fcfs_api_access_dentry_by_path_ex(ctx, &fname, mask, flags, &dentry); } int fcfs_api_euidaccess_ex(FCFSAPIContext *ctx, const char *path, const int mask, const FDIRDentryOperator *oper, const int flags) { FDIRClientOperFnamePair fname; FDIRDEntryInfo dentry; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path); return fcfs_api_access_dentry_by_path_ex(ctx, &fname, mask, flags, &dentry); } int fcfs_api_set_file_flags(FCFSAPIFileInfo *fi, const int flags) { /* if (flags & (O_ASYNC | O_DIRECT | O_NOATIME | O_NONBLOCK)) { logInfo("set flags: %d, is O_DIRECT: %d, is O_NOATIME: %d, " "is O_NONBLOCK: %d", flags, (flags & O_DIRECT), (flags & O_NOATIME), (flags & O_NONBLOCK)); } */ if ((flags & O_APPEND) == 0) { return 0; } if (!((fi->flags & O_WRONLY) || (fi->flags & O_RDWR))) { return EBADF; } fi->flags |= O_APPEND; fi->offset = fi->dentry.stat.size; return 0; } ================================================ FILE: src/api/fcfs_api_file.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_API_FILE_H #define _FCFS_API_FILE_H #include #include #include #include #include #include #include "fcfs_api_types.h" #include "fcfs_api_util.h" #ifndef FALLOC_FL_KEEP_SIZE #define FALLOC_FL_KEEP_SIZE 0x01 #endif #ifndef FALLOC_FL_PUNCH_HOLE #define FALLOC_FL_PUNCH_HOLE 0x02 #endif #ifdef O_SYMLINK #define FCFS_API_NOFOLLOW_SYMLINK_FLAGS (O_NOFOLLOW | O_SYMLINK) #else #define FCFS_API_NOFOLLOW_SYMLINK_FLAGS (O_NOFOLLOW) #endif #define FCFS_API_GET_ACCESS_FLAGS(open_flags) \ ((((open_flags) & FCFS_API_NOFOLLOW_SYMLINK_FLAGS) != 0) ? \ FDIR_FLAGS_OUTPUT_DENTRY : (FDIR_FLAGS_FOLLOW_SYMLINK | \ FDIR_FLAGS_OUTPUT_DENTRY)) #define FCFS_API_GET_ACCESS_MASK(open_flags) \ (((open_flags) & O_WRONLY) ? W_OK : (((open_flags) & O_RDWR) ? \ (R_OK | W_OK) : R_OK)) #ifdef __cplusplus extern "C" { #endif #define fcfs_api_create(fi, path, fctx) \ fcfs_api_create_ex(&g_fcfs_api_ctx, fi, path, fctx) #define fcfs_api_open(fi, path, flags, fctx) \ fcfs_api_open_ex(&g_fcfs_api_ctx, fi, path, flags, fctx) #define fcfs_api_open_by_inode(fi, inode, flags, fctx) \ fcfs_api_open_by_inode_ex(&g_fcfs_api_ctx, fi, inode, flags, fctx) #define fcfs_api_open_by_dentry(fi, dentry, flags, fctx) \ fcfs_api_open_by_dentry_ex(&g_fcfs_api_ctx, fi, dentry, flags, fctx) #define fcfs_api_write(fi, buff, size, written_bytes) \ fcfs_api_write_ex(fi, buff, size, written_bytes, (fi)->tid) #define fcfs_api_pwrite(fi, buff, size, offset, written_bytes) \ fcfs_api_pwrite_ex(fi, buff, size, offset, written_bytes, (fi)->tid) #define fcfs_api_read(fi, buff, size, read_bytes) \ fcfs_api_read_ex(fi, buff, size, read_bytes, (fi)->tid) #define fcfs_api_pread(fi, buff, size, offset, read_bytes) \ fcfs_api_pread_ex(fi, buff, size, offset, read_bytes, (fi)->tid) #define fcfs_api_writev(fi, iov, iovcnt, written_bytes) \ fcfs_api_writev_ex(fi, iov, iovcnt, written_bytes, (fi)->tid) #define fcfs_api_pwritev(fi, iov, iovcnt, offset, written_bytes) \ fcfs_api_pwritev_ex(fi, iov, iovcnt, offset, written_bytes, (fi)->tid) #define fcfs_api_readv(fi, iov, iovcnt, read_bytes) \ fcfs_api_readv_ex(fi, iov, iovcnt, read_bytes, (fi)->tid) #define fcfs_api_preadv(fi, iov, iovcnt, offset, read_bytes) \ fcfs_api_preadv_ex(fi, iov, iovcnt, offset, read_bytes, (fi)->tid) #define fcfs_api_ftruncate(fi, new_size) \ fcfs_api_ftruncate_ex(fi, new_size, getpid()) #define fcfs_api_truncate(path, new_size, fctx) \ fcfs_api_truncate_ex(&g_fcfs_api_ctx, path, new_size, fctx) #define fcfs_api_file_truncate(oino, new_size, tid, dentry) \ fcfs_api_file_truncate_ex(&g_fcfs_api_ctx, oino, new_size, tid, dentry) #define fcfs_api_unlink(path, tid) \ fcfs_api_unlink_ex(&g_fcfs_api_ctx, path, tid) #define fcfs_api_stat(path, oper, buf, flags) \ fcfs_api_stat_ex(&g_fcfs_api_ctx, path, oper, buf, flags) #define fcfs_api_lstat(path, oper, buf) \ fcfs_api_lstat_ex(&g_fcfs_api_ctx, path, oper, buf) #define fcfs_api_rename(old_path, new_path, fctx) \ fcfs_api_rename_ex(&g_fcfs_api_ctx, old_path, new_path, 0, fctx) #define fcfs_api_statvfs(path, stbuf) \ fcfs_api_statvfs_ex(&g_fcfs_api_ctx, path, stbuf) int fcfs_api_open_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi, const char *path, const int flags, const FCFSAPIFileContext *fctx); int fcfs_api_open_by_inode_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi, const int64_t inode, const int flags, const FCFSAPIFileContext *fctx); int fcfs_api_open_by_dentry_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi, const FDIRDEntryInfo *dentry, const int flags, const FCFSAPIFileContext *fctx); static inline int fcfs_api_create_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi, const char *path, const FCFSAPIFileContext *fctx) { const int flags = O_CREAT | O_TRUNC | O_WRONLY; return fcfs_api_open_ex(ctx, fi, path, flags, fctx); } int fcfs_api_close(FCFSAPIFileInfo *fi); void fcfs_api_file_write_done_callback( FSAPIWriteDoneCallbackArg *callback_arg); int fcfs_api_pwrite_ex(FCFSAPIFileInfo *fi, const char *buff, const int size, const int64_t offset, int *written_bytes, const int64_t tid); int fcfs_api_write_ex(FCFSAPIFileInfo *fi, const char *buff, const int size, int *written_bytes, const int64_t tid); int fcfs_api_pread_ex(FCFSAPIFileInfo *fi, char *buff, const int size, const int64_t offset, int *read_bytes, const int64_t tid); int fcfs_api_read_ex(FCFSAPIFileInfo *fi, char *buff, const int size, int *read_bytes, const int64_t tid); int fcfs_api_pwritev_ex(FCFSAPIFileInfo *fi, const struct iovec *iov, const int iovcnt, const int64_t offset, int *written_bytes, const int64_t tid); int fcfs_api_writev_ex(FCFSAPIFileInfo *fi, const struct iovec *iov, const int iovcnt, int *written_bytes, const int64_t tid); int fcfs_api_preadv_ex(FCFSAPIFileInfo *fi, const struct iovec *iov, const int iovcnt, const int64_t offset, int *read_bytes, const int64_t tid); int fcfs_api_readv_ex(FCFSAPIFileInfo *fi, const struct iovec *iov, const int iovcnt, int *read_bytes, const int64_t tid); int fcfs_api_file_truncate_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const int64_t new_size, const int64_t tid, FDIRDEntryInfo *dentry); int fcfs_api_ftruncate_ex(FCFSAPIFileInfo *fi, const int64_t new_size, const int64_t tid); int fcfs_api_truncate_ex(FCFSAPIContext *ctx, const char *path, const int64_t new_size, const FCFSAPIFileContext *fctx); int fcfs_api_fallocate_ex(FCFSAPIFileInfo *fi, const int mode, const int64_t offset, const int64_t len, const int64_t tid); static inline int fcfs_api_unlink_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const int64_t tid) { const int flags = FDIR_UNLINK_FLAGS_MATCH_FILE; return fcfs_api_remove_dentry_ex(ctx, path, flags, tid); } int fcfs_api_lseek(FCFSAPIFileInfo *fi, const int64_t offset, const int whence); void fcfs_api_fill_stat(const FDIRDEntryInfo *dentry, struct stat *stat); int fcfs_api_fstat(FCFSAPIFileInfo *fi, struct stat *buf); int fcfs_api_lstat_ex(FCFSAPIContext *ctx, const char *path, const FDIRDentryOperator *oper, struct stat *buf); int fcfs_api_stat_ex(FCFSAPIContext *ctx, const char *path, const FDIRDentryOperator *oper, struct stat *buf, const int flags); int fcfs_api_flock_ex2(FCFSAPIFileInfo *fi, const int operation, const int64_t owner_id, const pid_t pid); static inline int fcfs_api_flock_ex(FCFSAPIFileInfo *fi, const int operation, const int64_t owner_id) { return fcfs_api_flock_ex2(fi, operation, owner_id, getpid()); } static inline int fcfs_api_flock(FCFSAPIFileInfo *fi, const int operation) { return fcfs_api_flock_ex2(fi, operation, (long)pthread_self(), getpid()); } int fcfs_api_getlk_ex(FCFSAPIFileInfo *fi, struct flock *lock, int64_t *owner_id); static inline int fcfs_api_getlk(FCFSAPIFileInfo *fi, struct flock *lock) { int64_t owner_id; return fcfs_api_getlk_ex(fi, lock, &owner_id); } int fcfs_api_setlk_ex(FCFSAPIFileInfo *fi, const struct flock *lock, const int64_t owner_id, const bool blocked); static inline int fcfs_api_setlk(FCFSAPIFileInfo *fi, const struct flock *lock) { const bool blocked = false; int64_t owner_id; owner_id = fc_gettid(); return fcfs_api_setlk_ex(fi, lock, owner_id, blocked); } static inline int fcfs_api_setlkw(FCFSAPIFileInfo *fi, const struct flock *lock) { const bool blocked = true; int64_t owner_id; owner_id = fc_gettid(); return fcfs_api_setlk_ex(fi, lock, owner_id, blocked); } int fcfs_api_rename_ex(FCFSAPIContext *ctx, const char *old_path, const char *new_path, const int flags, const FCFSAPIFileContext *fctx); int fcfs_api_symlink_ex(FCFSAPIContext *ctx, const char *target, const char *path, const FDIRDentryOperator *oper, const mode_t mode); int fcfs_api_readlink(FCFSAPIContext *ctx, const char *path, const FDIRDentryOperator *oper, char *buff, const int size); int fcfs_api_link_ex(FCFSAPIContext *ctx, const char *old_path, const char *new_path, const FDIRDentryOperator *oper, const mode_t mode, const int flags); int fcfs_api_mknod_ex(FCFSAPIContext *ctx, const char *path, const FDIRDentryOperator *oper, const mode_t mode, const dev_t dev); int fcfs_api_mkfifo_ex(FCFSAPIContext *ctx, const char *path, FDIRDentryOperator *oper, const mode_t mode); int fcfs_api_mkdir_ex(FCFSAPIContext *ctx, const char *path, FDIRDentryOperator *oper, const mode_t mode); int fcfs_api_access_ex(FCFSAPIContext *ctx, const char *path, const int mask, const FDIRDentryOperator *oper, const int flags); int fcfs_api_euidaccess_ex(FCFSAPIContext *ctx, const char *path, const int mask, const FDIRDentryOperator *oper, const int flags); static inline int fcfs_api_eaccess_ex(FCFSAPIContext *ctx, const char *path, const int mask, const FDIRDentryOperator *oper, const int flags) { return fcfs_api_euidaccess_ex(ctx, path, mask, oper, flags); } int fcfs_api_statvfs_ex(FCFSAPIContext *ctx, const char *path, struct statvfs *stbuf); int fcfs_api_set_file_flags(FCFSAPIFileInfo *fi, const int flags); static inline int fcfs_api_fdatasync(FCFSAPIFileInfo *fi, const int64_t tid) { fs_api_datasync(fi->ctx->contexts.fsapi, fi->dentry.inode, tid); return 0; } static inline int fcfs_api_fsync(FCFSAPIFileInfo *fi, const int64_t tid) { fs_api_datasync(fi->ctx->contexts.fsapi, fi->dentry.inode, tid); if (fi->ctx->async_report.enabled) { inode_htable_check_conflict_and_wait(fi->dentry.inode); } return 0; } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/fcfs_api_types.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_API_TYPES_H #define _FCFS_API_TYPES_H #include #include #include #include "fastcommon/fast_mblock.h" #include "fastcommon/fast_buffer.h" #include "fastdir/client/fdir_client.h" #include "fastsore/api/fs_api.h" #define FCFS_FUSE_DEFAULT_CONFIG_FILENAME "/etc/fastcfs/fcfs/fuse.conf" typedef enum { fcfs_api_owner_type_caller, fcfs_api_owner_type_fixed } FCFSAPIOwnerType; typedef struct fcfs_api_owner_info { FCFSAPIOwnerType type; FDIRDentryOperator oper; } FCFSAPIOwnerInfo; typedef struct fcfs_api_ns_mountpoint_holder { char *ns; char *mountpoint; } FCFSAPINSMountpointHolder; typedef struct fcfs_api_opendir_session { FDIRClientDentryArray array; int btype; //buffer type FastBuffer buffer; } FCFSAPIOpendirSession; typedef struct fcfs_api_context { /* whether FCFSAPIFileInfo object persist additional gids */ bool persist_additional_gids; bool use_sys_lock_for_append; struct { bool enabled; bool busy_polling; //for RDMA } rdma; struct { bool enabled; int interval_ms; int shared_allocator_count; int hashtable_sharding_count; int64_t hashtable_total_capacity; } async_report; string_t ns; //namespace char ns_holder[NAME_MAX]; FCFSAPIOwnerInfo owner; struct { FDIRClientContext *fdir; FSAPIContext *fsapi; } contexts; struct fast_mblock_man opendir_session_pool; } FCFSAPIContext; typedef struct fcfs_api_file_info { FCFSAPIContext *ctx; FDIRDentryOperator oper; int64_t tid; struct { FDIRClientSession flock; FCFSAPIOpendirSession *opendir; } sessions; FDIRDEntryInfo dentry; int flags; int magic; struct { int last_modified_time; } write_notify; int64_t offset; //current offset char fixed_groups_buff[256]; //for additional gids } FCFSAPIFileInfo; typedef struct fcfs_api_file_context { FDIRDentryOperator oper; mode_t mode; int64_t tid; } FCFSAPIFileContext; typedef struct fcfs_api_write_done_callback_extra_data { FCFSAPIContext *ctx; int64_t file_size; int64_t space_end; int last_modified_time; } FCFSAPIWriteDoneCallbackExtraData; typedef struct fcfs_api_write_done_callback_arg { FSAPIWriteDoneCallbackArg arg; //must be the first FCFSAPIWriteDoneCallbackExtraData extra; } FCFSAPIWriteDoneCallbackArg; struct fcfs_api_inode_hentry; struct fcfs_api_insert_event_context; typedef struct fcfs_api_waiting_task { pthread_lock_cond_pair_t lcp; //for notify bool finished; struct fast_mblock_man *allocator; //for free struct fcfs_api_waiting_task *next; //for event waitings queue } FCFSAPIWaitingTask; typedef enum { fcfs_api_event_type_report, fcfs_api_event_type_notify } FCFSAPIEventType; typedef struct fcfs_api_async_report_event { FCFSAPIEventType type; int id; //used by async_reporter for stable sort FDIRSetDEntrySizeInfo dsize; struct fcfs_api_inode_hentry *inode_hentry; struct { FCFSAPIWaitingTask *head; //use lock of inode sharding } waitings; struct fc_list_head dlink; //for inode sharding htable struct fast_mblock_man *allocator; //for free struct fcfs_api_async_report_event *next; //for async_reporter's queue } FCFSAPIAsyncReportEvent; typedef struct fcfs_api_async_report_event_ptr_array { int alloc; int count; FCFSAPIAsyncReportEvent **events; } FCFSAPIAsyncReportEventPtrArray; #define FCFS_API_SET_OPERATOR(_oper, _owner, _uid, _gid) \ do { \ if ((_owner).type == fcfs_api_owner_type_fixed) { \ _oper = (_owner).oper; \ } else { \ FDIR_SET_OPERATOR(_oper, _uid, _gid, 0, NULL); \ } \ } while (0) #define FCFS_API_SET_FCTX(fctx, _oper, _mode, _tid) \ fctx.oper = _oper; \ fctx.mode = _mode; \ fctx.tid = _tid #ifdef __cplusplus extern "C" { #endif extern FCFSAPIContext g_fcfs_api_ctx; #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/fcfs_api_util.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/sched_thread.h" #include "fcfs_api_util.h" int fcfs_api_remove_dentry_by_pname_ex(FCFSAPIContext *ctx, const FDIRClientOperPnamePair *opname, const int flags, const int64_t tid) { FDIRDEntryInfo dentry; int result; if ((result=fdir_client_remove_dentry_by_pname_ex( ctx->contexts.fdir, &ctx->ns, opname, flags, &dentry)) != 0) { return result; } if (S_ISREG(dentry.stat.mode) && dentry.stat.nlink == 0 && !ctx->contexts.fdir->trash_bin_enabled) { result = fs_api_unlink_file(ctx->contexts.fsapi, dentry.inode, dentry.stat.space_end, tid); } return result; } int fcfs_api_remove_dentry_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const int flags, const int64_t tid) { FDIRDEntryInfo dentry; int result; if ((result=fdir_client_remove_dentry_ex(ctx->contexts.fdir, path, flags, &dentry)) != 0) { return result; } if (S_ISREG(dentry.stat.mode) && dentry.stat.nlink == 0 && !ctx->contexts.fdir->trash_bin_enabled) { result = fs_api_unlink_file(ctx->contexts.fsapi, dentry.inode, dentry.stat.space_end, tid); } return result; } int fcfs_api_rename_dentry_by_pname_ex(FCFSAPIContext *ctx, const int64_t src_parent_inode, const string_t *src_name, const int64_t dest_parent_inode, const string_t *dest_name, const FDIRDentryOperator *oper, const int flags, const int64_t tid) { FDIRDEntryPName src_pname; FDIRDEntryPName dest_pname; FDIRDEntryInfo dentry; FDIRDEntryInfo *pe; int result; FDIR_SET_DENTRY_PNAME_PTR(&src_pname, src_parent_inode, src_name); FDIR_SET_DENTRY_PNAME_PTR(&dest_pname, dest_parent_inode, dest_name); pe = &dentry; if ((result=fdir_client_rename_dentry_by_pname_ex(ctx->contexts.fdir, &ctx->ns, &src_pname, &ctx->ns, &dest_pname, oper, flags, &pe)) != 0) { return result; } if (pe != NULL && S_ISREG(pe->stat.mode) && pe->stat.nlink == 0 && !ctx->contexts.fdir->trash_bin_enabled) { fs_api_unlink_file(ctx->contexts.fsapi, pe->inode, pe->stat.space_end, tid); } return result; } int fcfs_api_rename_dentry_ex(FCFSAPIContext *ctx, const char *path1, const char *path2, const FDIRDentryOperator *oper, const int flags, const int64_t tid) { FDIRDEntryFullName src; FDIRDEntryFullName dest; FDIRDEntryInfo dentry; FDIRDEntryInfo *pe; int result; FCFSAPI_SET_PATH_FULLNAME(src, ctx, path1); FCFSAPI_SET_PATH_FULLNAME(dest, ctx, path2); pe = &dentry; if ((result=fdir_client_rename_dentry_ex(ctx->contexts.fdir, &src, &dest, oper, flags, &pe)) != 0) { return result; } if (pe != NULL && S_ISREG(pe->stat.mode) && pe->stat.nlink == 0 && !ctx->contexts.fdir->trash_bin_enabled) { fs_api_unlink_file(ctx->contexts.fsapi, pe->inode, pe->stat.space_end, tid); } return result; } ================================================ FILE: src/api/fcfs_api_util.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_API_UTIL_H #define _FCFS_API_UTIL_H #include #include #include "fastcommon/logger.h" #include "fcfs_api_types.h" #include "inode_htable.h" #include "async_reporter.h" #ifdef __cplusplus extern "C" { #endif #define FCFSAPI_SET_PATH_FULLNAME(fullname, ctx, path_str) \ fullname.ns = (ctx)->ns; \ FC_SET_STRING(fullname.path, (char *)path_str) #define FCFSAPI_SET_PATH_OPER_FNAME_EX(fname, ctx, \ _uid, _gid, _gcount, _glist, path_str) \ FDIR_SET_OPERATOR(fname.oper, _uid, _gid, _gcount, _glist); \ FCFSAPI_SET_PATH_FULLNAME(fname.fullname, ctx, path_str) #define FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, _oper, path_str) \ fname.oper = _oper; \ FCFSAPI_SET_PATH_FULLNAME(fname.fullname, ctx, path_str) #define FCFSAPI_SET_PATH_OPER_PNAME_EX(opname, _uid, _gid, \ _gcount, _glist, parent_inode, name) \ FDIR_SET_OPERATOR(opname.oper, _uid, _gid, _gcount, _glist); \ FDIR_SET_DENTRY_PNAME_PTR(&opname.pname, parent_inode, name) #define FCFSAPI_SET_PATH_OPER_PNAME(opname, _oper, parent_inode, name) \ opname.oper = _oper; \ FDIR_SET_DENTRY_PNAME_PTR(&opname.pname, parent_inode, name) #define FCFSAPI_SET_PATH_OPER_PNAME1(opname, _uid, _gid, \ _gcount, _glist, parent_inode, name) \ FDIR_SET_OPERATOR(opname.oper, _uid, _gid, _gcount, _glist); \ FDIR_SET_DENTRY_PNAME_STR(&opname.pname, parent_inode, name) #define FCFSAPI_SET_OPER_INODE_PAIR_EX(oino, \ _uid, _gid, _gcount, _glist, _inode) \ FDIR_SET_OPERATOR(fname.oper, _uid, _gid, _gcount, _glist); \ oino.inode = _inode #define FCFSAPI_SET_OPER_INODE_PAIR(oino, _oper, _inode) \ oino.oper = _oper; \ oino.inode = _inode #define fcfs_api_lookup_inode_by_path(path, oper, inode) \ fcfs_api_lookup_inode_by_path_ex(&g_fcfs_api_ctx, path, \ oper, LOG_DEBUG, inode) #define fcfs_api_stat_dentry_by_path(path, flags, dentry) \ fcfs_api_stat_dentry_by_path_ex(&g_fcfs_api_ctx, \ path, flags, LOG_DEBUG, dentry) #define fcfs_api_stat_dentry_by_fullname(fullname, flags, dentry) \ fcfs_api_stat_dentry_by_fullname_ex(&g_fcfs_api_ctx, \ fullname, flags, LOG_DEBUG, dentry) #define fcfs_api_stat_dentry_by_inode(inode, flags, dentry) \ fcfs_api_stat_dentry_by_inode_ex(&g_fcfs_api_ctx, inode, flags, dentry) #define fcfs_api_stat_dentry_by_pname(opname, flags, dentry) \ fcfs_api_stat_dentry_by_pname_ex(&g_fcfs_api_ctx, opname, \ flags, LOG_DEBUG, dentry) #define fcfs_api_access_dentry_by_path(path, mask, flags, dentry) \ fcfs_api_access_dentry_by_path_ex(&g_fcfs_api_ctx, \ path, mask, flags, dentry) #define fcfs_api_access_dentry_by_inode(oino, mask, flags, dentry) \ fcfs_api_access_dentry_by_inode_ex(&g_fcfs_api_ctx, \ oino, mask, flags, dentry) #define fcfs_api_access_dentry_by_pname(opname, mask, flags, dentry) \ fcfs_api_access_dentry_by_pname_ex(&g_fcfs_api_ctx, \ opname, mask, flags, dentry) #define fcfs_api_create_dentry_by_pname(parent_inode, name, oper, rdev, dentry) \ fcfs_api_create_dentry_by_pname_ex(&g_fcfs_api_ctx, \ parent_inode, name, oper, rdev, dentry) #define fcfs_api_symlink_dentry_by_pname(link, parent_inode, name, oper, dentry)\ fcfs_api_symlink_dentry_by_pname_ex(&g_fcfs_api_ctx, link, \ parent_inode, name, oper, dentry) #define fcfs_api_readlink_by_pname(parent_inode, name, link, size) \ fcfs_api_readlink_by_pname_ex(&g_fcfs_api_ctx, parent_inode, \ name, link, size) #define fcfs_api_readlink_by_inode(inode, link, size) \ fcfs_api_readlink_by_inode_ex(&g_fcfs_api_ctx, inode, link, size) #define fcfs_api_link_dentry_by_pname(src_inode, dest_parent_inode, \ dest_name, oper, flags, dentry) \ fcfs_api_link_dentry_by_pname_ex(&g_fcfs_api_ctx, src_inode, \ dest_parent_inode, dest_name, oper, flags, dentry) #define fcfs_api_remove_dentry_by_pname(opname, flags, tid) \ fcfs_api_remove_dentry_by_pname_ex(&g_fcfs_api_ctx, \ opname, flags, tid) #define fcfs_api_remove_dentry(path, flags, tid) \ fcfs_api_remove_dentry_ex(&g_fcfs_api_ctx, path, flags, tid) #define fcfs_api_rename_dentry_by_pname(src_parent_inode, src_name, \ dest_parent_inode, dest_name, oper, flags, tid) \ fcfs_api_rename_dentry_by_pname_ex(&g_fcfs_api_ctx, src_parent_inode, \ src_name, dest_parent_inode, dest_name, oper, flags, tid) #define fcfs_api_rename_dentry(path1, path2, flags, tid) \ fcfs_api_rename_dentry_ex(&g_fcfs_api_ctx, \ path1, path2, flags, tid) #define fcfs_api_modify_stat_by_inode(oino, attr, mflags, flags, dentry) \ fcfs_api_modify_stat_by_inode_ex(&g_fcfs_api_ctx, \ oino, attr, mflags, flags, dentry) #define fcfs_api_set_xattr_by_inode(inode, xattr, flags) \ fcfs_api_set_xattr_by_inode_ex(&g_fcfs_api_ctx, inode, xattr, flags) #define fcfs_api_set_xattr_by_path(path, xattr, flags) \ fcfs_api_set_xattr_by_path_ex(&g_fcfs_api_ctx, path, xattr, flags) #define fcfs_api_remove_xattr_by_inode(inode, name, flags) \ fcfs_api_remove_xattr_by_inode_ex(&g_fcfs_api_ctx, inode, name, flags) #define fcfs_api_remove_xattr_by_path(path, name, flags) \ fcfs_api_remove_xattr_by_path_ex(&g_fcfs_api_ctx, path, name, flags) #define fcfs_api_get_xattr_by_inode(inode, name, value, size, flags) \ fcfs_api_get_xattr_by_inode_ex(&g_fcfs_api_ctx, inode, \ name, LOG_DEBUG, value, size, flags) #define fcfs_api_get_xattr_by_path(path, name, value, size, flags) \ fcfs_api_get_xattr_by_path_ex(&g_fcfs_api_ctx, path, \ name, LOG_DEBUG, value, size, flags) #define fcfs_api_list_xattr_by_inode(inode, list, size, flags) \ fcfs_api_list_xattr_by_inode_ex(&g_fcfs_api_ctx, inode, list, size, flags) #define fcfs_api_list_xattr_by_path(path, list, size, flags) \ fcfs_api_list_xattr_by_path_ex(&g_fcfs_api_ctx, path, list, size, flags) #define fcfs_api_list_dentry_by_inode(inode, array) \ fcfs_api_list_dentry_by_inode_ex(&g_fcfs_api_ctx, inode, array) #define fcfs_api_list_compact_dentry_by_path(path, array) \ fcfs_api_list_compact_dentry_by_path_ex(&g_fcfs_api_ctx, path, array) #define fcfs_api_list_compact_dentry_by_inode(inode, array) \ fcfs_api_list_compact_dentry_by_inode_ex(&g_fcfs_api_ctx, inode, array) #define fcfs_api_alloc_opendir_session() \ fcfs_api_alloc_opendir_session_ex(&g_fcfs_api_ctx) #define fcfs_api_free_opendir_session(session) \ fcfs_api_free_opendir_session_ex(&g_fcfs_api_ctx, session) #define fcfs_api_dentry_sys_lock(session, inode, flags, file_size, space_end) \ fcfs_api_dentry_sys_lock_ex(&g_fcfs_api_ctx, session, inode, \ flags, file_size, space_end) static inline int fcfs_api_lookup_inode_by_fullname_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const int enoent_log_level, int64_t *inode) { return fdir_client_lookup_inode_by_path_ex(ctx->contexts.fdir, path, enoent_log_level, inode); } static inline int fcfs_api_lookup_inode_by_path_ex(FCFSAPIContext *ctx, const char *path, const FDIRDentryOperator *oper, const int enoent_log_level, int64_t *inode) { FDIRClientOperFnamePair fname; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path); return fdir_client_lookup_inode_by_path_ex(ctx->contexts.fdir, &fname, enoent_log_level, inode); } static inline int fcfs_api_stat_dentry_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const int flags, FDIRDEntryInfo *dentry) { if (ctx->async_report.enabled) { inode_htable_check_conflict_and_wait(oino->inode); } return fdir_client_stat_dentry_by_inode(ctx->contexts.fdir, &ctx->ns, oino, flags, dentry); } static inline int fcfs_api_stat_dentry_by_fullname_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const int flags, const int enoent_log_level, FDIRDEntryInfo *dentry) { if (ctx->async_report.enabled) { int result; FDIRClientOperInodePair oino; if ((result=fcfs_api_lookup_inode_by_fullname_ex(ctx, path, enoent_log_level, &oino.inode)) != 0) { return result; } oino.oper = path->oper; return fcfs_api_stat_dentry_by_inode_ex(ctx, &oino, flags, dentry); } else { return fdir_client_stat_dentry_by_path_ex(ctx->contexts.fdir, path, flags, enoent_log_level, dentry); } } static inline int fcfs_api_stat_dentry_by_path_ex(FCFSAPIContext *ctx, const char *path, const FDIRDentryOperator *oper, const int flags, const int enoent_log_level, FDIRDEntryInfo *dentry) { FDIRClientOperFnamePair fname; FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path); return fcfs_api_stat_dentry_by_fullname_ex(ctx, &fname, flags, enoent_log_level, dentry); } static inline int fcfs_api_stat_dentry_by_pname_ex(FCFSAPIContext *ctx, const FDIRClientOperPnamePair *opname, const int flags, const int enoent_log_level, FDIRDEntryInfo *dentry) { if (ctx->async_report.enabled) { int result; FDIRClientOperInodePair oino; if ((result=fdir_client_lookup_inode_by_pname_ex(ctx->contexts.fdir, &ctx->ns, opname, enoent_log_level, &oino.inode)) != 0) { return result; } oino.oper = opname->oper; return fcfs_api_stat_dentry_by_inode_ex(ctx, &oino, flags, dentry); } else { return fdir_client_stat_dentry_by_pname_ex(ctx->contexts.fdir, &ctx->ns, opname, flags, enoent_log_level, dentry); } } static inline int fcfs_api_access_dentry_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const char mask, const int flags, FDIRDEntryInfo *dentry) { if (ctx->async_report.enabled && (flags & FDIR_FLAGS_OUTPUT_DENTRY)) { inode_htable_check_conflict_and_wait(oino->inode); } return fdir_client_access_dentry_by_inode(ctx->contexts.fdir, &ctx->ns, oino, mask, flags, dentry); } static inline int fcfs_api_access_dentry_by_path_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const char mask, const int flags, FDIRDEntryInfo *dentry) { if (ctx->async_report.enabled && (flags & FDIR_FLAGS_OUTPUT_DENTRY)) { int result; FDIRClientOperInodePair oino; if ((result=fcfs_api_lookup_inode_by_fullname_ex(ctx, path, LOG_NOTHING, &oino.inode)) != 0) { return result; } oino.oper = path->oper; return fcfs_api_access_dentry_by_inode_ex( ctx, &oino, mask, flags, dentry); } return fdir_client_access_dentry_by_path(ctx-> contexts.fdir, path, mask, flags, dentry); } static inline int fcfs_api_access_dentry_by_pname_ex(FCFSAPIContext *ctx, const FDIRClientOperPnamePair *opname, const char mask, const int flags, FDIRDEntryInfo *dentry) { if (ctx->async_report.enabled && (flags & FDIR_FLAGS_OUTPUT_DENTRY)) { int result; FDIRClientOperInodePair oino; if ((result=fdir_client_lookup_inode_by_pname_ex(ctx->contexts.fdir, &ctx->ns, opname, LOG_NOTHING, &oino.inode)) != 0) { return result; } oino.oper = opname->oper; return fcfs_api_access_dentry_by_inode_ex( ctx, &oino, mask, flags, dentry); } return fdir_client_access_dentry_by_pname(ctx->contexts.fdir, &ctx->ns, opname, mask, flags, dentry); } static inline int fcfs_api_create_dentry_by_pname_ex(FCFSAPIContext *ctx, const int64_t parent_inode, const string_t *name, const FDIRDentryOperator *oper, const mode_t mode, const dev_t rdev, FDIRDEntryInfo *dentry) { FDIRClientOperPnamePair opname; FCFSAPI_SET_PATH_OPER_PNAME(opname, *oper, parent_inode, name); return fdir_client_create_dentry_by_pname_ex(ctx->contexts.fdir, &ctx->ns, &opname, mode, rdev, dentry); } static inline int fcfs_api_symlink_dentry_by_pname_ex(FCFSAPIContext *ctx, const string_t *link, const int64_t parent_inode, const string_t *name, const FDIRDentryOperator *oper, const mode_t mode, FDIRDEntryInfo *dentry) { FDIRClientOperPnamePair opname; FCFSAPI_SET_PATH_OPER_PNAME(opname, *oper, parent_inode, name); return fdir_client_symlink_dentry_by_pname(ctx->contexts.fdir, link, &ctx->ns, &opname, mode, dentry); } static inline int fcfs_api_readlink_by_pname_ex(FCFSAPIContext *ctx, const FDIRClientOperPnamePair *opname, string_t *link, const int size) { return fdir_client_readlink_by_pname(ctx->contexts.fdir, &ctx->ns, opname, link, size); } static inline int fcfs_api_readlink_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, string_t *link, const int size) { return fdir_client_readlink_by_inode(ctx->contexts.fdir, &ctx->ns, oino, link, size); } int fcfs_api_remove_dentry_by_pname_ex(FCFSAPIContext *ctx, const FDIRClientOperPnamePair *opname, const int flags, const int64_t tid); int fcfs_api_remove_dentry_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const int flags, const int64_t tid); int fcfs_api_rename_dentry_by_pname_ex(FCFSAPIContext *ctx, const int64_t src_parent_inode, const string_t *src_name, const int64_t dest_parent_inode, const string_t *dest_name, const FDIRDentryOperator *oper, const int flags, const int64_t tid); int fcfs_api_rename_dentry_ex(FCFSAPIContext *ctx, const char *path1, const char *path2, const FDIRDentryOperator *oper, const int flags, const int64_t tid); static inline int fcfs_api_modify_stat_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const struct stat *attr, const int64_t mflags, const int flags, FDIRDEntryInfo *dentry) { FDIRDEntryStat stat; if (ctx->async_report.enabled) { inode_htable_check_conflict_and_wait(oino->inode); } stat.mode = attr->st_mode; stat.gid = attr->st_gid; stat.uid = attr->st_uid; stat.atime = attr->st_atime; stat.ctime = attr->st_ctime; stat.mtime = attr->st_mtime; stat.size = attr->st_size; return fdir_client_modify_stat_by_inode(ctx->contexts.fdir, &ctx->ns, oino, mflags, &stat, flags, dentry); } #define FCFS_API_SET_UTIMES(stat, options, times) \ do { \ options.flags = 0; \ options.atime = options.mtime = 1; \ memset(&stat, 0, sizeof(stat)); \ if (times == NULL) { \ stat.atime = stat.mtime = get_current_time(); \ } else { \ stat.atime = times[0].tv_sec; \ stat.mtime = times[1].tv_sec; \ } \ } while (0) #define SET_ONE_UTIMENS(var, option_flag, option_now, tp) \ do { \ if (tp.tv_nsec == UTIME_NOW) { \ var = get_current_time(); \ option_flag = 1; \ option_now = 1; \ } else if (tp.tv_nsec == UTIME_OMIT) { \ option_flag = 0; \ } else { \ var = tp.tv_sec; \ option_flag = 1; \ } \ } while (0) #define FCFS_API_SET_UTIMENS(stat, options, times) \ do { \ options.flags = 0; \ memset(&stat, 0, sizeof(stat)); \ if (times == NULL) { \ stat.atime = stat.mtime = get_current_time(); \ } else { \ SET_ONE_UTIMENS(stat.atime, options.atime, \ options.atime_now, times[0]); \ SET_ONE_UTIMENS(stat.mtime, options.mtime, \ options.mtime_now, times[1]); \ } \ } while (0) static inline int fcfs_api_utimes_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const struct timeval times[2], const int flags) { FDIRStatModifyFlags options; FDIRDEntryStat stat; FDIRDEntryInfo dentry; if (ctx->async_report.enabled) { inode_htable_check_conflict_and_wait(oino->inode); } FCFS_API_SET_UTIMES(stat, options, times); return fdir_client_modify_stat_by_inode(ctx->contexts.fdir, &ctx->ns, oino, options.flags, &stat, flags, &dentry); } static inline int fcfs_api_utimes_by_path_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const struct timeval times[2], const int flags) { FDIRStatModifyFlags options; FDIRDEntryStat stat; FDIRDEntryInfo dentry; FCFS_API_SET_UTIMES(stat, options, times); return fdir_client_modify_stat_by_path(ctx->contexts.fdir, path, options.flags, &stat, flags, &dentry); } static inline int fcfs_api_utimens_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const struct timespec times[2], const int flags) { FDIRStatModifyFlags options; FDIRDEntryStat stat; FDIRDEntryInfo dentry; if (ctx->async_report.enabled) { inode_htable_check_conflict_and_wait(oino->inode); } FCFS_API_SET_UTIMENS(stat, options, times); if (options.flags == 0) { return 0; } else { return fdir_client_modify_stat_by_inode(ctx->contexts.fdir, &ctx->ns, oino, options.flags, &stat, flags, &dentry); } } static inline int fcfs_api_utimens_by_path_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const struct timespec times[2], const int flags) { FDIRStatModifyFlags options; FDIRDEntryStat stat; FDIRDEntryInfo dentry; FCFS_API_SET_UTIMENS(stat, options, times); if (options.flags == 0) { return 0; } else { return fdir_client_modify_stat_by_path(ctx->contexts.fdir, path, options.flags, &stat, flags, &dentry); } } static inline int fcfs_api_utime_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const struct utimbuf *times) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; FDIRStatModifyFlags options; FDIRDEntryStat stat; FDIRDEntryInfo dentry; options.flags = 0; options.atime = options.mtime = 1; memset(&stat, 0, sizeof(stat)); if (times == NULL) { stat.atime = stat.mtime = get_current_time(); } else { stat.atime = times->actime; stat.mtime = times->modtime; } return fdir_client_modify_stat_by_path(ctx->contexts.fdir, path, options.flags, &stat, flags, &dentry); } static inline int fcfs_api_chown_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const uid_t uid, const gid_t gid, const int flags) { FDIRStatModifyFlags options; FDIRDEntryStat stat; FDIRDEntryInfo dentry; options.flags = 0; options.uid = options.gid = 1; memset(&stat, 0, sizeof(stat)); stat.uid = uid; stat.gid = gid; return fdir_client_modify_stat_by_inode(ctx->contexts.fdir, &ctx->ns, oino, options.flags, &stat, flags, &dentry); } static inline int fcfs_api_chown_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const uid_t uid, const gid_t gid, const int flags) { FDIRStatModifyFlags options; FDIRDEntryStat stat; FDIRDEntryInfo dentry; options.flags = 0; options.uid = options.gid = 1; memset(&stat, 0, sizeof(stat)); stat.uid = uid; stat.gid = gid; return fdir_client_modify_stat_by_path(ctx->contexts.fdir, path, options.flags, &stat, flags, &dentry); } static inline int fcfs_api_chmod_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const mode_t mode, const int flags) { FDIRStatModifyFlags options; FDIRDEntryStat stat; FDIRDEntryInfo dentry; options.flags = 0; options.mode = 1; memset(&stat, 0, sizeof(stat)); stat.mode = (mode & ALLPERMS); return fdir_client_modify_stat_by_inode(ctx->contexts.fdir, &ctx->ns, oino, options.flags, &stat, flags, &dentry); } static inline int fcfs_api_chmod_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const mode_t mode, const int flags) { FDIRStatModifyFlags options; FDIRDEntryStat stat; FDIRDEntryInfo dentry; options.flags = 0; options.mode = 1; memset(&stat, 0, sizeof(stat)); stat.mode = (mode & ALLPERMS); return fdir_client_modify_stat_by_path(ctx->contexts.fdir, path, options.flags, &stat, flags, &dentry); } static inline int convert_xattr_flags(const int flags) { int new_flags; if (flags == 0) { return 0; } new_flags = (flags & FDIR_FLAGS_FOLLOW_SYMLINK); if ((flags & XATTR_CREATE) != 0) { return (new_flags | FDIR_FLAGS_XATTR_CREATE); } else if ((flags & XATTR_REPLACE) != 0) { return (new_flags | FDIR_FLAGS_XATTR_REPLACE); } else { return new_flags; } } static inline int fcfs_api_set_xattr_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const key_value_pair_t *xattr, const int flags) { return fdir_client_set_xattr_by_inode(ctx->contexts.fdir, &ctx->ns, oino, xattr, convert_xattr_flags(flags)); } static inline int fcfs_api_set_xattr_by_path_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const key_value_pair_t *xattr, const int flags) { return fdir_client_set_xattr_by_path(ctx->contexts.fdir, path, xattr, convert_xattr_flags(flags)); } static inline int fcfs_api_remove_xattr_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const string_t *name, const int flags) { return fdir_client_remove_xattr_by_inode_ex(ctx->contexts.fdir, &ctx->ns, oino, name, flags, LOG_DEBUG); } static inline int fcfs_api_remove_xattr_by_path_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const string_t *name, const int flags) { return fdir_client_remove_xattr_by_path_ex(ctx->contexts.fdir, path, name, flags, LOG_DEBUG); } static inline int fcfs_api_get_xattr_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, const string_t *name, const int enoattr_log_level, string_t *value, const int size, const int flags) { return fdir_client_get_xattr_by_inode_ex(ctx->contexts.fdir, &ctx->ns, oino, name, enoattr_log_level, value, size, flags); } static inline int fcfs_api_get_xattr_by_path_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, const string_t *name, const int enoattr_log_level, string_t *value, const int size, const int flags) { return fdir_client_get_xattr_by_path_ex(ctx->contexts.fdir, path, name, enoattr_log_level, value, size, flags); } static inline int fcfs_api_list_xattr_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, string_t *list, const int size, const int flags) { return fdir_client_list_xattr_by_inode(ctx->contexts.fdir, &ctx->ns, oino, list, size, flags); } static inline int fcfs_api_list_xattr_by_path_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, string_t *list, const int size, const int flags) { return fdir_client_list_xattr_by_path(ctx->contexts.fdir, path, list, size, flags); } static inline int fcfs_api_list_dentry_by_inode_ex(FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, FDIRClientDentryArray *array) { const int flags = FDIR_LIST_DENTRY_FLAGS_OUTPUT_SPECIAL; if (ctx->async_report.enabled) { async_reporter_wait_all(oino->inode); } return fdir_client_list_dentry_by_inode(ctx->contexts.fdir, &ctx->ns, oino, array, flags); } static inline int fcfs_api_list_compact_dentry_by_path_ex(FCFSAPIContext *ctx, const FDIRClientOperFnamePair *path, FDIRClientCompactDentryArray *array) { return fdir_client_list_compact_dentry_by_path( ctx->contexts.fdir, path, array); } static inline int fcfs_api_list_compact_dentry_by_inode_ex( FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino, FDIRClientCompactDentryArray *array) { return fdir_client_list_compact_dentry_by_inode( ctx->contexts.fdir, &ctx->ns, oino, array); } static inline FCFSAPIOpendirSession *fcfs_api_alloc_opendir_session_ex( FCFSAPIContext *ctx) { return (FCFSAPIOpendirSession *)fast_mblock_alloc_object( &ctx->opendir_session_pool); } static inline void fcfs_api_free_opendir_session_ex( FCFSAPIContext *ctx, FCFSAPIOpendirSession *session) { fast_mblock_free_object(&ctx->opendir_session_pool, session); } static inline int fcfs_api_dentry_sys_lock_ex(FCFSAPIContext *ctx, FDIRClientSession *session, const int64_t inode, const int flags, int64_t *file_size, int64_t *space_end) { int result; if ((result=fdir_client_init_session(ctx->contexts.fdir, session)) != 0) { return result; } return fdir_client_dentry_sys_lock(session, &ctx->ns, inode, flags, file_size, space_end); } static inline int fcfs_api_dentry_sys_unlock(FDIRClientSession *session, const string_t *ns, const int64_t old_size, const FDIRSetDEntrySizeInfo *dsize) { int result; result = fdir_client_dentry_sys_unlock_ex(session, ns, old_size, dsize); fdir_client_close_session(session, result != 0); return result; } static inline int fcfs_api_link_dentry_by_pname_ex(FCFSAPIContext *ctx, const int64_t src_inode, const int64_t dest_parent_inode, const string_t *dest_name, const FDIRDentryOperator *oper, const mode_t mode, const int flags, FDIRDEntryInfo *dentry) { FDIRClientOperPnamePair dest_opname; FCFSAPI_SET_PATH_OPER_PNAME(dest_opname, *oper, dest_parent_inode, dest_name); return fdir_client_link_dentry_by_pname(ctx->contexts.fdir, src_inode, &ctx->ns, &dest_opname, mode, flags, dentry); } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/inode_htable.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "fcfs_api_allocator.h" #include "async_reporter.h" #include "inode_htable.h" static SFHtableShardingContext inode_sharding_ctx; typedef struct fcfs_api_insert_event_context { FCFSAPIAsyncReportEvent *event; } FCFSAPIInsertEventContext; typedef struct fcfs_api_find_callback_arg { FCFSAPIAllocatorContext *allocator_ctx; FCFSAPIWaitingTask *waiting_task; } FCFSAPIFindCallbackArg; static int inode_htable_insert_callback(SFShardingHashEntry *he, void *arg, const bool new_create) { FCFSAPIInodeHEntry *inode; FCFSAPIInsertEventContext *ictx; inode = (FCFSAPIInodeHEntry *)he; ictx = (FCFSAPIInsertEventContext *)arg; if (new_create) { FC_INIT_LIST_HEAD(&inode->head); } ictx->event->inode_hentry = inode; fc_list_add(&ictx->event->dlink, &inode->head); return 0; } static void *inode_htable_find_callback(SFShardingHashEntry *he, void *arg) { FCFSAPIInodeHEntry *inode; FCFSAPIFindCallbackArg *farg; FCFSAPIAsyncReportEvent *event; inode = (FCFSAPIInodeHEntry *)he; event = fc_list_first_entry(&inode->head, FCFSAPIAsyncReportEvent, dlink); if (event == NULL) { return NULL; } farg = (FCFSAPIFindCallbackArg *)arg; farg->waiting_task = (FCFSAPIWaitingTask *)fast_mblock_alloc_object( &farg->allocator_ctx->waiting_task); if (farg->waiting_task == NULL) { return NULL; } //add to event waiting queue farg->waiting_task->next = event->waitings.head; event->waitings.head = farg->waiting_task; return event; } static bool inode_htable_accept_reclaim_callback(SFShardingHashEntry *he) { return fc_list_empty(&((FCFSAPIInodeHEntry *)he)->head); } int inode_htable_init(const int sharding_count, const int64_t htable_capacity, const int allocator_count, int64_t element_limit, const int64_t min_ttl_sec, const int64_t max_ttl_sec) { return sf_sharding_htable_init(&inode_sharding_ctx, sf_sharding_htable_key_ids_one, inode_htable_insert_callback, inode_htable_find_callback, NULL, inode_htable_accept_reclaim_callback, sharding_count, htable_capacity, allocator_count, sizeof(FCFSAPIInodeHEntry), element_limit, min_ttl_sec, max_ttl_sec); } int inode_htable_insert(FCFSAPIAsyncReportEvent *event) { SFTwoIdsHashKey key; FCFSAPIInsertEventContext ictx; key.oid = event->dsize.inode; ictx.event = event; return sf_sharding_htable_insert(&inode_sharding_ctx, &key, &ictx); } int inode_htable_check_conflict_and_wait(const uint64_t inode) { SFTwoIdsHashKey key; FCFSAPIFindCallbackArg callback_arg; key.oid = inode; callback_arg.allocator_ctx = fcfs_api_allocator_get(inode); callback_arg.waiting_task = NULL; if (sf_sharding_htable_find(&inode_sharding_ctx, &key, &callback_arg) == NULL) { return 0; } if (callback_arg.waiting_task != NULL) { async_reporter_notify(); //logInfo("oid: %"PRId64", wait_report_done_and_release", inode); fcfs_api_wait_report_done_and_release(callback_arg.waiting_task); } return 0; } ================================================ FILE: src/api/inode_htable.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _INODE_HTABLE_H #define _INODE_HTABLE_H #include "fcfs_api_types.h" #include "sf/sf_sharding_htable.h" typedef struct fcfs_api_inode_hentry { SFShardingHashEntry hentry; //must be the first struct fc_list_head head; //element: FCFSAPIAsyncReportEvent } FCFSAPIInodeHEntry; #ifdef __cplusplus extern "C" { #endif int inode_htable_init(const int sharding_count, const int64_t htable_capacity, const int allocator_count, int64_t element_limit, const int64_t min_ttl_sec, const int64_t max_ttl_sec); int inode_htable_insert(FCFSAPIAsyncReportEvent *event); int inode_htable_check_conflict_and_wait(const uint64_t inode); static inline void fcfs_api_notify_waiting_task(FCFSAPIWaitingTask *task) { PTHREAD_MUTEX_LOCK(&task->lcp.lock); task->finished = true; pthread_cond_signal(&task->lcp.cond); PTHREAD_MUTEX_UNLOCK(&task->lcp.lock); } static inline void fcfs_api_wait_report_done_and_release( FCFSAPIWaitingTask *waiting_task) { PTHREAD_MUTEX_LOCK(&waiting_task->lcp.lock); while (!waiting_task->finished) { pthread_cond_wait(&waiting_task->lcp.cond, &waiting_task->lcp.lock); } waiting_task->finished = false; //reset for next use PTHREAD_MUTEX_UNLOCK(&waiting_task->lcp.lock); fast_mblock_free_object(waiting_task->allocator, waiting_task); } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/std/api_types.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_POSIX_API_TYPES_H #define _FCFS_POSIX_API_TYPES_H #include "fastcommon/fc_list.h" #include "fastcommon/pthread_func.h" #include "../fcfs_api.h" #ifndef OS_LINUX typedef off_t off64_t; #endif #define FCFS_POSIX_API_FD_BASE (2 << 28) struct dirent; typedef int (*fcfs_dir_filter_func)(const struct dirent *ent); typedef int (*fcfs_dir_compare_func)(const struct dirent **ent1, const struct dirent **ent2); typedef struct fcfs_posix_api_context { FCFSAPINSMountpointHolder nsmp; string_t mountpoint; FCFSAPIContext api_ctx; } FCFSPosixAPIContext; typedef enum { fcfs_papi_tpid_type_tid, fcfs_papi_tpid_type_pid } FCFSPosixAPITPIDType; typedef struct fcfs_posix_api_file_info { string_t filename; int fd; FCFSPosixAPITPIDType tpid_type; //use pid or tid FCFSAPIFileInfo fi; } FCFSPosixAPIFileInfo; typedef struct fcfs_posix_file_ptr_array { volatile FCFSPosixAPIFileInfo **files; volatile int count; } FCFSPosixFilePtrArray; typedef struct fcfs_posix_api_dir { FCFSPosixAPIFileInfo *file; FDIRClientCompactDentryArray darray; int magic; int offset; } FCFSPosixAPIDIR; typedef struct fcfs_posix_capi_file { int magic; int fd; int error_no; int eof; pthread_mutex_t lock; struct fc_list_head dlink; //for opened files chain } FCFSPosixCAPIFILE; typedef struct fcfs_posix_api_global_vars { FCFSPosixAPIContext ctx; string_t *cwd; } FCFSPosixAPIGlobalVars; #define FCFS_PAPI_IS_MY_FD(fd) \ (fd > FCFS_POSIX_API_FD_BASE) #endif ================================================ FILE: src/api/std/capi.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fastcommon/locked_list.h" #include "papi.h" #include "capi.h" static FCLockedList capi_opend_files; #define FCFS_CAPI_MAGIC_NUMBER 1645431088 #ifdef OS_LINUX #define FCFS_POS_OFFSET(pos) (pos)->__pos #else #define FCFS_POS_OFFSET(pos) *(pos) #endif #define FCFS_CAPI_CONVERT_FP_EX(fp, ...) \ FCFSPosixCAPIFILE *file; \ file = (FCFSPosixCAPIFILE *)fp; \ if (file->magic != FCFS_CAPI_MAGIC_NUMBER) { \ errno = EBADF; \ return __VA_ARGS__; \ } #define FCFS_CAPI_CONVERT_FP(fp) \ FCFS_CAPI_CONVERT_FP_EX(fp, -1) #define FCFS_CAPI_CONVERT_FP_VOID(fp) \ FCFS_CAPI_CONVERT_FP_EX(fp) int fcfs_capi_init() { return locked_list_init(&capi_opend_files); } void fcfs_capi_destroy() { fcfs_fcloseall(); locked_list_destroy(&capi_opend_files); } static FILE *alloc_file_handle(int fd) { FCFSPosixCAPIFILE *file; int result; if ((file=fc_malloc(sizeof(FCFSPosixCAPIFILE))) == NULL) { errno = ENOMEM; return NULL; } if ((result=init_pthread_lock(&file->lock)) != 0) { free(file); errno = result; return NULL; } file->fd = fd; file->magic = FCFS_CAPI_MAGIC_NUMBER; file->error_no = 0; file->eof = 0; locked_list_add_tail(&file->dlink, &capi_opend_files); return (FILE *)file; } static inline int free_file_handle(FCFSPosixCAPIFILE *file) { int result; locked_list_del(&file->dlink, &capi_opend_files); if (fcfs_close(file->fd) == 0) { result = 0; } else { result = EOF; } file->magic = 0; free(file); return result; } static int mode_to_flags(const char *mode, int *flags) { const char *p; const char *end; int len; len = strlen(mode); if (len == 0) { return EINVAL; } p = mode; switch (*p++) { case 'r': if (*p == '+') { *flags = O_RDWR; p++; } else { *flags = O_RDONLY; } break; case 'w': if (*p == '+') { *flags = O_RDWR | O_CREAT | O_TRUNC; p++; } else { *flags = O_WRONLY | O_CREAT | O_TRUNC; } break; case 'a': if (*p == '+') { *flags = O_RDWR | O_CREAT | O_APPEND; p++; } else { *flags = O_WRONLY | O_CREAT | O_APPEND; } break; default: return EINVAL; } end = mode + len; while (p < end) { switch (*p) { case 'e': *flags |= O_CLOEXEC; break; case'x': *flags |= O_EXCL; break; default: break; } ++p; } return 0; } static int check_flags(const char *mode, const int flags, int *adding_flags) { const char *p; const char *end; int rwflags; int len; len = strlen(mode); if (len == 0) { return EINVAL; } *adding_flags = 0; p = mode; switch (*p++) { case 'r': if (*p == '+') { rwflags = O_RDWR; p++; } else { rwflags = O_RDONLY; } break; case 'a': *adding_flags = O_APPEND; case 'w': if (*p == '+') { rwflags = O_RDWR; p++; } else { rwflags = O_WRONLY; } break; default: return EINVAL; } if ((flags & rwflags) != rwflags) { return EINVAL; } end = mode + len; while (p < end) { switch (*p) { case 'e': *adding_flags |= O_CLOEXEC; break; case'x': //just ignore break; default: break; } ++p; } return 0; } static inline int do_fdopen(FCFSPosixAPIContext *ctx, const char *path, const char *mode) { int flags; int result; if ((result=mode_to_flags(mode, &flags)) != 0) { errno = result; return -1; } return fcfs_file_open(ctx, path, flags, 0666, fcfs_papi_tpid_type_pid); } void fcfs_flockfile(FILE *fp) { FCFS_CAPI_CONVERT_FP_VOID(fp); PTHREAD_MUTEX_LOCK(&file->lock); } int fcfs_ftrylockfile(FILE *fp) { int result; FCFS_CAPI_CONVERT_FP_EX(fp, -1); if ((result=pthread_mutex_trylock(&file->lock)) != 0) { errno = result; return -1; } return 0; } void fcfs_funlockfile(FILE *fp) { FCFS_CAPI_CONVERT_FP_VOID(fp); PTHREAD_MUTEX_UNLOCK(&file->lock); } FILE *fcfs_fopen_ex(FCFSPosixAPIContext *ctx, const char *path, const char *mode) { int fd; if ((fd=do_fdopen(ctx, path, mode)) < 0) { return NULL; } return alloc_file_handle(fd); } FILE *fcfs_fdopen_ex(FCFSPosixAPIContext *ctx, int fd, const char *mode) { int flags; int adding_flags; int result; if ((flags=fcfs_fcntl(fd, F_GETFL)) < 0) { return NULL; } if ((result=check_flags(mode, flags, &adding_flags)) != 0) { errno = result; return NULL; } if (adding_flags != 0 && (flags & adding_flags) != adding_flags) { if (fcfs_fcntl(fd, F_SETFL, (flags | adding_flags)) != 0) { return NULL; } } return alloc_file_handle(fd); } FILE *fcfs_freopen_ex(FCFSPosixAPIContext *ctx, const char *path, const char *mode, FILE *fp) { int fd; FCFSPosixAPIFileInfo *finfo; FCFS_CAPI_CONVERT_FP_EX(fp, NULL); if (path == NULL) { if ((finfo=fcfs_get_file_handle(file->fd)) == NULL) { errno = EBADF; return NULL; } path = finfo->filename.str; } if ((fd=do_fdopen(ctx, path, mode)) < 0) { return NULL; } fcfs_close(file->fd); file->fd = fd; file->eof = 0; file->error_no = 0; return (FILE *)file; } int fcfs_fclose(FILE *fp) { FCFS_CAPI_CONVERT_FP_EX(fp, EOF); return free_file_handle(file); } int fcfs_fcloseall() { int result; int r; FCFSPosixCAPIFILE *file; result = 0; while (1) { locked_list_first_entry(&capi_opend_files, FCFSPosixCAPIFILE, dlink, file); if (file == NULL) { break; } if ((r=free_file_handle(file)) != 0) { result = r; } } return result; } int fcfs_setvbuf(FILE *fp, char *buf, int mode, size_t size) { switch (mode) { case _IOFBF: break; case _IOLBF: break; case _IONBF: break; default: break; } //alloc_file_buffer(FCFSPosixCAPIFILE *file, const int size); return 0; } int fcfs_fseek(FILE *fp, long offset, int whence) { FCFS_CAPI_CONVERT_FP(fp); return fcfs_lseek(file->fd, offset, whence) >= 0 ? 0 : -1; } int fcfs_fseeko(FILE *fp, off_t offset, int whence) { FCFS_CAPI_CONVERT_FP(fp); return fcfs_lseek(file->fd, offset, whence) >= 0 ? 0 : -1; } long fcfs_ftell(FILE *fp) { FCFS_CAPI_CONVERT_FP(fp); return fcfs_ltell(file->fd); } off_t fcfs_ftello(FILE *fp) { FCFS_CAPI_CONVERT_FP(fp); return fcfs_ltell(file->fd); } void fcfs_rewind(FILE *fp) { FCFS_CAPI_CONVERT_FP_VOID(fp); fcfs_lseek(file->fd, 0L, SEEK_SET); } int fcfs_fgetpos(FILE *fp, fpos_t *pos) { FCFS_CAPI_CONVERT_FP(fp); FCFS_POS_OFFSET(pos) = fcfs_ltell(file->fd); return FCFS_POS_OFFSET(pos) >= 0 ? 0 : -1; } int fcfs_fsetpos(FILE *fp, const fpos_t *pos) { FCFS_CAPI_CONVERT_FP(fp); return fcfs_lseek(file->fd, FCFS_POS_OFFSET(pos), SEEK_SET) >= 0 ? 0 : -1; } #define SET_FILE_ERROR(file, bytes) \ do { \ if (bytes < 0) { \ file->error_no = (errno != 0 ? errno : EIO); \ } else { \ file->eof = 1; \ } \ } while (0) static inline int do_fgetc(FILE *fp, const bool need_lock) { int bytes; int c; unsigned char buff[8]; FCFS_CAPI_CONVERT_FP_EX(fp, EOF); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } bytes = fcfs_read(file->fd, buff, 1); if (bytes > 0) { c = *buff; } else { SET_FILE_ERROR(file, bytes); c = EOF; } if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } return c; } static inline int do_fputc(int c, FILE *fp, const bool need_lock) { int bytes; char ch; FCFS_CAPI_CONVERT_FP_EX(fp, EOF); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } ch = c; bytes = fcfs_write(file->fd, &ch, 1); if (bytes <= 0) { file->error_no = (errno != 0 ? errno : EIO); ch = EOF; } if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } return ch; } static inline void do_clearerr(FILE *fp, const bool need_lock) { FCFS_CAPI_CONVERT_FP_VOID(fp); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } file->error_no = 0; file->eof = 0; if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } } static inline int do_feof(FILE *fp, const bool need_lock) { int eof; FCFS_CAPI_CONVERT_FP(fp); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } eof = file->eof; if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } return eof; } static inline int do_ferror(FILE *fp, const bool need_lock) { int error_no; FCFS_CAPI_CONVERT_FP(fp); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } error_no = file->error_no; if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } return error_no; } static inline int do_fileno(FILE *fp, const bool need_lock) { int fd; FCFS_CAPI_CONVERT_FP(fp); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } fd = file->fd; if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } return fd; } static inline int do_fflush(FILE *fp, const bool need_lock) { FCFS_CAPI_CONVERT_FP_EX(fp, EOF); return 0; } static inline size_t do_fread(void *buff, size_t size, size_t n, FILE *fp, const bool need_lock) { size_t count; FCFS_CAPI_CONVERT_FP_EX(fp, EOF); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } count = fcfs_file_read(file->fd, buff, size, n); if (count != n) { if (count < 0) { file->error_no = (errno != 0 ? errno : EIO); count = 0; } else if (count == 0) { file->eof = 1; } } if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } return count; } static inline size_t do_fwrite(const void *buff, size_t size, size_t n, FILE *fp, const bool need_lock) { size_t count; FCFS_CAPI_CONVERT_FP_EX(fp, EOF); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } count = fcfs_file_write(file->fd, buff, size, n); if (count != n) { if (count < 0) { file->error_no = (errno != 0 ? errno : EIO); count = 0; } else if (count == 0) { file->error_no = (errno != 0 ? errno : EIO); } } if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } return count; } static inline int do_fputs(const char *s, FILE *fp, const bool need_lock) { int len; int bytes; FCFS_CAPI_CONVERT_FP_EX(fp, EOF); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } len = strlen(s); if (len == 0) { bytes = 0; } else { bytes = fcfs_write(file->fd, s, len); if (bytes <= 0) { file->error_no = (errno != 0 ? errno : EIO); bytes = EOF; } } if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } return bytes; } static inline char *do_fgets(char *s, int size, FILE *fp, const bool need_lock) { size_t bytes; FCFS_CAPI_CONVERT_FP_EX(fp, NULL); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } bytes = fcfs_file_gets(file->fd, s, size); if (bytes < 0) { file->error_no = (errno != 0 ? errno : EIO); } else if (bytes == 0) { file->eof = 1; } if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } return (bytes > 0 ? s : NULL); } int fcfs_fgetc_unlocked(FILE *fp) { const bool need_lock = false; return do_fgetc(fp, need_lock); } int fcfs_fputc_unlocked(int c, FILE *fp) { const bool need_lock = false; return do_fputc(c, fp, need_lock); } void fcfs_clearerr_unlocked(FILE *fp) { const bool need_lock = false; return do_clearerr(fp, need_lock); } int fcfs_feof_unlocked(FILE *fp) { const bool need_lock = false; return do_feof(fp, need_lock); } int fcfs_ferror_unlocked(FILE *fp) { const bool need_lock = false; return do_ferror(fp, need_lock); } int fcfs_fileno_unlocked(FILE *fp) { const bool need_lock = false; return do_fileno(fp, need_lock); } int fcfs_fflush_unlocked(FILE *fp) { const bool need_lock = false; return do_fflush(fp, need_lock); } size_t fcfs_fread_unlocked(void *buff, size_t size, size_t n, FILE *fp) { const bool need_lock = false; return do_fread(buff, size, n, fp, need_lock); } size_t fcfs_fwrite_unlocked(const void *buff, size_t size, size_t n, FILE *fp) { const bool need_lock = false; return do_fwrite(buff, size, n, fp, need_lock); } int fcfs_fputs_unlocked(const char *s, FILE *fp) { const bool need_lock = false; return do_fputs(s, fp, need_lock); } char *fcfs_fgets_unlocked(char *s, int size, FILE *fp) { const bool need_lock = false; return do_fgets(s, size, fp, need_lock); } int fcfs_fgetc(FILE *fp) { const bool need_lock = true; return do_fgetc(fp, need_lock); } int fcfs_fputc(int c, FILE *fp) { const bool need_lock = true; return do_fputc(c, fp, need_lock); } void fcfs_clearerr(FILE *fp) { const bool need_lock = true; return do_clearerr(fp, need_lock); } int fcfs_feof(FILE *fp) { const bool need_lock = true; return do_feof(fp, need_lock); } int fcfs_ferror(FILE *fp) { const bool need_lock = true; return do_ferror(fp, need_lock); } int fcfs_fileno(FILE *fp) { const bool need_lock = true; return do_fileno(fp, need_lock); } int fcfs_fflush(FILE *fp) { const bool need_lock = true; return do_fflush(fp, need_lock); } size_t fcfs_fread(void *buff, size_t size, size_t n, FILE *fp) { const bool need_lock = true; return do_fread(buff, size, n, fp, need_lock); } size_t fcfs_fwrite(const void *buff, size_t size, size_t n, FILE *fp) { const bool need_lock = true; return do_fwrite(buff, size, n, fp, need_lock); } int fcfs_fputs(const char *s, FILE *fp) { const bool need_lock = true; return do_fputs(s, fp, need_lock); } char *fcfs_fgets(char *s, int size, FILE *fp) { const bool need_lock = true; return do_fgets(s, size, fp, need_lock); } int fcfs_ungetc(int c, FILE *fp) { FCFS_CAPI_CONVERT_FP_EX(fp, EOF); PTHREAD_MUTEX_LOCK(&file->lock); if (c == EOF) { file->error_no = EINVAL; } else { if (fcfs_lseek(file->fd, -1, SEEK_CUR) < 0) { file->error_no = (errno != 0 ? errno : EIO); c = EOF; } } PTHREAD_MUTEX_UNLOCK(&file->lock); return c; } int fcfs_fprintf(FILE *fp, const char *format, ...) { va_list ap; int bytes; FCFS_CAPI_CONVERT_FP(fp); va_start(ap, format); bytes = fcfs_vdprintf(file->fd, format, ap); va_end(ap); return bytes; } int fcfs_vfprintf(FILE *fp, const char *format, va_list ap) { FCFS_CAPI_CONVERT_FP(fp); return fcfs_vdprintf(file->fd, format, ap); } ssize_t fcfs_getdelim(char **line, size_t *size, int delim, FILE *fp) { FCFS_CAPI_CONVERT_FP(fp); return fcfs_file_getdelim(file->fd, line, size, delim); } static inline ssize_t do_readline(FILE *fp, char *buff, size_t size, const bool need_lock) { size_t bytes; FCFS_CAPI_CONVERT_FP(fp); if (need_lock) { PTHREAD_MUTEX_LOCK(&file->lock); } bytes = fcfs_file_readline(file->fd, buff, size); if (bytes < 0) { file->error_no = (errno != 0 ? errno : EIO); } else if (bytes == 0) { file->eof = 1; } if (need_lock) { PTHREAD_MUTEX_UNLOCK(&file->lock); } return bytes; } ssize_t fcfs_readline(FILE *fp, char *buff, size_t size) { const bool need_lock = true; return do_readline(fp, buff, size, need_lock); } ssize_t fcfs_readline_unlocked(FILE *fp, char *buff, size_t size) { const bool need_lock = false; return do_readline(fp, buff, size, need_lock); } ================================================ FILE: src/api/std/capi.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_CAPI_H #define _FCFS_CAPI_H #include "api_types.h" #define fcfs_fopen(path, mode) \ fcfs_fopen_ex(&G_FCFS_PAPI_CTX, path, mode) #define fcfs_fdopen(fd, mode) \ fcfs_fdopen_ex(&G_FCFS_PAPI_CTX, fd, mode) #define fcfs_freopen(path, mode, fp) \ fcfs_freopen_ex(&G_FCFS_PAPI_CTX, path, mode, fp) #ifdef __cplusplus extern "C" { #endif int fcfs_capi_init(); void fcfs_capi_destroy(); FILE *fcfs_fopen_ex(FCFSPosixAPIContext *ctx, const char *path, const char *mode); FILE *fcfs_fdopen_ex(FCFSPosixAPIContext *ctx, int fd, const char *mode); FILE *fcfs_freopen_ex(FCFSPosixAPIContext *ctx, const char *path, const char *mode, FILE *fp); int fcfs_fclose(FILE *fp); int fcfs_fcloseall(); void fcfs_flockfile(FILE *fp); int fcfs_ftrylockfile(FILE *fp); void fcfs_funlockfile(FILE *fp); int fcfs_fseek(FILE *fp, long offset, int whence); int fcfs_fseeko(FILE *fp, off_t offset, int whence); long fcfs_ftell(FILE *fp); off_t fcfs_ftello(FILE *fp); void fcfs_rewind(FILE *fp); int fcfs_fgetpos(FILE *fp, fpos_t *pos); int fcfs_fsetpos(FILE *fp, const fpos_t *pos); int fcfs_fgetc_unlocked(FILE *fp); int fcfs_fputc_unlocked(int c, FILE *fp); static inline int fcfs_getc_unlocked(FILE *fp) { return fcfs_fgetc_unlocked(fp); } static inline int fcfs_putc_unlocked(int c, FILE *fp) { return fcfs_fputc_unlocked(c, fp); } void fcfs_clearerr_unlocked(FILE *fp); int fcfs_feof_unlocked(FILE *fp); int fcfs_ferror_unlocked(FILE *fp); int fcfs_fileno_unlocked(FILE *fp); int fcfs_fflush_unlocked(FILE *fp); size_t fcfs_fread_unlocked(void *buff, size_t size, size_t n, FILE *fp); size_t fcfs_fwrite_unlocked(const void *buff, size_t size, size_t n, FILE *fp); //with terminating null byte ('\0') char *fcfs_fgets_unlocked(char *s, int size, FILE *fp); int fcfs_fputs_unlocked(const char *s, FILE *fp); void fcfs_clearerr(FILE *fp); int fcfs_feof(FILE *fp); int fcfs_ferror(FILE *fp); int fcfs_fileno(FILE *fp); int fcfs_fgetc(FILE *fp); //with terminating null byte ('\0') char *fcfs_fgets(char *s, int size, FILE *fp); int fcfs_getc(FILE *fp); int fcfs_ungetc(int c, FILE *fp); int fcfs_fputc(int c, FILE *fp); int fcfs_fputs(const char *s, FILE *fp); int fcfs_putc(int c, FILE *fp); size_t fcfs_fread(void *buff, size_t size, size_t nmemb, FILE *fp); size_t fcfs_fwrite(const void *buff, size_t size, size_t nmemb, FILE *fp); int fcfs_fprintf(FILE *fp, const char *format, ...) __gcc_attribute__ ((format (printf, 2, 3))); int fcfs_vfprintf(FILE *fp, const char *format, va_list ap); ssize_t fcfs_getdelim(char **line, size_t *size, int delim, FILE *fp); static inline ssize_t fcfs_getline(char **line, size_t *size, FILE *fp) { return fcfs_getdelim(line, size, '\n', fp); } //without terminating null byte ('\0') ssize_t fcfs_readline(FILE *fp, char *buff, size_t size); //without terminating null byte ('\0') ssize_t fcfs_readline_unlocked(FILE *fp, char *buff, size_t size); //TODO /* int fcfs_fscanf(FILE *fp, const char *format, ...) __gcc_attribute__ ((format (scanf, 2, 3))); int fcfs_vfscanf(FILE *fp, const char *format, va_list ap); */ int fcfs_setvbuf(FILE *fp, char *buf, int mode, size_t size); static inline void fcfs_setbuf(FILE *fp, char *buf) { fcfs_setvbuf(fp, buf, (buf != NULL ? _IOFBF : _IONBF), BUFSIZ); } static inline void fcfs_setbuffer(FILE *fp, char *buf, size_t size) { fcfs_setvbuf(fp, buf, (buf != NULL ? _IOFBF : _IONBF), size); } static inline void fcfs_setlinebuf(FILE *fp) { fcfs_setvbuf(fp, NULL, _IOLBF, 0); } int fcfs_fflush(FILE *fp); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/std/fd_manager.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fastcommon/fast_mblock.h" #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fd_manager.h" #define FILE_INFO_ALLOC_ONCE 1024 typedef struct fcfs_fd_manager_context { volatile int generation; volatile int in_realloc; volatile int next_fd; struct fast_mblock_man file_info_allocator; //element: FCFSPosixAPIFileInfo FCFSPosixFilePtrArray file_parray; } FCFSFDManagerContext; static FCFSFDManagerContext fd_manager_ctx; #define PARRAY_IN_REALLOC fd_manager_ctx.in_realloc #define PARRAY_GENERATION fd_manager_ctx.generation #define FINFO_ALLOCATOR fd_manager_ctx.file_info_allocator #define PAPI_NEXT_FD fd_manager_ctx.next_fd #define FILE_PARRAY fd_manager_ctx.file_parray static int file_parray_realloc() { FCFSPosixAPIFileInfo **new_files; volatile FCFSPosixAPIFileInfo **old_files; int new_count; int old_count; int bytes; if (!__sync_bool_compare_and_swap(&PARRAY_IN_REALLOC, 0, 1)) { return 0; } old_count = FC_ATOMIC_GET(FILE_PARRAY.count); new_count = old_count + FILE_INFO_ALLOC_ONCE; bytes = sizeof(FCFSPosixAPIFileInfo *) * new_count; new_files = (FCFSPosixAPIFileInfo **)fc_malloc(bytes); if (new_files == NULL) { __sync_bool_compare_and_swap(&PARRAY_IN_REALLOC, 1, 0); return ENOMEM; } memset(new_files, 0, bytes); old_files = FC_ATOMIC_GET(FILE_PARRAY.files); if (old_count > 0) { memcpy(new_files, old_files, old_count * sizeof(FCFSPosixAPIFileInfo *)); } __sync_bool_compare_and_swap(&FILE_PARRAY.files, old_files, (volatile FCFSPosixAPIFileInfo **)new_files); __sync_bool_compare_and_swap(&FILE_PARRAY.count, old_count, new_count); FC_ATOMIC_INC(PARRAY_GENERATION); __sync_bool_compare_and_swap(&PARRAY_IN_REALLOC, 1, 0); if (old_files != NULL) { usleep(100000); //delay free free(old_files); } return 0; } static int finfo_init_func(FCFSPosixAPIFileInfo *finfo, void *args) { int elt_count; finfo->fd = __sync_add_and_fetch(&PAPI_NEXT_FD, 1); elt_count = finfo->fd - FCFS_POSIX_API_FD_BASE; if (elt_count <= FILE_PARRAY.count) { return 0; } else { return file_parray_realloc(); } } int fcfs_fd_manager_init() { int result; __sync_add_and_fetch(&PAPI_NEXT_FD, FCFS_POSIX_API_FD_BASE); if ((result=fast_mblock_init_ex1(&FINFO_ALLOCATOR, "papi_file_info", sizeof(FCFSPosixAPIFileInfo), FILE_INFO_ALLOC_ONCE, 0, (fast_mblock_object_init_func)finfo_init_func, NULL, true)) != 0) { return result; } return 0; } void fcfs_fd_manager_destroy() { fast_mblock_destroy(&FINFO_ALLOCATOR); free(FILE_PARRAY.files); FILE_PARRAY.count = 0; } #define FCFS_FD_TO_INDEX(fd) ((fd - FCFS_POSIX_API_FD_BASE) - 1) static inline int set_file_info(const int index, FCFSPosixAPIFileInfo *old_finfo, FCFSPosixAPIFileInfo *new_finfo) { int old_generation; do { old_generation = FC_ATOMIC_GET(PARRAY_GENERATION); if (!__sync_bool_compare_and_swap(&FILE_PARRAY. files[index], old_finfo, new_finfo)) { return EBUSY; } } while (FC_ATOMIC_GET(PARRAY_IN_REALLOC) || old_generation != FC_ATOMIC_GET(PARRAY_GENERATION)); return 0; } FCFSPosixAPIFileInfo *fcfs_fd_manager_alloc(const char *filename) { FCFSPosixAPIFileInfo *finfo; if ((finfo=fast_mblock_alloc_object(&FINFO_ALLOCATOR)) == NULL) { return finfo; } finfo->filename.len = strlen(filename); finfo->filename.str = (char *)fc_malloc(finfo->filename.len + 2); if (finfo->filename.str == NULL) { fast_mblock_free_object(&FINFO_ALLOCATOR, finfo); return NULL; } memcpy(finfo->filename.str, filename, finfo->filename.len + 1); if (set_file_info(FCFS_FD_TO_INDEX(finfo->fd), NULL, finfo) == 0) { return finfo; } else { logError("file: "__FILE__", line: %d, " "set file info fail, filename: %s, fd: %d", __LINE__, filename, finfo->fd); free(finfo->filename.str); FC_SET_STRING_NULL(finfo->filename); fast_mblock_free_object(&FINFO_ALLOCATOR, finfo); return NULL; } } FCFSPosixAPIFileInfo *fcfs_fd_manager_get(const int fd) { int result; int index; FCFSPosixAPIFileInfo *finfo; index = FCFS_FD_TO_INDEX(fd); if (index >= 0 && index < FC_ATOMIC_GET(FILE_PARRAY.count)) { finfo = (FCFSPosixAPIFileInfo *)FC_ATOMIC_GET( FILE_PARRAY.files[index]); result = finfo != NULL ? 0 : ENOENT; } else { finfo = NULL; result = EOVERFLOW; } if (result != 0) { logError("file: "__FILE__", line: %d, " "get file info fail, fd: %d, errno: %d, error info: %s", __LINE__, fd, result, STRERROR(result)); } return finfo; } int fcfs_fd_manager_free(FCFSPosixAPIFileInfo *finfo) { int result; int index; index = FCFS_FD_TO_INDEX(finfo->fd); if (index >= 0 && index < FC_ATOMIC_GET(FILE_PARRAY.count)) { if (finfo == FC_ATOMIC_GET(FILE_PARRAY.files[index])) { result = set_file_info(index, finfo, NULL); } else { result = ENOENT; } } else { result = EOVERFLOW; } if (result == 0) { free(finfo->filename.str); FC_SET_STRING_NULL(finfo->filename); fast_mblock_free_object(&FINFO_ALLOCATOR, finfo); } else { logError("file: "__FILE__", line: %d, " "free file info fail, fd: %d, errno: %d, error info: %s", __LINE__, finfo->fd, result, STRERROR(result)); } return result; } ================================================ FILE: src/api/std/fd_manager.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_FD_MANAGER_H #define _FCFS_FD_MANAGER_H #include "api_types.h" #ifdef __cplusplus extern "C" { #endif int fcfs_fd_manager_init(); void fcfs_fd_manager_destroy(); FCFSPosixAPIFileInfo *fcfs_fd_manager_alloc(const char *filename); FCFSPosixAPIFileInfo *fcfs_fd_manager_get(const int fd); static inline void fcfs_fd_manager_normalize_path( FCFSPosixAPIFileInfo *finfo) { if (!(finfo->filename.len > 0 && finfo->filename.str [finfo->filename.len - 1] == '/')) { *(finfo->filename.str + finfo->filename.len++) = '/'; *(finfo->filename.str + finfo->filename.len) = '\0'; } } int fcfs_fd_manager_free(FCFSPosixAPIFileInfo *finfo); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/std/papi.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "posix_api.h" #include "papi.h" #define FCFS_PAPI_MAGIC_NUMBER 1644551636 static FCFSPosixAPIFileInfo *do_open_ex(FCFSPosixAPIContext *ctx, const char *path, const int flags, const int mode, const FCFSPosixAPITPIDType tpid_type) { FCFSPosixAPIFileInfo *file; FCFSAPIFileContext fctx; int result; if ((file=fcfs_fd_manager_alloc(path)) == NULL) { errno = ENOMEM; return NULL; } fcfs_posix_api_set_fctx(&fctx, ctx, mode, tpid_type); if ((result=fcfs_api_open_ex(&ctx->api_ctx, &file->fi, path, flags, &fctx)) != 0) { fcfs_fd_manager_free(file); errno = result; return NULL; } if (S_ISDIR(file->fi.dentry.stat.mode)) { fcfs_fd_manager_normalize_path(file); } return file; } static inline int do_open(FCFSPosixAPIContext *ctx, const char *path, const int flags, const int mode, const FCFSPosixAPITPIDType tpid_type) { FCFSPosixAPIFileInfo *file; file = do_open_ex(ctx, path, flags, mode, tpid_type); if (file != NULL) { file->tpid_type = tpid_type; return file->fd; } else { return -1; } } static inline char *do_getcwd(FCFSPosixAPIContext *ctx, string_t *cwd, size_t size, const int overflow_errno) { if (G_FCFS_PAPI_CWD == NULL) { if (ctx->mountpoint.len >= size) { errno = overflow_errno; cwd->len = 0; return NULL; } cwd->len = ctx->mountpoint.len; memcpy(cwd->str, ctx->mountpoint.str, ctx->mountpoint.len); *(cwd->str + cwd->len) = '\0'; return cwd->str; } if (ctx->mountpoint.len + G_FCFS_PAPI_CWD->len >= size) { errno = overflow_errno; cwd->len = 0; return NULL; } cwd->len = ctx->mountpoint.len + G_FCFS_PAPI_CWD->len; memcpy(cwd->str, ctx->mountpoint.str, ctx->mountpoint.len); memcpy(cwd->str + ctx->mountpoint.len, G_FCFS_PAPI_CWD->str, G_FCFS_PAPI_CWD->len); *(cwd->str + cwd->len) = '\0'; return cwd->str; } static inline int do_getcwd1(FCFSPosixAPIContext *ctx, string_t *cwd, size_t size) { if (do_getcwd(ctx, cwd, size, EOVERFLOW) == NULL) { return -1; } if (cwd->len == 0 || cwd->str[cwd->len - 1] != '/') { cwd->str[cwd->len++] = '/'; } return 0; } static inline int papi_resolve_path(FCFSPosixAPIContext *ctx, const char *func, const char **path, char *full_filename, const int size) { char cwd[PATH_MAX]; string_t from_string; string_t path_string; if (**path != '/') { from_string.str = cwd; if (do_getcwd1(ctx, &from_string, sizeof(cwd)) != 0) { return -1; } FC_SET_STRING(path_string, (char *)*path); normalize_path(&from_string, &path_string, full_filename, size); *path = full_filename; } FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, *path, func); *path += ctx->mountpoint.len; return 0; } static int papi_resolve_pathat(FCFSPosixAPIContext *ctx, const char *func, int fd, const char **path, char *full_filename, const int size) { FCFSPosixAPIFileInfo *file; char cwd[PATH_MAX]; string_t from_string; string_t path_string; if (fd == AT_FDCWD) { from_string.str = cwd; if (do_getcwd1(ctx, &from_string, sizeof(cwd)) != 0) { return -1; } FC_SET_STRING(path_string, (char *)*path); normalize_path(&from_string, &path_string, full_filename, size); FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, full_filename, func); *path = full_filename + ctx->mountpoint.len; return 0; } if (**path == '/') { FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, *path, func); *path += ctx->mountpoint.len; return 0; } if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } normalize_path1(file->filename.str, *path, full_filename, size); *path = full_filename; return 0; } int fcfs_open_ex(FCFSPosixAPIContext *ctx, const char *path, int flags, ...) { char full_fname[PATH_MAX]; va_list ap; int fd; int mode; int result; if ((result=papi_resolve_path(ctx, "open", &path, full_fname, sizeof(full_fname))) != 0) { return result; } va_start(ap, flags); mode = va_arg(ap, int); fd = do_open(ctx, path, flags, mode, fcfs_papi_tpid_type_tid); va_end(ap); return fd; } int fcfs_file_open(FCFSPosixAPIContext *ctx, const char *path, const int flags, const int mode, const FCFSPosixAPITPIDType tpid_type) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "open", &path, full_fname, sizeof(full_fname))) != 0) { return result; } return do_open(ctx, path, flags, mode, tpid_type); } int fcfs_openat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, int flags, ...) { char full_fname[PATH_MAX]; va_list ap; int mode; int new_fd; int result; if ((result=papi_resolve_pathat(ctx, "openat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } va_start(ap, flags); mode = va_arg(ap, int); new_fd = do_open(ctx, path, flags, mode, fcfs_papi_tpid_type_tid); va_end(ap); return new_fd; } int fcfs_creat_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode) { return fcfs_open_ex(ctx, path, O_CREAT | O_TRUNC | O_WRONLY, mode); } int fcfs_close(int fd) { FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } fcfs_api_close(&file->fi); fcfs_fd_manager_free(file); return 0; } int fcfs_fsync(int fd) { FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } return fcfs_api_fsync(&file->fi, fcfs_posix_api_gettid( file->tpid_type)); } int fcfs_fdatasync(int fd) { FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } return fcfs_api_fdatasync(&file->fi, fcfs_posix_api_gettid( file->tpid_type)); } static inline ssize_t do_write(FCFSPosixAPIFileInfo *file, const void *buff, size_t count) { int result; int write_bytes; if ((result=fcfs_api_write_ex(&file->fi, buff, count, &write_bytes, fcfs_posix_api_gettid(file->tpid_type))) != 0) { errno = result; return -1; } else { return write_bytes; } } ssize_t fcfs_write(int fd, const void *buff, size_t count) { FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } return do_write(file, buff, count); } ssize_t fcfs_file_write(int fd, const void *buff, size_t size, size_t n) { FCFSPosixAPIFileInfo *file; size_t expect; size_t bytes; size_t count; size_t remain; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } expect = size * n; bytes = do_write(file, buff, expect); if (bytes == expect) { count = n; } else if (bytes > 0) { count = bytes / size; remain = bytes - (size * count); fcfs_api_lseek(&file->fi, -1 * remain, SEEK_CUR); } else { count = 0; } return count; } ssize_t fcfs_pwrite(int fd, const void *buff, size_t count, off_t offset) { int result; int write_bytes; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_pwrite_ex(&file->fi, buff, count, offset, &write_bytes, fcfs_posix_api_gettid( file->tpid_type))) != 0) { errno = result; return -1; } else { return write_bytes; } } ssize_t fcfs_writev(int fd, const struct iovec *iov, int iovcnt) { int result; int write_bytes; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_writev_ex(&file->fi, iov, iovcnt, &write_bytes, fcfs_posix_api_gettid(file->tpid_type))) != 0) { errno = result; return -1; } else { return write_bytes; } } ssize_t fcfs_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset) { int result; int write_bytes; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_pwritev_ex(&file->fi, iov, iovcnt, offset, &write_bytes, fcfs_posix_api_gettid( file->tpid_type))) != 0) { errno = result; return -1; } else { return write_bytes; } } static inline ssize_t do_read(FCFSPosixAPIFileInfo *file, void *buff, size_t count) { int result; int read_bytes; if ((result=fcfs_api_read_ex(&file->fi, buff, count, &read_bytes, fcfs_posix_api_gettid(file->tpid_type))) != 0) { errno = result; return -1; } else { return read_bytes; } } ssize_t fcfs_read(int fd, void *buff, size_t count) { FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } return do_read(file, buff, count); } ssize_t fcfs_readahead(int fd, off64_t offset, size_t count) { FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } return 0; } ssize_t fcfs_file_read(int fd, void *buff, size_t size, size_t n) { FCFSPosixAPIFileInfo *file; size_t expect; size_t bytes; size_t count; size_t remain; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } expect = size * n; bytes = do_read(file, buff, expect); if (bytes == expect) { count = n; } else if (bytes > 0) { count = bytes / size; remain = bytes - (size * count); fcfs_api_lseek(&file->fi, -1 * remain, SEEK_CUR); } else { count = 0; } return count; } ssize_t fcfs_file_readline(int fd, char *s, size_t size) { FCFSPosixAPIFileInfo *file; char *p; char *end; char *ch; int read_bytes_once; int current; int bytes; int remain; if (size <= 0) { errno = EINVAL; return -1; } if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } read_bytes_once = 64; remain = size; p = s; while (1) { current = FC_MIN(remain, read_bytes_once); bytes = do_read(file, p, current); if (bytes < 0) { return -1; } else if (bytes == 0) { break; } end = p + bytes; ch = p; while (ch < end && *ch != '\n') { ++ch; } if (ch < end) { //found p = ++ch; //skip the new line remain = end - p; if (remain > 0) { fcfs_api_lseek(&file->fi, -1 * remain, SEEK_CUR); } break; } p = end; if (bytes < current) { //end of file break; } remain -= bytes; if (remain == 0) { break; } read_bytes_once *= 2; } return (p - s); } ssize_t fcfs_file_gets(int fd, char *s, size_t size) { ssize_t bytes; if ((bytes=fcfs_file_readline(fd, s, size - 1)) >= 0) { *(s + bytes) = '\0'; } return bytes; } ssize_t fcfs_file_getdelim(int fd, char **line, size_t *size, int delim) { FCFSPosixAPIFileInfo *file; char *buff; char *p; char *end; char *ch; ssize_t alloc; ssize_t bytes; ssize_t len; ssize_t remain; if (*line != NULL && *size < 0) { errno = EINVAL; return -1; } if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if (*size < 128) { *size = 128; buff = fc_malloc(*size); if (buff == NULL) { errno = ENOMEM; return -1; } if (*line != NULL) { free(*line); } *line = buff; } else { buff = *line; } remain = *size - 1; p = buff; while (1) { bytes = do_read(file, p, remain); if (bytes < 0) { return -1; } else if (bytes == 0) { break; } end = p + bytes; ch = p; while (ch < end && *ch != delim) { ++ch; } if (ch < end) { //found p = ++ch; //skip the new line remain = end - p; if (remain > 0) { fcfs_api_lseek(&file->fi, -1 * remain, SEEK_CUR); } break; } if (bytes < remain) { //end of file p = end; break; } len = end - buff; alloc = (*size) * 2; buff = fc_malloc(alloc); if (buff == NULL) { errno = ENOMEM; return -1; } memcpy(buff, *line, len); free(*line); *line = buff; *size = alloc; p = buff + len; remain = (*size) - len; } *p = '\0'; return (p - buff); } ssize_t fcfs_pread(int fd, void *buff, size_t count, off_t offset) { int result; int read_bytes; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_pread_ex(&file->fi, buff, count, offset, &read_bytes, fcfs_posix_api_gettid( file->tpid_type))) != 0) { errno = result; return -1; } else { return read_bytes; } } ssize_t fcfs_readv(int fd, const struct iovec *iov, int iovcnt) { int result; int read_bytes; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_readv_ex(&file->fi, iov, iovcnt, &read_bytes, fcfs_posix_api_gettid(file->tpid_type))) != 0) { errno = result; return -1; } else { return read_bytes; } } ssize_t fcfs_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset) { int result; int read_bytes; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_preadv_ex(&file->fi, iov, iovcnt, offset, &read_bytes, fcfs_posix_api_gettid( file->tpid_type))) != 0) { errno = result; return -1; } else { return read_bytes; } } off_t fcfs_lseek(int fd, off_t offset, int whence) { int result; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_lseek(&file->fi, offset, whence)) != 0) { errno = result; return -1; } else { return file->fi.offset; } } off_t fcfs_ltell(int fd) { FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } return file->fi.offset; } int fcfs_fallocate(int fd, int mode, off_t offset, off_t length) { int result; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_fallocate_ex(&file->fi, mode, offset, length, fcfs_posix_api_gettid(file->tpid_type))) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_truncate_ex(FCFSPosixAPIContext *ctx, const char *path, off_t length) { FCFSAPIFileContext fctx; char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "truncate", &path, full_fname, sizeof(full_fname))) != 0) { return result; } fcfs_posix_api_set_fctx(&fctx, ctx, ACCESSPERMS, fcfs_papi_tpid_type_tid); if ((result=fcfs_api_truncate_ex(&ctx->api_ctx, path, length, &fctx)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_ftruncate(int fd, off_t length) { int result; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_ftruncate_ex(&file->fi, length, fcfs_posix_api_gettid(file->tpid_type))) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_lstat_ex(FCFSPosixAPIContext *ctx, const char *path, struct stat *buf) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "lstat", &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_lstat_ex(&ctx->api_ctx, path, &ctx->api_ctx.owner.oper, buf)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_stat_ex(FCFSPosixAPIContext *ctx, const char *path, struct stat *buf) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "stat", &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_stat_ex(&ctx->api_ctx, path, &ctx-> api_ctx.owner.oper, buf, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_fstat(int fd, struct stat *buf) { int result; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_fstat(&file->fi, buf)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_fstatat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, struct stat *buf, int flags) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_pathat(ctx, "fstatat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_stat_ex(&ctx->api_ctx, path, &ctx->api_ctx.owner. oper, buf, ((flags & AT_SYMLINK_NOFOLLOW) != 0 ? 0 : FDIR_FLAGS_FOLLOW_SYMLINK))) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_flock(int fd, int operation) { FCFSPosixAPIFileInfo *file; int result; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_flock(&file->fi, operation)) != 0) { errno = result; return -1; } else { return 0; } } static int do_fcntl(FCFSPosixAPIFileInfo *file, int cmd, void *arg) { int64_t owner_id; struct flock *lock; int flags; int result; switch (cmd) { /* case F_DUPFD: errno = EOPNOTSUPP; return -1; */ case F_GETFD: case F_SETFD: return 0; case F_GETFL: return file->fi.flags; case F_SETFL: flags = (long)arg; result = fcfs_api_set_file_flags(&file->fi, flags); break; case F_GETLK: case F_SETLK: case F_SETLKW: lock = (struct flock *)arg; if (cmd == F_GETLK) { result = fcfs_api_getlk_ex(&file->fi, lock, &owner_id); } else { owner_id = fc_gettid(); result = fcfs_api_setlk_ex(&file->fi, lock, owner_id, (cmd == F_SETLKW)); } break; default: logError("file: "__FILE__", line: %d, " "unsupport fcntl cmd: %d", __LINE__, cmd); errno = EOPNOTSUPP; return -1; } if (result != 0) { errno = result; return -1; } else { return 0; } } int fcfs_fcntl(int fd, int cmd, ...) { FCFSPosixAPIFileInfo *file; va_list ap; void *arg; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } va_start(ap, cmd); arg = va_arg(ap, void *); va_end(ap); return do_fcntl(file, cmd, arg); } int fcfs_symlink_ex(FCFSPosixAPIContext *ctx, const char *link, const char *path) { char full_fname[PATH_MAX]; int result; if (*link == '/') { FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, link, "symlink"); link += ctx->mountpoint.len; } if ((result=papi_resolve_path(ctx, "symlink", &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_symlink_ex(&ctx->api_ctx, link, path, &ctx->api_ctx.owner.oper, ACCESSPERMS)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_symlinkat_ex(FCFSPosixAPIContext *ctx, const char *link, int fd, const char *path) { char full_fname[PATH_MAX]; int result; if (*link == '/') { FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, link, "symlinkat"); link += ctx->mountpoint.len; } if ((result=papi_resolve_pathat(ctx, "symlinkat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_symlink_ex(&ctx->api_ctx, link, path, &ctx->api_ctx.owner.oper, ACCESSPERMS)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_link_ex(FCFSPosixAPIContext *ctx, const char *path1, const char *path2) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; char full_fname1[PATH_MAX]; char full_fname2[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "linkat", &path1, full_fname1, sizeof(full_fname1))) != 0) { return result; } if ((result=papi_resolve_path(ctx, "linkat", &path2, full_fname2, sizeof(full_fname2))) != 0) { return result; } if ((result=fcfs_api_link_ex(&ctx->api_ctx, path1, path2, &ctx-> api_ctx.owner.oper, ACCESSPERMS, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_linkat_ex(FCFSPosixAPIContext *ctx, int fd1, const char *path1, int fd2, const char *path2, int flags) { char full_fname1[PATH_MAX]; char full_fname2[PATH_MAX]; int result; if ((result=papi_resolve_pathat(ctx, "linkat", fd1, &path1, full_fname1, sizeof(full_fname1))) != 0) { return result; } if ((result=papi_resolve_pathat(ctx, "linkat", fd2, &path2, full_fname2, sizeof(full_fname2))) != 0) { return result; } if ((result=fcfs_api_link_ex(&ctx->api_ctx, path1, path2, &ctx->api_ctx.owner.oper, ACCESSPERMS, ((flags & AT_SYMLINK_FOLLOW) != 0) ? FDIR_FLAGS_FOLLOW_SYMLINK : 0)) != 0) { errno = result; return -1; } else { return 0; } } ssize_t fcfs_readlink_ex(FCFSPosixAPIContext *ctx, const char *path, char *buff, size_t size) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "readlink", &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_readlink(&ctx->api_ctx, path, &ctx-> api_ctx.owner.oper, buff, size)) != 0) { errno = result; return -1; } else { return 0; } } ssize_t fcfs_readlinkat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, char *buff, size_t size) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_pathat(ctx, "readlinkat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_readlink(&ctx->api_ctx, path, &ctx-> api_ctx.owner.oper, buff, size)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_mknod_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode, dev_t dev) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "mknod", &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_mknod_ex(&ctx->api_ctx, path, &ctx-> api_ctx.owner.oper, mode, dev)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_mknodat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, mode_t mode, dev_t dev) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_pathat(ctx, "mknodat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_mknod_ex(&ctx->api_ctx, path, &ctx-> api_ctx.owner.oper, mode, dev)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_mkfifo_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "mkfifo", &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_mkfifo_ex(&ctx->api_ctx, path, &ctx->api_ctx.owner.oper, mode)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_mkfifoat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, mode_t mode) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_pathat(ctx, "mkfifoat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_mkfifo_ex(&ctx->api_ctx, path, &ctx->api_ctx.owner.oper, mode)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_access_ex(FCFSPosixAPIContext *ctx, const char *path, int mode) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "access", &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_access_ex(&ctx->api_ctx, path, mode, &ctx->api_ctx.owner.oper, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_faccessat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, int mode, int flags) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_pathat(ctx, "faccessat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_access_ex(&ctx->api_ctx, path, mode, &ctx->api_ctx.owner.oper, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : FDIR_FLAGS_FOLLOW_SYMLINK)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_euidaccess_ex(FCFSPosixAPIContext *ctx, const char *path, int mode) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "euidaccess", &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_euidaccess_ex(&ctx->api_ctx, path, mode, &ctx->api_ctx.owner.oper, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_utime_ex(FCFSPosixAPIContext *ctx, const char *path, const struct utimbuf *times) { char full_fname[PATH_MAX]; int result; FDIRClientOperFnamePair fname; if ((result=papi_resolve_path(ctx, "utime", &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_utime_ex(&ctx->api_ctx, &fname, times)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_utimes_ex(FCFSPosixAPIContext *ctx, const char *path, const struct timeval times[2]) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; char full_fname[PATH_MAX]; int result; FDIRClientOperFnamePair fname; if ((result=papi_resolve_path(ctx, "utimes", &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_utimes_by_path_ex(&ctx->api_ctx, &fname, times, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_futimes_ex(FCFSPosixAPIContext *ctx, int fd, const struct timeval times[2]) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; FCFSPosixAPIFileInfo *file; FDIRClientOperInodePair oino; int result; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } FCFSAPI_SET_OPER_INODE_PAIR(oino, ctx->api_ctx. owner.oper, file->fi.dentry.inode); if ((result=fcfs_api_utimes_by_inode_ex(&ctx->api_ctx, &oino, times, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_futimesat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, const struct timeval times[2]) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; char full_fname[PATH_MAX]; int result; FDIRClientOperFnamePair fname; if ((result=papi_resolve_pathat(ctx, "futimesat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_utimes_by_path_ex(&ctx->api_ctx, &fname, times, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_futimens_ex(FCFSPosixAPIContext *ctx, int fd, const struct timespec times[2]) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; FCFSPosixAPIFileInfo *file; int result; FDIRClientOperInodePair oino; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } FCFSAPI_SET_OPER_INODE_PAIR(oino, ctx->api_ctx. owner.oper, file->fi.dentry.inode); if ((result=fcfs_api_utimens_by_inode_ex(&ctx->api_ctx, &oino, times, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_utimensat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, const struct timespec times[2], int flags) { char full_fname[PATH_MAX]; int result; FDIRClientOperFnamePair fname; if ((result=papi_resolve_pathat(ctx, "futimensat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_utimens_by_path_ex(&ctx->api_ctx, &fname, times, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : FDIR_FLAGS_FOLLOW_SYMLINK)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_unlink_ex(FCFSPosixAPIContext *ctx, const char *path) { char full_fname[PATH_MAX]; int result; FDIRClientOperFnamePair fname; if ((result=papi_resolve_path(ctx, "unlink", &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_unlink_ex(&ctx->api_ctx, &fname, fcfs_posix_api_gettid(fcfs_papi_tpid_type_tid))) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_unlinkat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, int flags) { char full_fname[PATH_MAX]; int result; FDIRClientOperFnamePair fname; if ((result=papi_resolve_pathat(ctx, "unlinkat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_remove_dentry_ex(&ctx->api_ctx, &fname, ((flags & AT_REMOVEDIR) ? FDIR_UNLINK_FLAGS_MATCH_DIR : FDIR_UNLINK_FLAGS_MATCH_FILE), fcfs_posix_api_gettid( fcfs_papi_tpid_type_tid))) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_rename_ex(FCFSPosixAPIContext *ctx, const char *path1, const char *path2) { const int flags = 0; int result; char full_fname1[PATH_MAX]; char full_fname2[PATH_MAX]; if ((result=papi_resolve_path(ctx, "rename", &path1, full_fname1, sizeof(full_fname1))) != 0) { return result; } if ((result=papi_resolve_path(ctx, "rename", &path2, full_fname2, sizeof(full_fname2))) != 0) { return result; } if ((result=fcfs_api_rename_dentry_ex(&ctx->api_ctx, path1, path2, &ctx->api_ctx.owner.oper, flags, fcfs_posix_api_gettid(fcfs_papi_tpid_type_tid))) != 0) { errno = result; return -1; } else { return 0; } } static inline int do_renameat(FCFSPosixAPIContext *ctx, const char *func_name, int fd1, const char *path1, int fd2, const char *path2, const int flags) { int result; char full_fname1[PATH_MAX]; char full_fname2[PATH_MAX]; if ((result=papi_resolve_pathat(ctx, func_name, fd1, &path1, full_fname1, sizeof(full_fname1))) != 0) { return result; } if ((result=papi_resolve_pathat(ctx, func_name, fd2, &path2, full_fname2, sizeof(full_fname2))) != 0) { return result; } if ((result=fcfs_api_rename_dentry_ex(&ctx->api_ctx, path1, path2, &ctx->api_ctx.owner.oper, flags, fcfs_posix_api_gettid(fcfs_papi_tpid_type_tid))) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_renameat_ex(FCFSPosixAPIContext *ctx, int fd1, const char *path1, int fd2, const char *path2) { const int flags = 0; return do_renameat(ctx, "renameat", fd1, path1, fd2, path2, flags); } int fcfs_renameat2_ex(FCFSPosixAPIContext *ctx, int fd1, const char *path1, int fd2, const char *path2, unsigned int flags) { /* flags convert from FreeBSD to Linux */ #if defined(RENAME_SWAP) && defined(RENAME_EXCL) if ((flags & RENAME_SWAP)) { flags = ((flags & (~RENAME_SWAP)) | RENAME_EXCHANGE); } else if ((flags & RENAME_EXCL)) { flags = ((flags & (~RENAME_EXCL)) | RENAME_NOREPLACE); } #endif return do_renameat(ctx, "renameat2", fd1, path1, fd2, path2, flags); } int fcfs_mkdir_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "mkdir", &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_mkdir_ex(&ctx->api_ctx, path, &ctx->api_ctx.owner.oper, mode)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_mkdirat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, mode_t mode) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_pathat(ctx, "mkdirat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_mkdir_ex(&ctx->api_ctx, path, &ctx->api_ctx.owner.oper, mode)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_rmdir_ex(FCFSPosixAPIContext *ctx, const char *path) { const int flags = FDIR_UNLINK_FLAGS_MATCH_DIR; int result; char full_fname[PATH_MAX]; FDIRClientOperFnamePair fname; if ((result=papi_resolve_path(ctx, "rmdir", &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_remove_dentry_ex(&ctx->api_ctx, &fname, flags, fcfs_posix_api_gettid(fcfs_papi_tpid_type_tid))) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_chown_ex(FCFSPosixAPIContext *ctx, const char *path, uid_t owner, gid_t group) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; int result; char full_fname[PATH_MAX]; FDIRClientOperFnamePair fname; if ((result=papi_resolve_path(ctx, "chown", &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_chown_ex(&ctx->api_ctx, &fname, owner, group, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_lchown_ex(FCFSPosixAPIContext *ctx, const char *path, uid_t owner, gid_t group) { const int flags = 0; int result; char full_fname[PATH_MAX]; FDIRClientOperFnamePair fname; if ((result=papi_resolve_path(ctx, "lchown", &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_chown_ex(&ctx->api_ctx, &fname, owner, group, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_fchown(int fd, uid_t owner, gid_t group) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; int result; FCFSPosixAPIFileInfo *file; FDIRClientOperInodePair oino; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx-> owner.oper, file->fi.dentry.inode); if ((result=fcfs_api_chown_by_inode_ex(file->fi.ctx, &oino, owner, group, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_fchownat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, uid_t owner, gid_t group, int flags) { int result; char full_fname[PATH_MAX]; FDIRClientOperFnamePair fname; if ((result=papi_resolve_pathat(ctx, "fchownat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_chown_ex(&ctx->api_ctx, &fname, owner, group, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : FDIR_FLAGS_FOLLOW_SYMLINK)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_chmod_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode) { const int flags = 0; int result; char full_fname[PATH_MAX]; FDIRClientOperFnamePair fname; if ((result=papi_resolve_path(ctx, "chmod", &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_chmod_ex(&ctx->api_ctx, &fname, mode, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_fchmod(int fd, mode_t mode) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; int result; FCFSPosixAPIFileInfo *file; FDIRClientOperInodePair oino; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx-> owner.oper, file->fi.dentry.inode); if ((result=fcfs_api_chmod_by_inode_ex(file->fi.ctx, &oino, mode, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_fchmodat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, mode_t mode, int flags) { int result; char full_fname[PATH_MAX]; FDIRClientOperFnamePair fname; if ((result=papi_resolve_pathat(ctx, "fchmodat", fd, &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_chmod_ex(&ctx->api_ctx, &fname, mode, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : FDIR_FLAGS_FOLLOW_SYMLINK)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_statvfs_ex(FCFSPosixAPIContext *ctx, const char *path, struct statvfs *buf) { char full_fname[PATH_MAX]; int result; if ((result=papi_resolve_path(ctx, "chmod", &path, full_fname, sizeof(full_fname))) != 0) { return result; } if ((result=fcfs_api_statvfs_ex(&ctx->api_ctx, path, buf)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_fstatvfs(int fd, struct statvfs *buf) { FCFSPosixAPIFileInfo *file; int result; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_statvfs_ex(file->fi.ctx, file->filename.str, buf)) != 0) { errno = result; return -1; } else { return 0; } } static inline int do_setxattr(FCFSPosixAPIContext *ctx, const char *func, const char *path, const char *name, const void *value, size_t size, int flags) { FDIRClientOperFnamePair fname; char full_fname[PATH_MAX]; key_value_pair_t xattr; int result; if ((result=papi_resolve_path(ctx, func, &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); FC_SET_STRING(xattr.key, (char *)name); FC_SET_STRING_EX(xattr.value, (char *)value, size); if ((result=fcfs_api_set_xattr_by_path_ex(&ctx->api_ctx, &fname, &xattr, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_setxattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name, const void *value, size_t size, int flags) { return do_setxattr(ctx, "setxattr", path, name, value, size, (flags | FDIR_FLAGS_FOLLOW_SYMLINK)); } int fcfs_lsetxattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name, const void *value, size_t size, int flags) { return do_setxattr(ctx, "lsetxattr", path, name, value, size, (flags & (~FDIR_FLAGS_FOLLOW_SYMLINK))); } int fcfs_fsetxattr(int fd, const char *name, const void *value, size_t size, int flags) { FDIRClientOperInodePair oino; key_value_pair_t xattr; FCFSPosixAPIFileInfo *file; int result; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx-> owner.oper, file->fi.dentry.inode); FC_SET_STRING(xattr.key, (char *)name); FC_SET_STRING_EX(xattr.value, (char *)value, size); if ((result=fcfs_api_set_xattr_by_inode_ex(file->fi.ctx, &oino, &xattr, (flags | FDIR_FLAGS_FOLLOW_SYMLINK))) != 0) { errno = result; return -1; } else { return 0; } } #define GET_XATTR_FLAGS_BY_VSIZE(size, flags) \ ((size == 0) ? (flags | FDIR_FLAGS_XATTR_GET_SIZE) : flags) static inline ssize_t do_getxattr(FCFSPosixAPIContext *ctx, const char *func, const char *path, const char *name, void *value, size_t size, int flags) { FDIRClientOperFnamePair fname; char full_fname[PATH_MAX]; string_t nm; string_t vl; int result; if ((result=papi_resolve_path(ctx, func, &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); FC_SET_STRING(nm, (char *)name); FC_SET_STRING_EX(vl, (char *)value, 0); if ((result=fcfs_api_get_xattr_by_path_ex(&ctx->api_ctx, &fname, &nm, LOG_DEBUG, &vl, size, GET_XATTR_FLAGS_BY_VSIZE(size, flags))) != 0) { errno = result; return -1; } else { return vl.len; } } ssize_t fcfs_getxattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name, void *value, size_t size) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; return do_getxattr(ctx, "getxattr", path, name, value, size, flags); } ssize_t fcfs_lgetxattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name, void *value, size_t size) { const int flags = 0; return do_getxattr(ctx, "lgetxattr", path, name, value, size, flags); } ssize_t fcfs_fgetxattr(int fd, const char *name, void *value, size_t size) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; int result; FCFSPosixAPIFileInfo *file; string_t nm; string_t vl; FDIRClientOperInodePair oino; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx-> owner.oper, file->fi.dentry.inode); FC_SET_STRING(nm, (char *)name); FC_SET_STRING_EX(vl, (char *)value, 0); if ((result=fcfs_api_get_xattr_by_inode_ex(file->fi.ctx, &oino, &nm, LOG_DEBUG, &vl, size, GET_XATTR_FLAGS_BY_VSIZE(size, flags))) != 0) { errno = result; return -1; } else { return vl.len; } } static inline ssize_t do_listxattr(FCFSPosixAPIContext *ctx, const char *func, const char *path, char *list, size_t size, int flags) { FDIRClientOperFnamePair fname; char full_fname[PATH_MAX]; string_t ls; int result; if ((result=papi_resolve_path(ctx, func, &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); ls.str = list; if ((result=fcfs_api_list_xattr_by_path_ex(&ctx->api_ctx, &fname, &ls, size, GET_XATTR_FLAGS_BY_VSIZE(size, flags))) != 0) { errno = result; return -1; } else { return ls.len; } } ssize_t fcfs_listxattr_ex(FCFSPosixAPIContext *ctx, const char *path, char *list, size_t size) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; return do_listxattr(ctx, "listattr", path, list, size, flags); } ssize_t fcfs_llistxattr_ex(FCFSPosixAPIContext *ctx, const char *path, char *list, size_t size) { const int flags = 0; return do_listxattr(ctx, "llistattr", path, list, size, flags); } ssize_t fcfs_flistxattr(int fd, char *list, size_t size) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; int result; FCFSPosixAPIFileInfo *file; string_t ls; FDIRClientOperInodePair oino; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx-> owner.oper, file->fi.dentry.inode); ls.str = list; if ((result=fcfs_api_list_xattr_by_inode_ex(file->fi.ctx, &oino, &ls, size, GET_XATTR_FLAGS_BY_VSIZE(size, flags))) != 0) { errno = result; return -1; } else { return ls.len; } } static inline int do_removexattr(FCFSPosixAPIContext *ctx, const char *func, const char *path, const char *name, int flags) { FDIRClientOperFnamePair fname; char full_fname[PATH_MAX]; string_t nm; int result; if ((result=papi_resolve_path(ctx, func, &path, full_fname, sizeof(full_fname))) != 0) { return result; } FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); FC_SET_STRING(nm, (char *)name); if ((result=fcfs_api_remove_xattr_by_path_ex(&ctx->api_ctx, &fname, &nm, flags)) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_removexattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; return do_removexattr(ctx, "removexattr", path, name, flags); } int fcfs_lremovexattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name) { const int flags = 0; return do_removexattr(ctx, "lremovexattr", path, name, flags); } int fcfs_fremovexattr(int fd, const char *name) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; int result; FCFSPosixAPIFileInfo *file; string_t nm; FDIRClientOperInodePair oino; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx-> owner.oper, file->fi.dentry.inode); FC_SET_STRING(nm, (char *)name); if ((result=fcfs_api_remove_xattr_by_inode_ex(file->fi.ctx, &oino, &nm, flags)) != 0) { errno = result; return -1; } else { return 0; } } static FCFSPosixAPIDIR *do_opendir(FCFSPosixAPIContext *ctx, FCFSPosixAPIFileInfo *file) { FDIRClientOperInodePair oino; FCFSPosixAPIDIR *dir; int result; dir = (FCFSPosixAPIDIR *)fc_malloc(sizeof(FCFSPosixAPIDIR)); if (dir == NULL) { errno = ENOMEM; return NULL; } FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx-> owner.oper, file->fi.dentry.inode); dir->file = file; dir->magic = FCFS_PAPI_MAGIC_NUMBER; dir->offset = 0; fdir_client_compact_dentry_array_init(&dir->darray); if ((result=fcfs_api_list_compact_dentry_by_inode_ex( &ctx->api_ctx, &oino, &dir->darray)) != 0) { free(dir); errno = result; return NULL; } return dir; } DIR *fcfs_opendir_ex(FCFSPosixAPIContext *ctx, const char *path) { const int flags = O_RDONLY; const mode_t mode = ACCESSPERMS | S_IFDIR; FCFSPosixAPIFileInfo *file; FCFSPosixAPIDIR *dir; char full_fname[PATH_MAX]; if (papi_resolve_path(ctx, "opendir", &path, full_fname, sizeof(full_fname)) != 0) { return NULL; } if ((file=do_open_ex(ctx, path, flags, mode, fcfs_papi_tpid_type_tid)) == NULL) { return NULL; } if ((dir=do_opendir(ctx, file)) == NULL) { fcfs_api_close(&file->fi); fcfs_fd_manager_free(file); } return (DIR *)dir; } DIR *fcfs_fdopendir_ex(FCFSPosixAPIContext *ctx, int fd) { FCFSPosixAPIFileInfo *file; FCFSPosixAPIDIR *dir; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return NULL; } if ((dir=do_opendir(ctx, file)) == NULL) { fcfs_api_close(&file->fi); fcfs_fd_manager_free(file); } return (DIR *)dir; } #define FCFS_CONVERT_DIRP_EX(dirp, ...) \ FCFSPosixAPIDIR *dir; \ dir = (FCFSPosixAPIDIR *)dirp; \ if (dir->magic != FCFS_PAPI_MAGIC_NUMBER) { \ errno = EBADF; \ return __VA_ARGS__; \ } #define FCFS_CONVERT_DIRP(dirp) \ FCFS_CONVERT_DIRP_EX(dirp, -1) #define FCFS_CONVERT_DIRP_VOID(dirp) \ FCFS_CONVERT_DIRP_EX(dirp) int fcfs_closedir_ex(FCFSPosixAPIContext *ctx, DIR *dirp) { FCFS_CONVERT_DIRP(dirp); fdir_client_compact_dentry_array_free(&dir->darray); fcfs_api_close(&dir->file->fi); fcfs_fd_manager_free(dir->file); dir->magic = 0; free(dir); return 0; } static inline struct dirent *do_readdir(FCFSPosixAPIDIR *dir) { if (dir->offset < 0 || dir->offset >= dir->darray.count) { return NULL; } return dir->darray.entries + dir->offset++; } struct dirent *fcfs_readdir_ex(FCFSPosixAPIContext *ctx, DIR *dirp) { FCFS_CONVERT_DIRP_EX(dirp, NULL); return do_readdir(dir); } int fcfs_readdir_r_ex(FCFSPosixAPIContext *ctx, DIR *dirp, struct dirent *entry, struct dirent **result) { struct dirent *current; FCFS_CONVERT_DIRP(dirp); if ((current=do_readdir(dir)) != NULL) { memcpy(entry, current, sizeof(FDIRDirent)); *result = entry; } else { *result = NULL; } return 0; } void fcfs_seekdir_ex(FCFSPosixAPIContext *ctx, DIR *dirp, long loc) { FCFS_CONVERT_DIRP_VOID(dirp); dir->offset = loc; } long fcfs_telldir_ex(FCFSPosixAPIContext *ctx, DIR *dirp) { FCFS_CONVERT_DIRP(dirp); return dir->offset; } void fcfs_rewinddir_ex(FCFSPosixAPIContext *ctx, DIR *dirp) { FCFS_CONVERT_DIRP_VOID(dirp); dir->offset = 0; } int fcfs_dirfd_ex(FCFSPosixAPIContext *ctx, DIR *dirp) { FCFS_CONVERT_DIRP(dirp); if (fcfs_fd_manager_get(dir->file->fd) != dir->file) { errno = EBADF; return -1; } return dir->file->fd; } static int do_scandir(FCFSPosixAPIContext *ctx, const char *path, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)) { FDIRClientOperFnamePair fname; FCFSPosixAPIDIR dir; FDIRDirent *ent; FDIRDirent *end; FDIRDirent **cur; int result; int count; fdir_client_compact_dentry_array_init(&dir.darray); FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx, ctx->api_ctx.owner.oper, path); if ((result=fcfs_api_list_compact_dentry_by_path_ex(&ctx-> api_ctx, &fname, &dir.darray)) != 0) { errno = result; return -1; } do { if ((*namelist=fc_malloc(sizeof(FDIRDirent *) * dir.darray.count)) == NULL) { errno = ENOMEM; count = -1; break; } count = 0; if (dir.darray.count == 0) { break; } cur = (FDIRDirent **)*namelist; end = dir.darray.entries + dir.darray.count; for (ent=dir.darray.entries; ent 1) { qsort(*namelist, count, sizeof(struct dirent *), (int (*)(const void *, const void *))compar); } } } while (0); fdir_client_compact_dentry_array_free(&dir.darray); return count; } int fcfs_scandir_ex(FCFSPosixAPIContext *ctx, const char *path, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)) { char full_fname[PATH_MAX]; if (papi_resolve_path(ctx, "scandir", &path, full_fname, sizeof(full_fname)) != 0) { return -1; } return do_scandir(ctx, path, namelist, filter, compar); } int fcfs_scandirat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)) { char full_fname[PATH_MAX]; if (papi_resolve_pathat(ctx, "scandirat", fd, &path, full_fname, sizeof(full_fname)) != 0) { return -1; } return do_scandir(ctx, path, namelist, filter, compar); } static int do_chdir(const string_t *path) { string_t *cwd; char *old_cwd; if ((cwd=fc_malloc(sizeof(string_t) + path->len + 1)) == NULL) { errno = ENOMEM; return -1; } cwd->str = (char *)(cwd + 1); memcpy(cwd->str, path->str, path->len + 1); cwd->len = path->len; old_cwd = (G_FCFS_PAPI_CWD != NULL ? G_FCFS_PAPI_CWD->str : NULL); G_FCFS_PAPI_CWD = cwd; if (old_cwd != NULL) { free(old_cwd); } return 0; } int fcfs_chdir_ex(FCFSPosixAPIContext *ctx, const char *path) { char full_fname[PATH_MAX]; string_t cwd; if (papi_resolve_path(ctx, "chdir", &path, full_fname, sizeof(full_fname)) != 0) { return -1; } FC_SET_STRING(cwd, (char *)path); return do_chdir(&cwd); } int fcfs_fchdir(int fd) { FCFSPosixAPIFileInfo *file; char full_path[PATH_MAX]; string_t cwd; char *p; int len; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } p = strrchr(file->filename.str, '/'); if (p == NULL) { errno = EBUSY; return -1; } len = (p - file->filename.str) + 1; if (len >= sizeof(full_path)) { snprintf(full_path, sizeof(full_path), "%.*s", len, file->filename.str); } else { memcpy(full_path, file->filename.str, len); *(full_path + len) = '\0'; } FC_SET_STRING_EX(cwd, full_path, len); return do_chdir(&cwd); } char *fcfs_getcwd_ex(FCFSPosixAPIContext *ctx, char *buf, size_t size) { string_t cwd; cwd.str = buf; return do_getcwd(ctx, &cwd, size, ERANGE); } char *fcfs_getwd_ex(FCFSPosixAPIContext *ctx, char *buf) { string_t cwd; cwd.str = buf; return do_getcwd(ctx, &cwd, PATH_MAX, ENAMETOOLONG); } #define FCFS_API_NOT_IMPLEMENTED(api_name, retval) \ logError("file: "__FILE__", line: %d, " \ "function \"%s\" not implemented!", \ __LINE__, api_name); \ errno = EOPNOTSUPP; \ return retval int fcfs_chroot_ex(FCFSPosixAPIContext *ctx, const char *path) { FCFS_API_NOT_IMPLEMENTED("chroot", -1); } int fcfs_dup_ex(FCFSPosixAPIContext *ctx, int fd) { logInfo("file: "__FILE__", line: %d, func: %s, " "fd: %d", __LINE__, __FUNCTION__, fd); FCFS_API_NOT_IMPLEMENTED("dup", -1); } int fcfs_dup2_ex(FCFSPosixAPIContext *ctx, int fd1, int fd2) { logInfo("file: "__FILE__", line: %d, func: %s, " "fd1: %d, fd2: %d", __LINE__, __FUNCTION__, fd1, fd2); FCFS_API_NOT_IMPLEMENTED("dup2", -1); } void *fcfs_mmap_ex(FCFSPosixAPIContext *ctx, void *addr, size_t length, int prot, int flags, int fd, off_t offset) { FCFS_API_NOT_IMPLEMENTED("mmap", NULL); } int fcfs_munmap_ex(FCFSPosixAPIContext *ctx, void *addr, size_t length) { FCFS_API_NOT_IMPLEMENTED("munmap", -1); } int fcfs_lockf_ex(FCFSPosixAPIContext *ctx, int fd, int cmd, off_t len) { FCFSPosixAPIFileInfo *file; struct flock lock; int fcntl_cmd; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } switch (cmd) { case F_LOCK: fcntl_cmd = F_SETLKW; lock.l_type = F_WRLCK; break; case F_TLOCK: fcntl_cmd = F_SETLK; lock.l_type = F_WRLCK; break; case F_ULOCK: fcntl_cmd = F_SETLK; lock.l_type = F_UNLCK; break; case F_TEST: fcntl_cmd = F_GETLK; lock.l_type = F_UNLCK; break; default: errno = EOPNOTSUPP; return -1; } lock.l_whence = SEEK_SET; lock.l_start = file->fi.offset; lock.l_len = len; lock.l_pid = fcfs_posix_api_getpid(); return do_fcntl(file, fcntl_cmd, &lock); } int fcfs_posix_fallocate_ex(FCFSPosixAPIContext *ctx, int fd, off_t offset, off_t len) { const int mode = 0; int result; FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } if ((result=fcfs_api_fallocate_ex(&file->fi, mode, offset, len, fcfs_posix_api_gettid(file->tpid_type))) != 0) { errno = result; return -1; } else { return 0; } } int fcfs_posix_fadvise_ex(FCFSPosixAPIContext *ctx, int fd, off_t offset, off_t len, int advice) { FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } return 0; } static int do_vdprintf(FCFSPosixAPIFileInfo *file, const char *format, va_list ap) { #define FIXED_BUFFUER_SIZE (4 * 1024) char fixed[FIXED_BUFFUER_SIZE]; char *buff; int length; int result; va_list new_ap; va_copy(new_ap, ap); buff = fixed; length = vsnprintf(buff, FIXED_BUFFUER_SIZE, format, ap); if (length > FIXED_BUFFUER_SIZE - 1) { //overflow buff = (char *)fc_malloc(length + 1); if (buff == NULL) { errno = ENOMEM; return -1; } length = vsprintf(buff, format, new_ap); } va_end(new_ap); result = do_write(file, buff, length); if (buff != fixed) { free(buff); } if (result != 0) { errno = result; return -1; } else { return length; } } int fcfs_dprintf(int fd, const char *format, ...) { FCFSPosixAPIFileInfo *file; va_list ap; int bytes; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } va_start(ap, format); bytes = do_vdprintf(file, format, ap); va_end(ap); return bytes; } int fcfs_vdprintf(int fd, const char *format, va_list ap) { FCFSPosixAPIFileInfo *file; if ((file=fcfs_fd_manager_get(fd)) == NULL) { errno = EBADF; return -1; } return do_vdprintf(file, format, ap); } ================================================ FILE: src/api/std/papi.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_PAPI_H #define _FCFS_PAPI_H #include #include #include "api_types.h" #include "fd_manager.h" #define G_FCFS_PAPI_CTX g_fcfs_papi_global_vars.ctx #define G_FCFS_PAPI_CWD g_fcfs_papi_global_vars.cwd #define fcfs_open(path, flags, ...) \ fcfs_open_ex(&G_FCFS_PAPI_CTX, path, flags, ##__VA_ARGS__) #define fcfs_openat(fd, path, flags, ...) \ fcfs_openat_ex(&G_FCFS_PAPI_CTX, fd, path, flags, ##__VA_ARGS__) #define fcfs_creat(path, mode) \ fcfs_creat_ex(&G_FCFS_PAPI_CTX, path, mode) #define fcfs_truncate(path, length) \ fcfs_truncate_ex(&G_FCFS_PAPI_CTX, path, length) #define fcfs_lstat(path, buf) \ fcfs_lstat_ex(&G_FCFS_PAPI_CTX, path, buf) #define fcfs_stat(path, buf) \ fcfs_stat_ex(&G_FCFS_PAPI_CTX, path, buf) #define fcfs_fstatat(fd, path, buf, flags) \ fcfs_fstatat_ex(&G_FCFS_PAPI_CTX, fd, path, buf, flags) #define fcfs_symlink(link, path) \ fcfs_symlink_ex(&G_FCFS_PAPI_CTX, link, path) #define fcfs_symlinkat(link, fd, path) \ fcfs_symlinkat_ex(&G_FCFS_PAPI_CTX, link, fd, path) #define fcfs_link(path1, path2) \ fcfs_link_ex(&G_FCFS_PAPI_CTX, path1, path2) #define fcfs_linkat(fd1, path1, fd2, path2, flags) \ fcfs_linkat_ex(&G_FCFS_PAPI_CTX, fd1, path1, fd2, path2, flags) #define fcfs_readlink(path, buff, size) \ fcfs_readlink_ex(&G_FCFS_PAPI_CTX, path, buff, size) #define fcfs_readlinkat(fd, path, buff, size) \ fcfs_readlinkat_ex(&G_FCFS_PAPI_CTX, fd, path, buff, size) #define fcfs_mknod(path, mode, dev) \ fcfs_mknod_ex(&G_FCFS_PAPI_CTX, path, mode, dev) #define fcfs_mknodat(fd, path, mode, dev) \ fcfs_mknodat_ex(&G_FCFS_PAPI_CTX, fd, path, mode, dev) #define fcfs_mkfifo(path, mode) \ fcfs_mkfifo_ex(&G_FCFS_PAPI_CTX, path, mode) #define fcfs_mkfifoat(fd, path, mode) \ fcfs_mkfifoat_ex(&G_FCFS_PAPI_CTX, fd, path, mode) #define fcfs_access(path, mode) \ fcfs_access_ex(&G_FCFS_PAPI_CTX, path, mode) #define fcfs_faccessat(fd, path, mode, flags) \ fcfs_faccessat_ex(&G_FCFS_PAPI_CTX, fd, path, mode, flags) #define fcfs_euidaccess(path, mode) \ fcfs_euidaccess_ex(&G_FCFS_PAPI_CTX, path, mode) #define fcfs_eaccess(path, mode) \ fcfs_eaccess_ex(&G_FCFS_PAPI_CTX, path, mode) #define fcfs_utime(path, times) \ fcfs_utime_ex(&G_FCFS_PAPI_CTX, path, times) #define fcfs_utimes(path, times) \ fcfs_utimes_ex(&G_FCFS_PAPI_CTX, path, times) #define fcfs_futimes(fd, times) \ fcfs_futimes_ex(&G_FCFS_PAPI_CTX, fd, times) #define fcfs_futimesat(fd, path, times) \ fcfs_futimesat_ex(&G_FCFS_PAPI_CTX, fd, path, times) #define fcfs_futimens(fd, times) \ fcfs_futimens_ex(&G_FCFS_PAPI_CTX, fd, times) #define fcfs_utimensat(fd, path, times, flags) \ fcfs_utimensat_ex(&G_FCFS_PAPI_CTX, fd, path, times, flags) #define fcfs_unlink(path) \ fcfs_unlink_ex(&G_FCFS_PAPI_CTX, path) #define fcfs_unlinkat(fd, path, flags) \ fcfs_unlinkat_ex(&G_FCFS_PAPI_CTX, fd, path, flags) #define fcfs_rename(path1, path2) \ fcfs_rename_ex(&G_FCFS_PAPI_CTX, path1, path2) #define fcfs_renameat(fd1, path1, fd2, path2) \ fcfs_renameat_ex(&G_FCFS_PAPI_CTX, fd1, path1, fd2, path2) #define fcfs_renameat2(fd1, path1, fd2, path2, flags) \ fcfs_renameat2_ex(&G_FCFS_PAPI_CTX, fd1, path1, fd2, path2, flags) #define fcfs_mkdir(path, mode) \ fcfs_mkdir_ex(&G_FCFS_PAPI_CTX, path, mode) #define fcfs_mkdirat(fd, path, mode) \ fcfs_mkdirat_ex(&G_FCFS_PAPI_CTX, fd, path, mode) #define fcfs_rmdir(path) \ fcfs_rmdir_ex(&G_FCFS_PAPI_CTX, path) #define fcfs_chown(path, owner, group) \ fcfs_chown_ex(&G_FCFS_PAPI_CTX, path, owner, group) #define fcfs_lchown(path, owner, group) \ fcfs_lchown_ex(&G_FCFS_PAPI_CTX, path, owner, group) #define fcfs_fchownat(fd, path, owner, group, flags) \ fcfs_fchownat_ex(&G_FCFS_PAPI_CTX, fd, path, owner, group, flags) #define fcfs_chmod(path, mode) \ fcfs_chmod_ex(&G_FCFS_PAPI_CTX, path, mode) #define fcfs_fchmodat(fd, path, mode, flags) \ fcfs_fchmodat_ex(&G_FCFS_PAPI_CTX, fd, path, mode, flags) #define fcfs_statvfs(path, buf) \ fcfs_statvfs_ex(&G_FCFS_PAPI_CTX, path, buf) #define fcfs_setxattr(path, name, value, size, flags) \ fcfs_setxattr_ex(&G_FCFS_PAPI_CTX, path, name, value, size, flags) #define fcfs_lsetxattr(path, name, value, size, flags) \ fcfs_lsetxattr_ex(&G_FCFS_PAPI_CTX, path, name, value, size, flags) #define fcfs_getxattr(path, name, value, size) \ fcfs_getxattr_ex(&G_FCFS_PAPI_CTX, path, name, value, size) #define fcfs_lgetxattr(path, name, value, size) \ fcfs_lgetxattr_ex(&G_FCFS_PAPI_CTX, path, name, value, size) #define fcfs_listxattr(path, list, size) \ fcfs_listxattr_ex(&G_FCFS_PAPI_CTX, path, list, size) #define fcfs_llistxattr(path, list, size) \ fcfs_llistxattr_ex(&G_FCFS_PAPI_CTX, path, list, size) #define fcfs_removexattr(path, name) \ fcfs_removexattr_ex(&G_FCFS_PAPI_CTX, path, name) #define fcfs_lremovexattr(path, name) \ fcfs_lremovexattr_ex(&G_FCFS_PAPI_CTX, path, name) #define fcfs_opendir(path) \ fcfs_opendir_ex(&G_FCFS_PAPI_CTX, path) #define fcfs_fdopendir(fd) \ fcfs_fdopendir_ex(&G_FCFS_PAPI_CTX, fd) #define fcfs_closedir(dirp) \ fcfs_closedir_ex(&G_FCFS_PAPI_CTX, dirp) #define fcfs_readdir(dirp) \ fcfs_readdir_ex(&G_FCFS_PAPI_CTX, dirp) #define fcfs_readdir_r(dirp, entry, result) \ fcfs_readdir_r_ex(&G_FCFS_PAPI_CTX, dirp, entry, result) #define fcfs_seekdir(dirp, loc) \ fcfs_seekdir_ex(&G_FCFS_PAPI_CTX, dirp, loc) #define fcfs_telldir(dirp) \ fcfs_telldir_ex(&G_FCFS_PAPI_CTX, dirp) #define fcfs_rewinddir(dirp) \ fcfs_rewinddir_ex(&G_FCFS_PAPI_CTX, dirp) #define fcfs_dirfd(dirp) \ fcfs_dirfd_ex(&G_FCFS_PAPI_CTX, dirp) #define fcfs_scandir(path, namelist, filter, compar) \ fcfs_scandir_ex(&G_FCFS_PAPI_CTX, path, namelist, filter, compar) #define fcfs_scandirat(fd, path, namelist, filter, compar) \ fcfs_scandirat_ex(&G_FCFS_PAPI_CTX, fd, path, namelist, filter, compar) #define fcfs_lockf(fd, cmd, len) \ fcfs_lockf_ex(&G_FCFS_PAPI_CTX, fd, cmd, len) #define fcfs_posix_fallocate(fd, offset, len) \ fcfs_posix_fallocate_ex(&G_FCFS_PAPI_CTX, fd, offset, len) #define fcfs_posix_fadvise(fd, offset, len, advice) \ fcfs_posix_fadvise_ex(&G_FCFS_PAPI_CTX, fd, offset, len, advice) #define fcfs_chdir(path) \ fcfs_chdir_ex(&G_FCFS_PAPI_CTX, path) #define fcfs_getcwd(buf, size) \ fcfs_getcwd_ex(&G_FCFS_PAPI_CTX, buf, size) #define fcfs_getwd(buf) \ fcfs_getwd_ex(&G_FCFS_PAPI_CTX, buf) #define fcfs_chroot(path) \ fcfs_chroot_ex(&G_FCFS_PAPI_CTX, path) #define fcfs_dup(fd) \ fcfs_dup_ex(&G_FCFS_PAPI_CTX, fd) #define fcfs_dup2(fd1, fd2) \ fcfs_dup2_ex(&G_FCFS_PAPI_CTX, fd1, fd2) #define fcfs_mmap(addr, length, prot, flags, fd, offset) \ fcfs_mmap_ex(&G_FCFS_PAPI_CTX, addr, length, prot, flags, fd, offset) #define fcfs_munmap(addr, length) \ fcfs_munmap_ex(&G_FCFS_PAPI_CTX, addr, length) #ifdef __cplusplus extern "C" { #endif int fcfs_open_ex(FCFSPosixAPIContext *ctx, const char *path, int flags, ...); int fcfs_openat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, int flags, ...); int fcfs_creat_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode); int fcfs_close(int fd); int fcfs_fsync(int fd); int fcfs_fdatasync(int fd); ssize_t fcfs_write(int fd, const void *buff, size_t count); ssize_t fcfs_pwrite(int fd, const void *buff, size_t count, off_t offset); ssize_t fcfs_writev(int fd, const struct iovec *iov, int iovcnt); ssize_t fcfs_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset); ssize_t fcfs_read(int fd, void *buff, size_t count); ssize_t fcfs_pread(int fd, void *buff, size_t count, off_t offset); ssize_t fcfs_readv(int fd, const struct iovec *iov, int iovcnt); ssize_t fcfs_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset); ssize_t fcfs_readahead(int fd, off64_t offset, size_t count); off_t fcfs_lseek(int fd, off_t offset, int whence); off_t fcfs_ltell(int fd); int fcfs_fallocate(int fd, int mode, off_t offset, off_t length); int fcfs_truncate_ex(FCFSPosixAPIContext *ctx, const char *path, off_t length); int fcfs_ftruncate(int fd, off_t length); int fcfs_lstat_ex(FCFSPosixAPIContext *ctx, const char *path, struct stat *buf); int fcfs_stat_ex(FCFSPosixAPIContext *ctx, const char *path, struct stat *buf); int fcfs_fstat(int fd, struct stat *buf); int fcfs_fstatat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, struct stat *buf, int flags); int fcfs_flock(int fd, int operation); int fcfs_fcntl(int fd, int cmd, ...); int fcfs_symlink_ex(FCFSPosixAPIContext *ctx, const char *link, const char *path); int fcfs_symlinkat_ex(FCFSPosixAPIContext *ctx, const char *link, int fd, const char *path); int fcfs_link_ex(FCFSPosixAPIContext *ctx, const char *path1, const char *path2); int fcfs_linkat_ex(FCFSPosixAPIContext *ctx, int fd1, const char *path1, int fd2, const char *path2, int flags); ssize_t fcfs_readlink_ex(FCFSPosixAPIContext *ctx, const char *path, char *buff, size_t size); ssize_t fcfs_readlinkat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, char *buff, size_t size); int fcfs_mknod_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode, dev_t dev); int fcfs_mknodat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, mode_t mode, dev_t dev); int fcfs_mkfifo_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode); int fcfs_mkfifoat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, mode_t mode); int fcfs_access_ex(FCFSPosixAPIContext *ctx, const char *path, int mode); int fcfs_faccessat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, int mode, int flags); int fcfs_euidaccess_ex(FCFSPosixAPIContext *ctx, const char *path, int mode); static inline int fcfs_eaccess_ex(FCFSPosixAPIContext *ctx, const char *path, int mode) { return fcfs_euidaccess_ex(ctx, path, mode); } int fcfs_utime_ex(FCFSPosixAPIContext *ctx, const char *path, const struct utimbuf *times); int fcfs_utimes_ex(FCFSPosixAPIContext *ctx, const char *path, const struct timeval times[2]); int fcfs_futimes_ex(FCFSPosixAPIContext *ctx, int fd, const struct timeval times[2]); int fcfs_futimesat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, const struct timeval times[2]); int fcfs_futimens_ex(FCFSPosixAPIContext *ctx, int fd, const struct timespec times[2]); int fcfs_utimensat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, const struct timespec times[2], int flags); int fcfs_unlink_ex(FCFSPosixAPIContext *ctx, const char *path); int fcfs_unlinkat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, int flags); int fcfs_rename_ex(FCFSPosixAPIContext *ctx, const char *path1, const char *path2); int fcfs_renameat_ex(FCFSPosixAPIContext *ctx, int fd1, const char *path1, int fd2, const char *path2); //renameatx_np for FreeBSD int fcfs_renameat2_ex(FCFSPosixAPIContext *ctx, int fd1, const char *path1, int fd2, const char *path2, unsigned int flags); int fcfs_mkdir_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode); int fcfs_mkdirat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, mode_t mode); int fcfs_rmdir_ex(FCFSPosixAPIContext *ctx, const char *path); int fcfs_chown_ex(FCFSPosixAPIContext *ctx, const char *path, uid_t owner, gid_t group); int fcfs_lchown_ex(FCFSPosixAPIContext *ctx, const char *path, uid_t owner, gid_t group); int fcfs_fchown(int fd, uid_t owner, gid_t group); int fcfs_fchownat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, uid_t owner, gid_t group, int flags); int fcfs_chmod_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode); int fcfs_fchmod(int fd, mode_t mode); int fcfs_fchmodat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, mode_t mode, int flags); int fcfs_statvfs_ex(FCFSPosixAPIContext *ctx, const char *path, struct statvfs *buf); int fcfs_fstatvfs(int fd, struct statvfs *buf); int fcfs_setxattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name, const void *value, size_t size, int flags); int fcfs_lsetxattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name, const void *value, size_t size, int flags); int fcfs_fsetxattr(int fd, const char *name, const void *value, size_t size, int flags); ssize_t fcfs_getxattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name, void *value, size_t size); ssize_t fcfs_lgetxattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name, void *value, size_t size); ssize_t fcfs_fgetxattr(int fd, const char *name, void *value, size_t size); ssize_t fcfs_listxattr_ex(FCFSPosixAPIContext *ctx, const char *path, char *list, size_t size); ssize_t fcfs_llistxattr_ex(FCFSPosixAPIContext *ctx, const char *path, char *list, size_t size); ssize_t fcfs_flistxattr(int fd, char *list, size_t size); int fcfs_removexattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name); int fcfs_lremovexattr_ex(FCFSPosixAPIContext *ctx, const char *path, const char *name); int fcfs_fremovexattr(int fd, const char *name); DIR *fcfs_opendir_ex(FCFSPosixAPIContext *ctx, const char *path); DIR *fcfs_fdopendir_ex(FCFSPosixAPIContext *ctx, int fd); int fcfs_closedir_ex(FCFSPosixAPIContext *ctx, DIR *dirp); struct dirent *fcfs_readdir_ex(FCFSPosixAPIContext *ctx, DIR *dirp); int fcfs_readdir_r_ex(FCFSPosixAPIContext *ctx, DIR *dirp, struct dirent *entry, struct dirent **result); void fcfs_seekdir_ex(FCFSPosixAPIContext *ctx, DIR *dirp, long loc); long fcfs_telldir_ex(FCFSPosixAPIContext *ctx, DIR *dirp); void fcfs_rewinddir_ex(FCFSPosixAPIContext *ctx, DIR *dirp); int fcfs_dirfd_ex(FCFSPosixAPIContext *ctx, DIR *dirp); int fcfs_scandir_ex(FCFSPosixAPIContext *ctx, const char *path, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)); int fcfs_scandirat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)); int fcfs_lockf_ex(FCFSPosixAPIContext *ctx, int fd, int cmd, off_t len); int fcfs_posix_fallocate_ex(FCFSPosixAPIContext *ctx, int fd, off_t offset, off_t len); int fcfs_posix_fadvise_ex(FCFSPosixAPIContext *ctx, int fd, off_t offset, off_t len, int advice); int fcfs_dprintf(int fd, const char *format, ...) __gcc_attribute__ ((format (printf, 2, 3))); int fcfs_vdprintf(int fd, const char *format, va_list ap); int fcfs_chdir_ex(FCFSPosixAPIContext *ctx, const char *path); int fcfs_fchdir(int fd); char *fcfs_getcwd_ex(FCFSPosixAPIContext *ctx, char *buf, size_t size); char *fcfs_getwd_ex(FCFSPosixAPIContext *ctx, char *buf); int fcfs_chroot_ex(FCFSPosixAPIContext *ctx, const char *path); int fcfs_dup_ex(FCFSPosixAPIContext *ctx, int fd); int fcfs_dup2_ex(FCFSPosixAPIContext *ctx, int fd1, int fd2); void *fcfs_mmap_ex(FCFSPosixAPIContext *ctx, void *addr, size_t length, int prot, int flags, int fd, off_t offset); int fcfs_munmap_ex(FCFSPosixAPIContext *ctx, void *addr, size_t length); /* following functions for internal use only */ static inline FCFSPosixAPIFileInfo *fcfs_get_file_handle(int fd) { return fcfs_fd_manager_get(fd); } int fcfs_file_open(FCFSPosixAPIContext *ctx, const char *path, const int flags, const int mode, const FCFSPosixAPITPIDType tpid_type); //for fread ssize_t fcfs_file_read(int fd, void *buff, size_t size, size_t n); //for fwrite ssize_t fcfs_file_write(int fd, const void *buff, size_t size, size_t n); ssize_t fcfs_file_readline(int fd, char *s, size_t size); //for fgets ssize_t fcfs_file_gets(int fd, char *s, size_t size); //for getdelim ssize_t fcfs_file_getdelim(int fd, char **line, size_t *size, int delim); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/std/posix_api.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fastcommon/logger.h" #include "sf/idempotency/client/client_channel.h" #include "posix_api.h" #define DUMMY_MOUNTPOINT_STR "/fastcfs/dummy/" #define DUMMY_MOUNTPOINT_LEN (sizeof(DUMMY_MOUNTPOINT_STR) - 1) FCFSPosixAPIGlobalVars g_fcfs_papi_global_vars = { {{NULL, NULL}, {DUMMY_MOUNTPOINT_STR, DUMMY_MOUNTPOINT_LEN}} }; static int load_posix_api_config(FCFSPosixAPIContext *ctx, const char *ns, IniFullContext *ini_ctx, const char *fdir_section_name) { int result; ctx->nsmp.ns = (char *)ns; FC_SET_STRING_NULL(ctx->mountpoint); if ((result=fcfs_api_load_ns_mountpoint(ini_ctx, fdir_section_name, &ctx->nsmp, &ctx->mountpoint, false)) != 0) { return result; } return 0; } int fcfs_posix_api_init_ex1(FCFSPosixAPIContext *ctx, const char *log_prefix_name, const char *ns, const char *config_filename, const char *fdir_section_name, const char *fs_section_name, const bool publish) { const bool need_lock = true; const bool persist_additional_gids = false; int result; IniContext iniContext; IniFullContext ini_ctx; log_try_init(); if ((result=iniLoadFromFile(config_filename, &iniContext)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, config_filename, result); return result; } FAST_INI_SET_FULL_CTX_EX(ini_ctx, config_filename, NULL, &iniContext); do { if ((result=load_posix_api_config(ctx, ns, &ini_ctx, fdir_section_name)) != 0) { break; } if ((result=fcfs_api_pooled_init_ex1(&ctx->api_ctx, ns, &ini_ctx, fdir_section_name, fs_section_name, need_lock, persist_additional_gids)) != 0) { break; } if ((result=fcfs_api_check_mountpoint(config_filename, &ctx->mountpoint)) != 0) { break; } if ((result=fcfs_api_load_idempotency_config_ex(log_prefix_name, &ini_ctx, fdir_section_name, fs_section_name)) != 0) { break; } } while (0); iniFreeContext(&iniContext); if (result != 0) { return result; } if ((result=fcfs_api_client_session_create( &ctx->api_ctx, publish)) != 0) { return result; } return fcfs_fd_manager_init(); } void fcfs_posix_api_log_configs_ex(FCFSPosixAPIContext *ctx, const char *fdir_section_name, const char *fs_section_name) { BufferInfo sf_idempotency_config; char buff[256]; char rdma_busy_polling[128]; char owner_config[2 * NAME_MAX + 64]; sf_idempotency_config.buff = buff; sf_idempotency_config.alloc_size = sizeof(buff); fcfs_api_log_client_common_configs(&ctx->api_ctx, fdir_section_name, fs_section_name, &sf_idempotency_config, owner_config); if (ctx->api_ctx.rdma.enabled) { sprintf(rdma_busy_polling, "rdma busy polling: %s, ", ctx->api_ctx.rdma.busy_polling ? "true" : "false"); } else { *rdma_busy_polling = '\0'; } logInfo("%sFastDIR namespace: %s, %smountpoint: %s, %s", rdma_busy_polling, ctx->nsmp.ns, sf_idempotency_config.buff, ctx->nsmp.mountpoint, owner_config); } void fcfs_posix_api_destroy_ex(FCFSPosixAPIContext *ctx) { if (ctx->mountpoint.str != NULL) { fcfs_api_free_ns_mountpoint(&ctx->nsmp); ctx->mountpoint.str = NULL; fcfs_api_destroy_ex(&ctx->api_ctx); } } ================================================ FILE: src/api/std/posix_api.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_POSIX_API_H #define _FCFS_POSIX_API_H #include #include #include "fastcommon/shared_func.h" #include "api_types.h" #include "fd_manager.h" #include "papi.h" #include "capi.h" #ifdef __cplusplus extern "C" { #endif extern FCFSPosixAPIGlobalVars g_fcfs_papi_global_vars; /** FastCFS POSIX API init * parameters: * ctx: the POSIX API context * log_prefix_name: the prefix name for log filename, NULL for stderr * ns: the namespace/poolname of FastDIR * config_filename: the config filename, eg. /etc/fastcfs/fcfs/fuse.conf * fdir_section_name: the section name of FastDIR * fs_section_name: the section name of FastStore * publish: if publish the session, this parameter is valid when auth enabled * return: error no, 0 for success, != 0 fail */ int fcfs_posix_api_init_ex1(FCFSPosixAPIContext *ctx, const char *log_prefix_name, const char *ns, const char *config_filename, const char *fdir_section_name, const char *fs_section_name, const bool publish); /** FastCFS POSIX API init with default section names * parameters: * ctx: the POSIX API context * log_prefix_name: the prefix name for log filename, NULL for stderr * ns: the namespace/poolname of FastDIR * config_filename: the config filename, eg. /etc/fastcfs/fcfs/fuse.conf * return: error no, 0 for success, != 0 fail */ static inline int fcfs_posix_api_init_ex(FCFSPosixAPIContext *ctx, const char *log_prefix_name, const char *ns, const char *config_filename) { const bool publish = true; return fcfs_posix_api_init_ex1(ctx, log_prefix_name, ns, config_filename, FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME, publish); } /** FastCFS POSIX API init with the global context * parameters: * log_prefix_name: the prefix name for log filename, NULL for stderr * ns: the namespace/poolname of FastDIR * config_filename: the config filename, eg. /etc/fastcfs/fcfs/fuse.conf * return: error no, 0 for success, != 0 fail */ static inline int fcfs_posix_api_init(const char *log_prefix_name, const char *ns, const char *config_filename) { return fcfs_posix_api_init_ex(&g_fcfs_papi_global_vars.ctx, log_prefix_name, ns, config_filename); } /** log configs of FastCFS POSIX API * parameters: * ctx: the POSIX API context * fdir_section_name: the section name of FastDIR * fs_section_name: the section name of FastStore * return: none */ void fcfs_posix_api_log_configs_ex(FCFSPosixAPIContext *ctx, const char *fdir_section_name, const char *fs_section_name); /** log configs of FastCFS POSIX API with the global context * and default section names * * return: none */ static inline void fcfs_posix_api_log_configs() { fcfs_posix_api_log_configs_ex(&g_fcfs_papi_global_vars.ctx, FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME); } /** FastCFS POSIX API start (create the background threads) * parameters: * ctx: the POSIX API context * return: error no, 0 for success, != 0 fail */ static inline int fcfs_posix_api_start_ex(FCFSPosixAPIContext *ctx) { return fcfs_api_start_ex(&ctx->api_ctx); } /** FastCFS POSIX API init and start * parameters: * ctx: the POSIX API context * log_prefix_name: the prefix name for log filename, NULL for stderr * ns: the namespace/poolname of FastDIR * config_filename: the config filename, eg. /etc/fastcfs/fcfs/fuse.conf * return: error no, 0 for success, != 0 fail */ static inline int fcfs_posix_api_init_start_ex( FCFSPosixAPIContext *ctx, const char *log_prefix_name, const char *ns, const char *config_filename) { int result; if ((result=fcfs_posix_api_init_ex(ctx, log_prefix_name, ns, config_filename)) != 0) { return result; } return fcfs_api_start_ex(&ctx->api_ctx); } /** FastCFS POSIX API stop the background threads * parameters: * ctx: the POSIX API context * return: none */ static inline void fcfs_posix_api_stop_ex(FCFSPosixAPIContext *ctx) { fcfs_api_terminate_ex(&ctx->api_ctx); } /** FastCFS POSIX API destroy * parameters: * ctx: the POSIX API context * return: none */ void fcfs_posix_api_destroy_ex(FCFSPosixAPIContext *ctx); /** FastCFS POSIX API start (create the background threads) * * return: error no, 0 for success, != 0 fail */ static inline int fcfs_posix_api_start() { return fcfs_posix_api_start_ex(&g_fcfs_papi_global_vars.ctx); } /** FastCFS POSIX API init and start with the global context * parameters: * log_prefix_name: the prefix name for log filename, NULL for stderr * ns: the namespace/poolname of FastDIR * config_filename: the config filename, eg. /etc/fastcfs/fcfs/fuse.conf * return: error no, 0 for success, != 0 fail */ static inline int fcfs_posix_api_init_start(const char *log_prefix_name, const char *ns, const char *config_filename) { return fcfs_posix_api_init_start_ex(&g_fcfs_papi_global_vars.ctx, log_prefix_name, ns, config_filename); } /** FastCFS POSIX API stop the background threads with the global context * * return: none */ static inline void fcfs_posix_api_stop() { fcfs_posix_api_stop_ex(&g_fcfs_papi_global_vars.ctx); } /** FastCFS POSIX API destroy with the global context * * return: none */ static inline void fcfs_posix_api_destroy() { fcfs_posix_api_destroy_ex(&g_fcfs_papi_global_vars.ctx); } static inline pid_t fcfs_posix_api_getpid() { return getpid(); } static inline pid_t fcfs_posix_api_gettid( const FCFSPosixAPITPIDType tpid_type) { if (tpid_type == fcfs_papi_tpid_type_pid) { return getpid(); } else { return fc_gettid(); } } static inline int fcfs_posix_api_set_owner_ex( FCFSPosixAPIContext *pctx) { return fcfs_api_set_owner(&pctx->api_ctx); } static inline int fcfs_posix_api_set_owner() { return fcfs_posix_api_set_owner_ex(&g_fcfs_papi_global_vars.ctx); } static inline void fcfs_posix_api_set_fctx(FCFSAPIFileContext *fctx, const FCFSPosixAPIContext *pctx, const mode_t mode, const FCFSPosixAPITPIDType tpid_type) { fctx->mode = (mode & (~fc_get_umask())); fctx->oper = pctx->api_ctx.owner.oper; fctx->tid = fcfs_posix_api_gettid(tpid_type); } #define FCFS_API_IS_MY_MOUNTPOINT_EX(ctx, path) \ (strlen(path) > (ctx)->mountpoint.len && \ ((ctx)->mountpoint.len == 0 || \ memcmp(path, (ctx)->mountpoint.str, \ (ctx)->mountpoint.len) == 0)) #define FCFS_API_IS_MY_MOUNTPOINT(path) \ FCFS_API_IS_MY_MOUNTPOINT_EX(&g_fcfs_papi_global_vars.ctx, path) #define FCFS_API_CHECK_PATH_MOUNTPOINT_EX(file, line, ctx, path, func, retval) \ do { \ if (!FCFS_API_IS_MY_MOUNTPOINT_EX(ctx, path)) \ { \ logError("file: %s, line: %d, " \ "%s path: %s is not the FastCFS mountpoint!", \ file, line, func, path); \ errno = EOPNOTSUPP; \ return retval; \ } \ } while (0) #define FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, path, func) \ FCFS_API_CHECK_PATH_MOUNTPOINT_EX(__FILE__, __LINE__, ctx, path, func, -1) #ifdef __cplusplus } #endif #endif ================================================ FILE: src/api/tests/Makefile.in ================================================ .SUFFIXES: .c .o .lo COMPILE = $(CC) $(CFLAGS) INC_PATH = -I../../include LIB_PATH = -L.. $(LIBS) -lfcfsapi -lfsapi -lfdirclient -lfsclient -lfastcommon -lserverframe TARGET_PATH = $(TARGET_PREFIX)/bin STATIC_OBJS = ALL_PRGS = fcfs_test_file_op fcfs_test_file_copy fcfs_test_papi_copy \ fcfs_test_read_ahead fcfs_beachmark all: $(STATIC_OBJS) $(ALL_PRGS) .o: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c.o: $(COMPILE) -c -o $@ $< $(INC_PATH) install: mkdir -p $(TARGET_PATH) cp -f $(ALL_PRGS) $(TARGET_PATH) clean: rm -f $(STATIC_OBJS) $(ALL_PRGS) ================================================ FILE: src/api/tests/fcfs_beachmark.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/fc_atomic.h" #include "fastcfs/api/std/posix_api.h" typedef enum { fcfs_beachmark_mode_read, fcfs_beachmark_mode_write, fcfs_beachmark_mode_randread, fcfs_beachmark_mode_randwrite } BeachmarkMode; typedef struct { int index; volatile int64_t success_count; } BeachmarkThreadInfo; static struct { const char *config_filename; char *ns; int64_t file_size; struct { BeachmarkMode val; const char *str; } mode; int buffer_size; int thread_count; int runtime; string_t filename_prefix; bool is_fcfs_input; } cfg = {FCFS_FUSE_DEFAULT_CONFIG_FILENAME, "fs", 1 * 1024 * 1024 * 1024, {fcfs_beachmark_mode_read, "read"}, 4 * 1024, 1, 60, {NULL, 0}, false}; static struct { volatile int ready_count; volatile int running_count; volatile char ready_flag; volatile char continue_flag; time_t start_time; struct { int cur; int avg; int max; } iops; struct { char cur[32]; char avg[32]; char max[32]; } iops_buff; BeachmarkThreadInfo *threads; BeachmarkThreadInfo *tend; } st = {0, 0, 0, 1}; static inline void usage(char *argv[]) { fprintf(stderr, "Usage: %s [-c config_filename=%s] " "[-n namespace=fs] [-b buffer_size=4KB] " "[-s file_size=1G] [-m mode=read] " "[-T threads=1] [-t runtime=60] <-f filename_prefix>\n" "\t mode value list: read, write, randrand, randwrite\n\n" "for example: \n" "\tfcfs_beachmark -m randread -s 256M -T 4 -t 300 " "-f /opt/fastcfs/fuse/test_file\n\n", argv[0], FCFS_FUSE_DEFAULT_CONFIG_FILENAME); } static inline int open_file(const char *filename, int flags) { if (cfg.is_fcfs_input) { return fcfs_open(filename, flags); } else { return open(filename, flags); } } static inline void close_file(int fd) { if (cfg.is_fcfs_input) { fcfs_close(fd); } else { close(fd); } } static int create_file(const char *filename, const int64_t start_offset) { #define BUFFER_SIZE (4 * 1024 * 1024) const int flags = O_WRONLY | O_CREAT; int result; int bytes; int fd; int len; int64_t start_time_us; char *buff; char time_buff[32]; unsigned char *p; unsigned char *end; int64_t remain; if (cfg.is_fcfs_input) { fd = fcfs_open(filename, flags, 0755); } else { fd = open(filename, flags, 0755); } if (fd < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "open file %s to write fail, errno: %d, error info: %s", __LINE__, filename, result, strerror(result)); return result; } if (start_offset > 0) { if (cfg.is_fcfs_input) { bytes = fcfs_lseek(fd, start_offset, SEEK_SET); } else { bytes = lseek(fd, start_offset, SEEK_SET); } if (bytes < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "lseek file %s fail, errno: %d, error info: %s", __LINE__, filename, result, strerror(result)); return result; } } start_time_us = get_current_time_us(); printf("creating file: %s ...\n", filename); if ((buff=fc_malloc(BUFFER_SIZE)) == NULL) { close_file(fd); return ENOMEM; } end = (unsigned char *)buff + BUFFER_SIZE; for (p=(unsigned char *)buff; p 0) { len = remain > BUFFER_SIZE ? BUFFER_SIZE : remain; if (cfg.is_fcfs_input) { bytes = fcfs_write(fd, buff, len); } else { bytes = write(fd, buff, len); } if (bytes != len) { result = errno != 0 ? errno : EIO; logError("file: "__FILE__", line: %d, " "write to file %s fail, errno: %d, error info: %s", __LINE__, filename, result, strerror(result)); break; } remain -= len; } if (result == 0) { long_to_comma_str((get_current_time_us() - start_time_us) / 1000, time_buff); printf("create file: %s successfully, time used: %s ms\n", filename, time_buff); } free(buff); close_file(fd); return result; } static int check_create_file(const char *filename) { struct stat stbuf; int ret; int result; if (cfg.is_fcfs_input) { ret = fcfs_stat(filename, &stbuf); } else { ret = stat(filename, &stbuf); } if (ret != 0) { result = errno != 0 ? errno : EIO; if (result == ENOENT) { stbuf.st_size = 0; } else { logError("file: "__FILE__", line: %d, " "stat file %s fail, errno: %d, error info: %s", __LINE__, filename, result, strerror(result)); return result; } } if (stbuf.st_size == cfg.file_size) { return 0; } else if (stbuf.st_size > cfg.file_size) { if (cfg.is_fcfs_input) { ret = fcfs_truncate(filename, cfg.file_size); } else { ret = truncate(filename, cfg.file_size); } if (ret == 0) { return 0; } else { result = errno != 0 ? errno : EIO; logError("file: "__FILE__", line: %d, " "truncate file %s fail, errno: %d, error info: %s", __LINE__, filename, result, strerror(result)); return result; } } return create_file(filename, stbuf.st_size); } static int thread_run(BeachmarkThreadInfo *thread) { int result; int flags; int fd; bool is_read; bool is_sequence; char *buff; int64_t blocks; int64_t offset; int size; int bytes; char *filename; char *p; buff = (char *)fc_malloc(cfg.buffer_size); if (buff == NULL) { return ENOMEM; } filename = fc_malloc(cfg.filename_prefix.len + 8); if (filename == NULL) { return ENOMEM; } p = filename; memcpy(p, cfg.filename_prefix.str, cfg.filename_prefix.len); p += cfg.filename_prefix.len; *p++ = '.'; p += fc_itoa(thread->index, p); *p = '\0'; if ((result=check_create_file(filename)) != 0) { return result; } switch (cfg.mode.val) { case fcfs_beachmark_mode_read: case fcfs_beachmark_mode_randread: is_read = true; flags = O_RDONLY; break; case fcfs_beachmark_mode_write: case fcfs_beachmark_mode_randwrite: is_read = false; flags = O_WRONLY; break; default: return EINVAL; } is_sequence = (cfg.mode.val == fcfs_beachmark_mode_read || cfg.mode.val == fcfs_beachmark_mode_write); if ((fd=open_file(filename, flags)) < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "open file %s to read fail, errno: %d, error info: %s", __LINE__, filename, result, strerror(result)); return result; } __sync_add_and_fetch(&st.ready_count, 1); while (FC_ATOMIC_GET(st.continue_flag) && !FC_ATOMIC_GET(st.ready_flag)) { fc_sleep_ms(10); } offset = 0; size = cfg.buffer_size; blocks = cfg.file_size / cfg.buffer_size; while (FC_ATOMIC_GET(st.continue_flag)) { switch (cfg.mode.val) { case fcfs_beachmark_mode_read: case fcfs_beachmark_mode_write: size = cfg.file_size - offset; if (size <= 0) { offset = 0; size = cfg.buffer_size; } else if (size > cfg.buffer_size) { size = cfg.buffer_size; } break; case fcfs_beachmark_mode_randread: case fcfs_beachmark_mode_randwrite: offset = (((int64_t)rand() * blocks) / (int64_t)RAND_MAX) * cfg.buffer_size; break; default: break; } if (is_read) { if (cfg.is_fcfs_input) { bytes = fcfs_pread(fd, buff, size, offset); } else { bytes = pread(fd, buff, size, offset); } } else { if (cfg.is_fcfs_input) { bytes = fcfs_pwrite(fd, buff, size, offset); } else { bytes = pwrite(fd, buff, size, offset); } } if (bytes != size) { result = errno != 0 ? errno : EIO; logError("file: "__FILE__", line: %d, " "read from file %s fail, errno: %d, error info: %s", __LINE__, filename, result, strerror(result)); return result; } FC_ATOMIC_INC(thread->success_count); if (is_sequence) { offset += bytes; } } close_file(fd); return 0; } static void *thread_entrance(void *arg) { BeachmarkThreadInfo *thread; thread = arg; __sync_add_and_fetch(&st.running_count, 1); thread_run(thread); __sync_sub_and_fetch(&st.running_count, 1); return NULL; } static void output(const time_t current_time) { BeachmarkThreadInfo *thread; static time_t last_time = 0; static int64_t last_count = 0; int64_t total_count; int time_distance; if (last_time == 0) { last_time = st.start_time; printf("\n"); } total_count = 0; for (thread=st.threads; threadsuccess_count); } time_distance = current_time - last_time; if (time_distance > 0) { st.iops.cur = (total_count - last_count) / time_distance; if (st.iops.cur > st.iops.max) { st.iops.max = st.iops.cur; } st.iops.avg = total_count / (current_time - st.start_time); long_to_comma_str(st.iops.cur, st.iops_buff.cur); long_to_comma_str(st.iops.avg, st.iops_buff.avg); printf("running time: %4d seconds, %s IOPS {current: %s, avg: %s}\n", (int)(current_time - st.start_time), cfg.mode.str, st.iops_buff.cur, st.iops_buff.avg); last_time = current_time; last_count = total_count; } } static void sigQuitHandler(int sig) { if (FC_ATOMIC_GET(st.continue_flag)) { FC_ATOMIC_SET(st.continue_flag, 0); printf("file: "__FILE__", line: %d, " "catch signal %d, program exiting...\n", __LINE__, sig); } } static int setup_signal_handler() { struct sigaction act; memset(&act, 0, sizeof(act)); sigemptyset(&act.sa_mask); act.sa_handler = sigQuitHandler; if(sigaction(SIGINT, &act, NULL) < 0 || sigaction(SIGTERM, &act, NULL) < 0 || sigaction(SIGQUIT, &act, NULL) < 0) { fprintf(stderr, "file: "__FILE__", line: %d, " "call sigaction fail, errno: %d, error info: %s\n", __LINE__, errno, strerror(errno)); return errno != 0 ? errno : EBUSY; } return 0; } static int beachmark() { const char *log_prefix_name = NULL; int result; int count; int bytes; long i; time_t current_time; time_t end_time; pthread_t *tids; void **args; char buffer_size_prompt[32]; BeachmarkThreadInfo *thread; if ((result=fcfs_posix_api_init(log_prefix_name, cfg.ns, cfg.config_filename)) != 0) { return result; } if ((result=fcfs_posix_api_start()) != 0) { return result; } if ((result=setup_signal_handler()) != 0) { return result; } fcfs_posix_api_log_configs(); cfg.is_fcfs_input = FCFS_API_IS_MY_MOUNTPOINT(cfg.filename_prefix.str); bytes = sizeof(BeachmarkThreadInfo) * cfg.thread_count; st.threads = fc_malloc(bytes); if (st.threads == NULL) { return ENOMEM; } memset(st.threads, 0, bytes); st.tend = st.threads + cfg.thread_count; tids = fc_malloc(sizeof(pthread_t) * cfg.thread_count); if (tids == NULL) { return ENOMEM; } args = fc_malloc(sizeof(void *) * cfg.thread_count); if (args == NULL) { return ENOMEM; } for (i=0, thread=st.threads; iindex = i; args[i] = thread; } count = cfg.thread_count; if ((result=create_work_threads(&count, thread_entrance, args, tids, 256 * 1024)) != 0) { return result; } if (cfg.buffer_size % 1024 != 0) { long_to_comma_str(cfg.buffer_size, buffer_size_prompt); } else { sprintf(buffer_size_prompt, "%d KB", cfg.buffer_size / 1024); } printf("\nthreads: %d, mode: %s, file size: %"PRId64" MB, " "buffer size: %s\n\n", cfg.thread_count, cfg.mode.str, cfg.file_size / (1024 * 1024), buffer_size_prompt); fc_sleep_ms(100); while (FC_ATOMIC_GET(st.continue_flag) && FC_ATOMIC_GET(st.ready_count) < FC_ATOMIC_GET(st.running_count)) { fc_sleep_ms(10); } FC_ATOMIC_SET(st.ready_flag, 1); st.start_time = time(NULL); end_time = st.start_time + cfg.runtime; do { sleep(1); current_time = time(NULL); output(current_time); } while (FC_ATOMIC_GET(st.continue_flag) && FC_ATOMIC_GET(st.running_count) > 0 && current_time < end_time); long_to_comma_str(st.iops.avg, st.iops_buff.avg); long_to_comma_str(st.iops.max, st.iops_buff.max); printf("\nrunning time: %4d seconds, %s IOPS {avg: %s, max: %s}\n", (int)(current_time - st.start_time), cfg.mode.str, st.iops_buff.avg, st.iops_buff.max); FC_ATOMIC_SET(st.continue_flag, 0); while (FC_ATOMIC_GET(st.running_count) > 0) { sleep(1); } fcfs_posix_api_stop(); return 0; } int main(int argc, char *argv[]) { int result; int ch; int64_t bytes; if (argc < 3) { usage(argv); return 1; } log_try_init(); while ((ch=getopt(argc, argv, "hc:m:n:b:T:t:f:s:")) != -1) { switch (ch) { case 'h': usage(argv); return 0; case 'c': cfg.config_filename = optarg; break; case 'm': if (strcasecmp(optarg, "read") == 0) { cfg.mode.val = fcfs_beachmark_mode_read; cfg.mode.str = "read"; } else if (strcasecmp(optarg, "write") == 0) { cfg.mode.val = fcfs_beachmark_mode_write; cfg.mode.str = "write"; } else if (strcasecmp(optarg, "randread") == 0) { cfg.mode.val = fcfs_beachmark_mode_randread; cfg.mode.str = "randread"; } else if (strcasecmp(optarg, "randwrite") == 0) { cfg.mode.val = fcfs_beachmark_mode_randwrite; cfg.mode.str = "randwrite"; } else { logError("file: "__FILE__", line: %d, " "invalid mode: %s!", __LINE__, optarg); usage(argv); return EINVAL; } break; case 'n': cfg.ns = optarg; break; case 's': if ((result=parse_bytes(optarg, 1, &cfg.file_size)) != 0) { usage(argv); return result; } break; case 'T': cfg.thread_count = strtol(optarg, NULL, 10); if (cfg.thread_count <= 0) { logError("file: "__FILE__", line: %d, " "invalid thread count: %d which <= 0", __LINE__, cfg.thread_count); return EINVAL; } break; case 't': cfg.runtime = strtol(optarg, NULL, 10); if (cfg.runtime <= 0) { logError("file: "__FILE__", line: %d, " "invalid runtime: %d which <= 0", __LINE__, cfg.runtime); return EINVAL; } break; case 'b': if ((result=parse_bytes(optarg, 1, &bytes)) != 0) { usage(argv); return result; } if (bytes <= 0) { logError("file: "__FILE__", line: %d, " "invalid buffer size: %"PRId64" which <= 0", __LINE__, bytes); return EINVAL; } cfg.buffer_size = bytes; break; case 'f': FC_SET_STRING(cfg.filename_prefix, optarg); break; default: usage(argv); return 1; } } if (cfg.filename_prefix.str == NULL) { fprintf(stderr, "expect parameter -f filename_prefix\n\n"); usage(argv); return EINVAL; } if (cfg.file_size < cfg.buffer_size) { logError("file: "__FILE__", line: %d, " "invalid file size: %"PRId64" which < buffer size: %d", __LINE__, cfg.file_size, cfg.buffer_size); return EINVAL; } srand(time(NULL)); return beachmark(); } ================================================ FILE: src/api/tests/fcfs_test_file_copy.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcfs/api/fcfs_api.h" const char *config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME; static char *ns = "fs"; static int buffer_size = 128 * 1024; static char *input_filename; static char *fs_filename; static void usage(char *argv[]) { fprintf(stderr, "Usage: %s [-c config_filename=%s] " "[-n namespace=fs] [-b buffer_size=128KB] " " \n\n", argv[0], FCFS_FUSE_DEFAULT_CONFIG_FILENAME); } static int copy_file() { const bool publish = false; #define FIXED_BUFFEER_SIZE (128 * 1024) FCFSAPIFileContext fctx; int result; int fd; FCFSAPIFileInfo fi; char fixed_buff[FIXED_BUFFEER_SIZE]; char async_report_config[256]; char write_combine_config[512]; char *buff; char new_fs_filename[PATH_MAX]; int read_bytes; int write_bytes; int current_write; if ((fd=open(input_filename, O_RDONLY)) < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "open file %s to read fail, " "errno: %d, error info: %s", __LINE__, input_filename, result, strerror(result)); return result; } if ((result=fcfs_api_pooled_init_with_auth(ns, config_filename, publish)) != 0) { return result; } if ((result=fcfs_api_start()) != 0) { return result; } fcfs_api_async_report_config_to_string_ex(&g_fcfs_api_ctx, async_report_config, sizeof(async_report_config)); fdir_client_log_config_ex(g_fcfs_api_ctx.contexts.fdir, async_report_config, true); fs_api_config_to_string(write_combine_config, sizeof(write_combine_config)); fs_client_log_config_ex(g_fcfs_api_ctx.contexts.fsapi->fs, write_combine_config, true); if (fs_filename[strlen(fs_filename) - 1] == '/') { const char *filename; filename = strrchr(input_filename, '/'); if (filename != NULL) { filename++; //skip "/" } else { filename = input_filename; } snprintf(new_fs_filename, sizeof(new_fs_filename), "%s%s", fs_filename, filename); } else { snprintf(new_fs_filename, sizeof(new_fs_filename), "%s", fs_filename); } fctx.tid = getpid(); fctx.mode = 0755; FDIR_SET_OPERATOR(fctx.oper, geteuid(), getegid(), 0, NULL); if ((result=fcfs_api_open(&fi, new_fs_filename, O_CREAT | O_WRONLY, &fctx)) != 0) { return result; } if (buffer_size <= FIXED_BUFFEER_SIZE) { buff = fixed_buff; } else { buff = (char *)fc_malloc(buffer_size); if (buff == NULL) { return ENOMEM; } } write_bytes = 0; while (1) { read_bytes = read(fd, buff, buffer_size); if (read_bytes == 0) { break; } else if (read_bytes < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "read from file %s fail, " "errno: %d, error info: %s", __LINE__, input_filename, result, strerror(result)); return result; } result = fcfs_api_write(&fi, buff, read_bytes, ¤t_write); if (result != 0) { logError("file: "__FILE__", line: %d, " "write to file %s fail, " "errno: %d, error info: %s", __LINE__, fs_filename, result, STRERROR(result)); return result; } write_bytes += current_write; } fcfs_api_close(&fi); fcfs_api_terminate(); return 0; } int main(int argc, char *argv[]) { int result; int ch; int64_t bytes; if (argc < 3) { usage(argv); return 1; } while ((ch=getopt(argc, argv, "hc:n:b:")) != -1) { switch (ch) { case 'h': usage(argv); break; case 'c': config_filename = optarg; break; case 'n': ns = optarg; break; case 'b': if ((result=parse_bytes(optarg, 1, &bytes)) != 0) { usage(argv); return result; } buffer_size = bytes; break; default: usage(argv); return 1; } } if (optind + 1 >= argc) { usage(argv); return 1; } log_init(); //g_log_context.log_level = LOG_DEBUG; input_filename = argv[optind]; fs_filename = argv[optind + 1]; if (strlen(fs_filename) == 0) { usage(argv); return EINVAL; } return copy_file(); } ================================================ FILE: src/api/tests/fcfs_test_file_op.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcfs/api/fcfs_api.h" static void usage(char *argv[]) { fprintf(stderr, "Usage: %s [-c config_filename=%s] " "-n [namespace=fs] \n\t[-o offset=0] [-l length=0 for auto] " "[-A append mode] \n\t[-T truncate mode] [-S set file size = -1] " "-i \n\n", argv[0], FCFS_FUSE_DEFAULT_CONFIG_FILENAME); } int main(int argc, char *argv[]) { const bool publish = false; const char *config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME; FCFSAPIFileContext fctx; int ch; int result; int open_flags; int64_t offset = 0; int64_t file_size; int64_t file_size_to_set; int length = 0; FCFSAPIFileInfo fi; char *ns = "fs"; char *input_filename = NULL; char *filename; char *endptr; char *out_buff; char *in_buff; int write_bytes; int read_bytes; if (argc < 2) { usage(argv); return 1; } fctx.tid = getpid(); fctx.mode = 0755; FDIR_SET_OPERATOR(fctx.oper, geteuid(), getegid(), 0, NULL); open_flags = 0; file_size_to_set = -1; while ((ch=getopt(argc, argv, "hc:o:n:i:l:S:AT")) != -1) { switch (ch) { case 'h': usage(argv); break; case 'c': config_filename = optarg; break; case 'i': input_filename = optarg; break; case 'n': ns = optarg; break; case 'o': offset = strtol(optarg, &endptr, 10); break; case 'l': length = strtol(optarg, &endptr, 10); break; case 'S': file_size_to_set = strtol(optarg, &endptr, 10); break; case 'A': open_flags |= O_APPEND; break; case 'T': open_flags |= O_TRUNC; break; default: usage(argv); return 1; } } if (input_filename == NULL) { fprintf(stderr, "expect input filename\n"); usage(argv); return 1; } if (optind >= argc) { fprintf(stderr, "expect filename\n"); usage(argv); return 1; } log_init(); //g_log_context.log_level = LOG_DEBUG; filename = argv[optind]; if ((result=getFileContent(input_filename, &out_buff, &file_size)) != 0) { return result; } if (file_size == 0) { logError("file: "__FILE__", line: %d, " "empty file: %s", __LINE__, input_filename); return ENOENT; } if (length == 0 || length > file_size) { length = file_size; } if ((result=fcfs_api_pooled_init_with_auth(ns, config_filename, publish)) != 0) { return result; } /* { struct statvfs stbuf; if (fcfs_api_statvfs(filename, &stbuf) == 0) { printf("%s fs id: %ld, total: %"PRId64" MB, free: %"PRId64" MB, " "avail: %"PRId64" MB, f_namemax: %ld, f_flag: %ld\n", filename, stbuf.f_fsid, (int64_t)(stbuf.f_blocks * stbuf.f_frsize) / (1024 * 1024), (int64_t)(stbuf.f_bfree * stbuf.f_frsize) / (1024 * 1024), (int64_t)(stbuf.f_bavail * stbuf.f_frsize) / (1024 * 1024), stbuf.f_namemax, stbuf.f_flag); } } */ if ((result=fcfs_api_start()) != 0) { return result; } if ((result=fcfs_api_open(&fi, filename, O_CREAT | O_WRONLY | open_flags, &fctx)) != 0) { return result; } if (file_size_to_set >= 0) { if ((result=fcfs_api_ftruncate_ex(&fi, file_size_to_set, fctx.tid)) != 0) { return result; } } if (offset == 0) { result = fcfs_api_write(&fi, out_buff, length, &write_bytes); } else { result = fcfs_api_pwrite(&fi, out_buff, length, offset, &write_bytes); } if (result != 0) { logError("file: "__FILE__", line: %d, " "write to file %s fail, offset: %"PRId64", length: %d, " "write_bytes: %d, errno: %d, error info: %s", __LINE__, filename, offset, length, write_bytes, result, STRERROR(result)); return result; } fcfs_api_close(&fi); if ((result=fcfs_api_open(&fi, filename, O_RDONLY, &fctx)) != 0) { return result; } in_buff = (char *)malloc(length); if (in_buff == NULL) { logError("file: "__FILE__", line: %d, " "malloc %d bytes fail", __LINE__, length); return ENOMEM; } if ((result=fcfs_api_lseek(&fi, offset, SEEK_SET)) != 0) { return result; } if (offset == 0) { result = fcfs_api_read(&fi, in_buff, length, &read_bytes); } else { //result = fcfs_api_pread(&fi, in_buff, length, offset, &read_bytes); result = fcfs_api_read(&fi, in_buff, length, &read_bytes); } if (result != 0) { logError("file: "__FILE__", line: %d, " "read from file %s fail, offset: %"PRId64", length: %d, " "read_bytes: %d, errno: %d, error info: %s", __LINE__, filename, offset, length, read_bytes, result, STRERROR(result)); return result; } if (read_bytes != length) { logError("file: "__FILE__", line: %d, " "read bytes: %d != slice length: %d", __LINE__, read_bytes, length); return EINVAL; } result = memcmp(in_buff, out_buff, length); if (result != 0) { printf("read and write buffer compare result: %d != 0\n", result); return EINVAL; } fcfs_api_close(&fi); fcfs_api_terminate(); return 0; } ================================================ FILE: src/api/tests/fcfs_test_papi_copy.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcfs/api/std/posix_api.h" const char *config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME; static char *ns = "fs"; static int buffer_size = 128 * 1024; static int slice_size = 4 * 1024; static char *input_filename; static char *fs_filename; static bool use_iov = false; //use readv & writev static void usage(char *argv[]) { fprintf(stderr, "Usage: %s [-c config_filename=%s] " "[-n namespace=fs] [-b buffer_size=128KB] " "[-V use readv & writev] [-s slice_size=4K] " " \n\n", argv[0], FCFS_FUSE_DEFAULT_CONFIG_FILENAME); } static int alloc_iovec_array(iovec_array_t *array, char *buff) { struct iovec *iob; struct iovec *last; char *p; array->count = array->alloc = (buffer_size + (slice_size - 1)) / slice_size; array->iovs = fc_malloc(sizeof(struct iovec) * array->alloc); if (array->iovs == NULL) { return ENOMEM; } p = buff; last = array->iovs + (array->alloc - 1); for (iob=array->iovs; iobiov_base = p; iob->iov_len = slice_size; p += iob->iov_len; } last->iov_base = p; last->iov_len = (buff + buffer_size) - p; return 0; } static int copy_file() { #define FIXED_BUFFEER_SIZE (128 * 1024) const char *log_prefix_name = NULL; int result; int src_fd; int dst_fd; bool is_fcfs_input; char fixed_buff[FIXED_BUFFEER_SIZE]; iovec_array_t src_buffers; iovec_array_t dst_buffers; char *buff; char new_filename[PATH_MAX]; char new_fs_filename[PATH_MAX]; int filename_len; int read_bytes; int write_bytes; int current_write; int count; if ((result=fcfs_posix_api_init(log_prefix_name, ns, config_filename)) != 0) { return result; } if ((result=fcfs_posix_api_start()) != 0) { return result; } fcfs_posix_api_log_configs(); is_fcfs_input = FCFS_API_IS_MY_MOUNTPOINT(input_filename); if (is_fcfs_input) { src_fd = fcfs_open(input_filename, O_RDONLY); } else { src_fd = open(input_filename, O_RDONLY); } if (src_fd < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "open file %s to read fail, " "errno: %d, error info: %s", __LINE__, input_filename, result, strerror(result)); return result; } if (fs_filename[strlen(fs_filename) - 1] == '/') { const char *filename; filename = strrchr(input_filename, '/'); if (filename != NULL) { filename++; //skip "/" } else { filename = input_filename; } filename_len = snprintf(new_filename, sizeof(new_filename), "%s%s", fs_filename, filename); } else { filename_len = snprintf(new_filename, sizeof(new_filename), "%s%s", (*fs_filename != '/' ? "/" : ""), fs_filename); } if (!(filename_len > g_fcfs_papi_global_vars.ctx.mountpoint.len && memcmp(new_filename, g_fcfs_papi_global_vars.ctx.mountpoint.str, g_fcfs_papi_global_vars.ctx.mountpoint.len) == 0)) { snprintf(new_fs_filename, sizeof(new_fs_filename), "%s%s", g_fcfs_papi_global_vars.ctx.mountpoint.str, new_filename); } else { snprintf(new_fs_filename, sizeof(new_fs_filename), "%s", new_filename); } if ((dst_fd=fcfs_open(new_fs_filename, O_CREAT | O_WRONLY, 0755)) < 0) { result = errno != 0 ? errno : EIO; logError("file: "__FILE__", line: %d, " "open file %s fail, errno: %d, error info: %s", __LINE__, fs_filename, result, STRERROR(result)); return result; } logInfo("is_fcfs_input: %d, input fd: %d, output fd: %d", is_fcfs_input, src_fd, dst_fd); if (buffer_size <= FIXED_BUFFEER_SIZE) { buff = fixed_buff; } else { buff = (char *)fc_malloc(buffer_size); if (buff == NULL) { return ENOMEM; } } if (use_iov) { if ((result=alloc_iovec_array(&src_buffers, buff)) != 0) { return result; } if ((result=alloc_iovec_array(&dst_buffers, buff)) != 0) { return result; } } count = 0; write_bytes = 0; while (1) { if (is_fcfs_input) { if (use_iov) { read_bytes = fcfs_readv(src_fd, src_buffers.iovs, src_buffers.count); } else { read_bytes = fcfs_read(src_fd, buff, buffer_size); } } else { if (use_iov) { read_bytes = readv(src_fd, src_buffers.iovs, src_buffers.count); } else { read_bytes = read(src_fd, buff, buffer_size); } } if (read_bytes == 0) { break; } else if (read_bytes < 0) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "read from file %s fail, " "errno: %d, error info: %s", __LINE__, input_filename, result, strerror(result)); return result; } if (use_iov) { int bytes; int remain; struct iovec *iob; struct iovec *last; if (read_bytes == buffer_size) { dst_buffers.count = src_buffers.count; } else { iob = src_buffers.iovs; bytes = iob->iov_len; while (bytes < read_bytes) { ++iob; bytes += iob->iov_len; } dst_buffers.count = (iob - src_buffers.iovs + 1); last = dst_buffers.iovs + (dst_buffers.count - 1); remain = bytes - read_bytes; last->iov_len -= remain; logInfo("read_bytes: %d, bytes: %d, count: %d, last length: %d", read_bytes, bytes, dst_buffers.count, (int)last->iov_len); } current_write = fcfs_writev(dst_fd, dst_buffers.iovs, dst_buffers.count); if (read_bytes < buffer_size) { /* restore the last iov */ (dst_buffers.iovs + (dst_buffers.count - 1))->iov_len = (src_buffers.iovs + (dst_buffers.count - 1))->iov_len; } } else { current_write = fcfs_write(dst_fd, buff, read_bytes); } if (current_write != read_bytes) { result = errno != 0 ? errno : EIO; logError("file: "__FILE__", line: %d, " "write to file %s fail, " "errno: %d, error info: %s", __LINE__, fs_filename, result, STRERROR(result)); return result; } if (++count % 10 == 0) { fcfs_fsync(dst_fd); } write_bytes += current_write; } if (is_fcfs_input) { fcfs_close(src_fd); } else { close(src_fd); } fcfs_fsync(dst_fd); fcfs_close(dst_fd); fcfs_posix_api_stop(); return 0; } int main(int argc, char *argv[]) { int result; int ch; int64_t bytes; if (argc < 3) { usage(argv); return 1; } while ((ch=getopt(argc, argv, "hc:n:b:s:V")) != -1) { switch (ch) { case 'h': usage(argv); return 0; case 'c': config_filename = optarg; break; case 'n': ns = optarg; break; case 'b': if ((result=parse_bytes(optarg, 1, &bytes)) != 0) { usage(argv); return result; } if (bytes < 4 * 1024) { buffer_size = 4 * 1024; } else { buffer_size = bytes; } break; case 's': if ((result=parse_bytes(optarg, 1, &bytes)) != 0) { usage(argv); return result; } if (bytes < 1 * 1024) { slice_size = 1 * 1024; } else { slice_size = bytes; } break; case 'V': use_iov = true; break; default: usage(argv); return 1; } } if (optind + 1 >= argc) { usage(argv); return 1; } log_try_init(); //g_log_context.log_level = LOG_DEBUG; if (use_iov) { logInfo("use writev & readv, slice size: %d", slice_size); } input_filename = argv[optind]; fs_filename = argv[optind + 1]; if (strlen(fs_filename) == 0) { usage(argv); return EINVAL; } return copy_file(); } ================================================ FILE: src/api/tests/fcfs_test_read_ahead.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "sf/sf_global.h" #include "fastcfs/api/fcfs_api.h" #define FCFS_BLOCK_SIZE 512 typedef struct { int count; char **blocks; } BlockArray; static char *ns = "test"; static int64_t file_size; static int64_t write_buffer_size; static int64_t read_buffer_size; static int read_thread_count; static int read_loop_count; static bool is_random_read; static bool is_random_write; static volatile int running_read_threads; static volatile int can_read = 0; static BlockArray barray; const char *filename = "/test.dat"; static void usage(char *argv[]) { fprintf(stderr, "Usage: %s [-c config_filename=%s] " "-n [namespace=test]\n\t[-s file_size=64MB] " "[-r read_buffer_size=4KB] [-w write_buffer_size=4KB]\n" "\t[-t read_thread_count=1]\n\t[-R random read] " "[-W random write] [-l read_loop_count=1]\n\n", argv[0], FCFS_FUSE_DEFAULT_CONFIG_FILENAME); } static int check_create_root_path() { FDIRClientOperFnamePair path; FDIRDEntryInfo dentry; int64_t inode; int result; FC_SET_STRING(path.fullname.ns, ns); FC_SET_STRING(path.fullname.path, "/"); FDIR_SET_OPERATOR(path.oper, geteuid(), getegid(), 0, NULL); if ((result=fdir_client_lookup_inode_by_path_ex(g_fcfs_api_ctx. contexts.fdir, &path, LOG_DEBUG, &inode)) != 0) { if (result == ENOENT) { result = fdir_client_create_dentry(g_fcfs_api_ctx. contexts.fdir, &path, 0777 | S_IFDIR, &dentry); } } return result; } static int init() { char *buff; char *p; char **pp; char **end; int count; int bytes; srand(time(NULL)); count = (int64_t)rand() * 256LL / RAND_MAX; barray.count = 2; while (barray.count < count) { barray.count *= 2; } bytes = FCFS_BLOCK_SIZE * barray.count; buff = (char *)fc_malloc(bytes); if (buff == NULL) { return ENOMEM; } barray.blocks = (char **)fc_malloc(sizeof(char *) * barray.count); if (barray.blocks == NULL) { return ENOMEM; } p = buff; end = barray.blocks + barray.count; for (pp=barray.blocks; pp 0 && SF_G_CONTINUE_FLAG) { fill_buffer(out_buff, write_buffer_size, file_size - remain); length = FC_MIN(remain, write_buffer_size); result = fcfs_api_write(fi, out_buff, length, &write_bytes); if (result != 0) { logError("file: "__FILE__", line: %d, " "write to file %s fail, offset: %"PRId64", length: %d, " "write_bytes: %d, errno: %d, error info: %s", __LINE__, filename, file_size - remain, length, write_bytes, result, STRERROR(result)); return result; } remain -= write_bytes; } return 0; } static int random_write(FCFSAPIFileInfo *fi, char *out_buff) { int result; int write_bytes; int i; int64_t loop_count; int64_t offset; i = 0; loop_count = (file_size / write_buffer_size) - 1; while (i <= loop_count && SF_G_CONTINUE_FLAG) { offset = ((int64_t)rand() * loop_count / RAND_MAX) * write_buffer_size; fill_buffer(out_buff, write_buffer_size, offset); result = fcfs_api_pwrite(fi, out_buff, write_buffer_size, offset, &write_bytes); if (result != 0) { logError("file: "__FILE__", line: %d, " "write to file %s fail, offset: %"PRId64", " "length: %"PRId64", write_bytes: %d, " "errno: %d, error info: %s", __LINE__, filename, offset, write_buffer_size, write_bytes, result, STRERROR(result)); return result; } ++i; } return 0; } static inline int do_write(FCFSAPIFileInfo *fi, char *out_buff) { if (is_random_write) { return random_write(fi, out_buff); } else { return sequential_write(fi, out_buff); } } static void *write_thread(void *arg) { FCFSAPIFileContext fctx; FCFSAPIFileInfo fi; char *out_buff; long thread_index; int result; thread_index = (long)arg; fctx.tid = getpid() + thread_index; fctx.mode = 0755; FDIR_SET_OPERATOR(fctx.oper, geteuid(), getegid(), 0, NULL); if ((result=fcfs_api_open(&fi, filename, O_CREAT | O_WRONLY | O_TRUNC, &fctx)) != 0) { __sync_bool_compare_and_swap(&can_read, 0, -1); return NULL; } out_buff = (char *)fc_malloc(write_buffer_size); if (out_buff == NULL) { __sync_bool_compare_and_swap(&can_read, 0, -1); fcfs_api_close(&fi); return NULL; } do { if (is_random_read || is_random_write) { logInfo("file: "__FILE__", line: %d, " "prepare file for read ...", __LINE__); if (sequential_write(&fi, out_buff) != 0) { __sync_bool_compare_and_swap(&can_read, 0, -1); break; } logInfo("file: "__FILE__", line: %d, " "file ready for read.", __LINE__); } __sync_bool_compare_and_swap(&can_read, 0, 1); while (SF_G_CONTINUE_FLAG) { if (do_write(&fi, out_buff) != 0) { break; } } } while (0); fcfs_api_close(&fi); free(out_buff); return NULL; } static int do_read(const int thread_index, FCFSAPIFileInfo *fi, char *in_buff, char *expect_buff, int *wait_count, int *retry_count) { int length; int read_bytes; int result; int fail_count; int64_t loop_count; int64_t i; int64_t offset; int64_t remain; length = read_buffer_size; loop_count = (file_size / read_buffer_size) - 1; i = 0; fail_count = 0; remain = file_size; while (i <= loop_count && SF_G_CONTINUE_FLAG) { if (is_random_read) { offset = ((int64_t)rand() * loop_count / RAND_MAX) * read_buffer_size; } else { offset = file_size - remain; length = FC_MIN(remain, read_buffer_size); } result = fcfs_api_pread(fi, in_buff, length, offset, &read_bytes); if (result != 0) { logError("file: "__FILE__", line: %d, thread index: %d, " "read from file %s fail, offset: %"PRId64", length: %d, " "read_bytes: %d, errno: %d, error info: %s", __LINE__, thread_index, filename, offset, length, read_bytes, result, STRERROR(result)); break; } if (read_bytes != length) { ++(*wait_count); fc_sleep_ms(1); continue; } fill_buffer(expect_buff, read_buffer_size, offset); result = memcmp(in_buff, expect_buff, length); if (result != 0) { if (fail_count++ < 10) { fc_sleep_ms(1); ++(*retry_count); continue; } logError("file: "__FILE__", line: %d, thread index: %d, " "file offset: %"PRId64", read and expect buffer " "compare result: %d != 0", __LINE__, thread_index, offset, result); return result; } if (fail_count > 0) { fail_count = 0; } remain -= read_bytes; ++i; } return 0; } static int read_test(const int thread_index) { FCFSAPIFileContext fctx; FCFSAPIFileInfo fi; char *expect_buff; char *in_buff; int wait_count; int retry_count; int i; int result; fctx.tid = getpid() + thread_index; fctx.mode = 0755; FDIR_SET_OPERATOR(fctx.oper, geteuid(), getegid(), 0, NULL); if ((result=fcfs_api_open(&fi, filename, O_RDONLY, &fctx)) != 0) { return result; } in_buff = (char *)fc_malloc(read_buffer_size * 2); if (in_buff == NULL) { fcfs_api_close(&fi); return ENOMEM; } expect_buff = in_buff + read_buffer_size; wait_count = retry_count = 0; for (i=0; i 0 || retry_count > 0) { logInfo("file: "__FILE__", line: %d, " "thread index: %d, wait file content ready count: %d, " "retry count: %d", __LINE__, thread_index, wait_count, retry_count); } return result; } static void *read_thread(void *arg) { int thread_index; thread_index = (long)arg; if (read_test(thread_index) == 0) { logInfo("file: "__FILE__", line: %d, thread index: %d, " "read test successfully!", __LINE__, thread_index); } FC_ATOMIC_DEC(read_thread_count); return NULL; } static void sigQuitHandler(int sig) { if (SF_G_CONTINUE_FLAG) { SF_G_CONTINUE_FLAG = false; logInfo("file: "__FILE__", line: %d, " "catch signal %d, program exiting...", __LINE__, sig); } } int main(int argc, char *argv[]) { const bool publish = false; const char *config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME; int ch; int i; int result; int status; struct sigaction act; long thread_index; pthread_t wtid; pthread_t rtid; is_random_read = is_random_write = false; read_thread_count = 1; read_loop_count = 1; file_size = 64 * 1024 * 1024; write_buffer_size = read_buffer_size = 4 * 1024; while ((ch=getopt(argc, argv, "hc:n:s:w:r:t:l:RW")) != -1) { switch (ch) { case 'h': usage(argv); return 0; case 'c': config_filename = optarg; break; case 'n': ns = optarg; break; case 's': if ((result=parse_bytes(optarg, 1, &file_size)) != 0) { return EINVAL; } if (file_size < 1024 * 1024) { fprintf(stderr, "file size: %"PRId64" " "is too small < 1MB", file_size); return EINVAL; } file_size = MEM_ALIGN_CEIL(file_size, 1024 * 1024); break; case 'w': if ((result=parse_bytes(optarg, 1, &write_buffer_size)) != 0) { return EINVAL; } write_buffer_size = MEM_ALIGN_CEIL( write_buffer_size, 1024); break; case 'r': if ((result=parse_bytes(optarg, 1, &read_buffer_size)) != 0) { return EINVAL; } read_buffer_size = MEM_ALIGN_CEIL( read_buffer_size, 1024); break; case 't': read_thread_count = strtol(optarg, NULL, 10); break; case 'l': read_loop_count = strtol(optarg, NULL, 10); break; case 'R': is_random_read = true; break; case 'W': is_random_write = true; break; default: usage(argv); return 1; } } log_init(); //g_log_context.log_level = LOG_DEBUG; if ((result=init()) != 0) { return result; } if ((result=fcfs_api_pooled_init_with_auth(ns, config_filename, publish)) != 0) { return result; } if ((result=check_create_root_path()) != 0) { return result; } if ((result=fcfs_api_start()) != 0) { return result; } memset(&act, 0, sizeof(act)); sigemptyset(&act.sa_mask); act.sa_handler = sigQuitHandler; if(sigaction(SIGINT, &act, NULL) < 0 || sigaction(SIGTERM, &act, NULL) < 0 || sigaction(SIGQUIT, &act, NULL) < 0) { logCrit("file: "__FILE__", line: %d, " "call sigaction fail, errno: %d, error info: %s", __LINE__, errno, strerror(errno)); logCrit("exit abnormally!\n"); return errno; } thread_index = 1; if ((result=pthread_create(&wtid, NULL, write_thread, (void *)(thread_index++))) != 0) { return result; } while ((status=FC_ATOMIC_GET(can_read)) == 0) { fc_sleep_ms(10); } if (status < 0) { return EIO; } running_read_threads = read_thread_count; for (i=0; i * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/ini_file_reader.h" #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "client_global.h" #include "simple_connection_manager.h" #include "client_func.h" int fcfs_auth_alloc_group_servers(FCFSAuthServerGroup *server_group, const int alloc_size) { int bytes; bytes = sizeof(ConnectionInfo) * alloc_size; server_group->servers = (ConnectionInfo *)fc_malloc(bytes); if (server_group->servers == NULL) { return errno != 0 ? errno : ENOMEM; } memset(server_group->servers, 0, bytes); server_group->alloc_size = alloc_size; server_group->count = 0; return 0; } static int fcfs_auth_load_server_config(FCFSAuthClientContext *client_ctx, IniFullContext *ini_ctx) { int result; if ((result=sf_load_cluster_config(&client_ctx->cluster, ini_ctx, FCFS_AUTH_DEFAULT_CLUSTER_PORT)) != 0) { return result; } return 0; } static int fcfs_auth_client_do_init_ex(FCFSAuthClientContext *client_ctx, IniFullContext *ini_ctx) { int result; client_ctx->common_cfg.connect_timeout = iniGetIntValueEx( ini_ctx->section_name, "connect_timeout", ini_ctx->context, SF_DEFAULT_CONNECT_TIMEOUT, true); if (client_ctx->common_cfg.connect_timeout <= 0) { client_ctx->common_cfg.connect_timeout = SF_DEFAULT_CONNECT_TIMEOUT; } client_ctx->common_cfg.network_timeout = iniGetIntValueEx( ini_ctx->section_name, "network_timeout", ini_ctx->context, SF_DEFAULT_NETWORK_TIMEOUT, true); if (client_ctx->common_cfg.network_timeout <= 0) { client_ctx->common_cfg.network_timeout = SF_DEFAULT_NETWORK_TIMEOUT; } if ((result=fcfs_auth_load_server_config(client_ctx, ini_ctx)) != 0) { return result; } if ((result=sf_load_net_retry_config(&client_ctx-> common_cfg.net_retry_cfg, ini_ctx)) != 0) { return result; } return 0; } void fcfs_auth_client_log_config_ex(FCFSAuthClientContext *client_ctx, const char *extra_config) { char net_retry_output[256]; sf_net_retry_config_to_string(&client_ctx->common_cfg.net_retry_cfg, net_retry_output, sizeof(net_retry_output)); logInfo("fauth v%d.%d.%d, " "connect_timeout=%d, " "network_timeout=%d, " "%s, auth_server_count=%d%s%s", g_fcfs_auth_global_vars.version.major, g_fcfs_auth_global_vars.version.minor, g_fcfs_auth_global_vars.version.patch, client_ctx->common_cfg.connect_timeout, client_ctx->common_cfg.network_timeout, net_retry_output, FC_SID_SERVER_COUNT(client_ctx->cluster. server_cfg), (extra_config != NULL ? ", " : ""), (extra_config != NULL ? extra_config : "")); } int fcfs_auth_client_load_from_file_ex1(FCFSAuthClientContext *client_ctx, IniFullContext *ini_ctx) { IniContext iniContext; int result; if (ini_ctx->context == NULL) { if ((result=iniLoadFromFile(ini_ctx->filename, &iniContext)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, ini_ctx->filename, result); return result; } ini_ctx->context = &iniContext; } if ((result=fcfs_auth_client_do_init_ex(client_ctx, ini_ctx)) == 0) { client_ctx->inited = true; } if (ini_ctx->context == &iniContext) { iniFreeContext(&iniContext); ini_ctx->context = NULL; } return result; } int fcfs_auth_client_init_ex2(FCFSAuthClientContext *client_ctx, IniFullContext *ini_ctx, const SFServerGroupIndexType index_type) { int result; int server_group_index; if ((result=fcfs_auth_client_load_from_file_ex1( client_ctx, ini_ctx)) != 0) { return result; } server_group_index = (index_type == sf_server_group_index_type_cluster ? client_ctx->cluster.cluster_group_index : client_ctx->cluster.service_group_index); if ((result=fcfs_auth_simple_connection_manager_init(client_ctx, &client_ctx->cm, server_group_index)) != 0) { return result; } return 0; } void fcfs_auth_client_destroy_ex(FCFSAuthClientContext *client_ctx) { fc_server_destroy(&client_ctx->cluster.server_cfg); fcfs_auth_simple_connection_manager_destroy(&client_ctx->cm); memset(client_ctx, 0, sizeof(FCFSAuthClientContext)); } ================================================ FILE: src/auth/client/client_func.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_CLIENT_FUNC_H #define _FCFS_AUTH_CLIENT_FUNC_H #include "auth_global.h" #include "client_types.h" #ifdef __cplusplus extern "C" { #endif #define fcfs_auth_client_load_from_file(filename) \ fcfs_auth_client_load_from_file_ex((&g_fcfs_auth_client_vars.client_ctx), \ filename, NULL) #define fcfs_auth_client_init_ex(filename, index_type) \ fcfs_auth_client_init_ex1((&g_fcfs_auth_client_vars.client_ctx), \ filename, NULL, index_type) #define fcfs_auth_client_init(filename) \ fcfs_auth_client_init_ex(filename, \ sf_server_group_index_type_service) #define fcfs_auth_client_destroy() \ fcfs_auth_client_destroy_ex((&g_fcfs_auth_client_vars.client_ctx)) #define fcfs_auth_client_log_config(client_ctx) \ fcfs_auth_client_log_config_ex(client_ctx, NULL) int fcfs_auth_client_load_from_file_ex1(FCFSAuthClientContext *client_ctx, IniFullContext *ini_ctx); /** * client initial from config file * params: * client_ctx: the client context * config_filename: the client config filename * section_name: the section name, NULL or empty for global section * return: 0 success, != 0 fail, return the error code **/ static inline int fcfs_auth_client_load_from_file_ex(FCFSAuthClientContext *client_ctx, const char *config_filename, const char *section_name) { IniFullContext ini_ctx; FAST_INI_SET_FULL_CTX(ini_ctx, config_filename, section_name); return fcfs_auth_client_load_from_file_ex1(client_ctx, &ini_ctx); } int fcfs_auth_client_init_ex2(FCFSAuthClientContext *client_ctx, IniFullContext *ini_ctx, const SFServerGroupIndexType index_type); static inline int fcfs_auth_client_init_ex1(FCFSAuthClientContext *client_ctx, const char *config_filename, const char *section_name, const SFServerGroupIndexType index_type) { IniFullContext ini_ctx; FAST_INI_SET_FULL_CTX(ini_ctx, config_filename, section_name); return fcfs_auth_client_init_ex2(client_ctx, &ini_ctx, index_type); } /** * client destroy function * params: * client_ctx: tracker group * return: none **/ void fcfs_auth_client_destroy_ex(FCFSAuthClientContext *client_ctx); int fcfs_auth_alloc_group_servers(FCFSAuthServerGroup *server_group, const int alloc_size); void fcfs_auth_client_log_config_ex(FCFSAuthClientContext *client_ctx, const char *extra_config); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/client/client_global.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "client_global.h" FCFSAuthClientGlobalVars g_fcfs_auth_client_vars = { true, false, {false} }; ================================================ FILE: src/auth/client/client_global.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_CLIENT_GLOBAL_H #define _FCFS_AUTH_CLIENT_GLOBAL_H #include "auth_global.h" #include "client_types.h" typedef struct fcfs_auth_client_global_vars { bool need_load_passwd; //false for auth server bool ignore_when_passwd_not_exist; //true for fdir and fstore servers FCFSAuthClientContext client_ctx; } FCFSAuthClientGlobalVars; #define fcfs_auth_client_init_full_ctx_ex(auth, client_ctx) \ (auth)->ctx = client_ctx #define fcfs_auth_client_init_full_ctx(auth) \ fcfs_auth_client_init_full_ctx_ex(auth, &g_fcfs_auth_client_vars.client_ctx) #ifdef __cplusplus extern "C" { #endif extern FCFSAuthClientGlobalVars g_fcfs_auth_client_vars; #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/client/client_proto.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/ini_file_reader.h" #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/connection_pool.h" #include "auth_proto.h" #include "auth_func.h" #include "client_global.h" #include "client_proto.h" #define check_name(name, caption) check_name_ex(name, caption, true) #define check_username_ex(username, required) \ check_name_ex(username, "username", required) #define check_username(username) check_username_ex(username, true) #define pack_username_ex(username, proto_uname, required) \ pack_name_ex(username, "username", proto_uname, 0, required) #define pack_username(username, proto_uname) \ pack_username_ex(username, proto_uname, true) #define check_poolname_ex(poolname, required) \ check_name_ex(poolname, "poolname", required) #define check_poolname(poolname) check_poolname_ex(poolname, true) #define pack_poolname_ex(poolname, proto_pname, str_offset, required) \ pack_name_ex(poolname, "poolname", proto_pname, str_offset, required) #define pack_poolname(poolname, proto_pname) \ pack_poolname_ex(poolname, proto_pname, 0, true) #define pack_user_pool_pair(username, poolname, up_pair) \ pack_user_pool_pair_ex(username, poolname, up_pair, true) static inline int check_name_ex(const string_t *name, const char *caption, const bool required) { int min_length; min_length = required ? 1 : 0; if (name->len < min_length || name->len > NAME_MAX) { logError("file: "__FILE__", line: %d, " "invalid %s length: %d, which <= 0 or > %d", __LINE__, caption, name->len, NAME_MAX); return EINVAL; } return 0; } static inline int pack_name_ex(const string_t *name, const char *caption, FCFSAuthProtoNameInfo *proto_name, const int str_offset, const bool required) { int result; if ((result=check_name_ex(name, caption, required)) != 0) { return result; } if (name->len > 0) { memcpy(proto_name->str + str_offset, name->str, name->len); } proto_name->len = name->len; return 0; } static inline int pack_user_pool_pair_ex(const string_t *username, const string_t *poolname, FCFSAuthProtoUserPoolPair *up_pair, const bool required) { int result; if ((result=check_username_ex(username, required)) != 0) { return result; } if ((result=check_poolname_ex(poolname, required)) != 0) { return result; } fcfs_auth_pack_user_pool_pair(username, poolname, up_pair); return 0; } static inline int pack_user_passwd_pair(const string_t *username, const string_t *passwd, FCFSAuthProtoUserPasswdPair *up_pair) { if (passwd->len != FCFS_AUTH_PASSWD_LEN) { logError("file: "__FILE__", line: %d, " "invalid password length: %d != %d", __LINE__, passwd->len, FCFS_AUTH_PASSWD_LEN); return EINVAL; } memcpy(up_pair->passwd, passwd->str, passwd->len); return pack_username(username, &up_pair->username); } int fcfs_auth_client_proto_user_login(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *passwd, const string_t *poolname, const int flags) { FCFSAuthProtoHeader *header; FCFSAuthProtoUserLoginReq *req; FCFSAuthProtoNameInfo *proto_pname; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoUserLoginReq) + 2 * NAME_MAX + FCFS_AUTH_PASSWD_LEN]; SFResponseInfo response; FCFSAuthProtoUserLoginResp login_resp; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoUserLoginReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + username->len + poolname->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); req->flags = flags; if ((result=pack_user_passwd_pair(username, passwd, &req->up_pair)) != 0) { return result; } proto_pname = (FCFSAuthProtoNameInfo *)(req-> up_pair.username.str + username->len); if ((result=pack_poolname_ex(poolname, proto_pname, 0, false)) != 0) { return result; } response.error.length = 0; if ((result=sf_send_and_recv_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_RESP, (char *)&login_resp, sizeof(login_resp))) == 0) { memcpy(client_ctx->session.id, login_resp.session_id, FCFS_AUTH_SESSION_ID_LEN); } else { auth_log_network_error(&response, conn, result); } return result; } int fcfs_auth_client_proto_session_subscribe( FCFSAuthClientContext *client_ctx, ConnectionInfo *conn) { FCFSAuthProtoHeader *header; FCFSAuthProtoSessionSubscribeReq *req; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSessionSubscribeReq) + NAME_MAX + FCFS_AUTH_PASSWD_LEN]; SFResponseInfo response; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoSessionSubscribeReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + client_ctx->auth_cfg.username.len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); if ((result=pack_user_passwd_pair(&client_ctx->auth_cfg.username, &client_ctx->auth_cfg.passwd, &req->up_pair)) != 0) { return result; } response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_RESP)) != 0) { auth_log_network_error(&response, conn, result); } return result; } int fcfs_auth_client_proto_session_validate( FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *session_id, const string_t *validate_key, const FCFSAuthValidatePriviledgeType priv_type, const int64_t pool_id, const int64_t priv_required) { FCFSAuthProtoHeader *header; FCFSAuthProtoSessionValidateReq *req; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSessionValidateReq) + NAME_MAX + FCFS_AUTH_PASSWD_LEN]; FCFSAuthProtoSessionValidateResp validate_resp; SFResponseInfo response; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoSessionValidateReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req); SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); if (validate_key->len != FCFS_AUTH_PASSWD_LEN) { logError("file: "__FILE__", line: %d, " "invalid session validate key length: %d != %d", __LINE__, validate_key->len, FCFS_AUTH_PASSWD_LEN); return EINVAL; } memcpy(req->validate_key, validate_key->str, validate_key->len); if (session_id->len != FCFS_AUTH_SESSION_ID_LEN) { logError("file: "__FILE__", line: %d, " "invalid session id length: %d != %d", __LINE__, session_id->len, FCFS_AUTH_SESSION_ID_LEN); return EINVAL; } memcpy(req->session_id, session_id->str, session_id->len); req->priv_type = priv_type; long2buff(pool_id, req->pool_id); long2buff(priv_required, req->priv_required); response.error.length = 0; if ((result=sf_send_and_recv_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_RESP, (char *)&validate_resp, sizeof(validate_resp))) == 0) { result = buff2int(validate_resp.result); } else { auth_log_network_error(&response, conn, result); } return result; } int fcfs_auth_client_proto_user_create(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const FCFSAuthUserInfo *user) { FCFSAuthProtoHeader *header; FCFSAuthProtoUserCreateReq *req; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoUserCreateReq) + NAME_MAX + FCFS_AUTH_PASSWD_LEN]; SFResponseInfo response; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoUserCreateReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + user->name.len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); if ((result=pack_user_passwd_pair(&user->name, &user->passwd, &req->up_pair)) != 0) { return result; } long2buff(user->priv, req->priv); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_USER_CREATE_RESP)) != 0) { auth_log_network_error(&response, conn, result); } return result; } int fcfs_auth_client_proto_user_passwd(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *passwd) { FCFSAuthProtoHeader *header; FCFSAuthProtoUserPasswdReq *req; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoUserPasswdReq) + NAME_MAX + FCFS_AUTH_PASSWD_LEN]; SFResponseInfo response; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoUserPasswdReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + username->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); if ((result=pack_user_passwd_pair(username, passwd, &req->up_pair)) != 0) { return result; } response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_RESP)) != 0) { auth_log_network_error(&response, conn, result); } return result; } typedef int (*client_proto_list_func)(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, SFProtoRecvBuffer *buffer, struct fast_mpool_man *mpool, void *array, bool *is_last); static int client_proto_user_list_do(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, SFProtoRecvBuffer *buffer, struct fast_mpool_man *mpool, FCFSAuthUserArray *array, bool *is_last) { FCFSAuthProtoHeader *header; FCFSAuthProtoUserListReq *req; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoUserListReq) + NAME_MAX]; SFResponseInfo response; FCFSAuthProtoListRespHeader *resp_header; FCFSAuthProtoUserListRespBodyPart *body_part; FCFSAuthUserInfo *user; FCFSAuthUserInfo *end; char *p; int out_bytes; int count; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoUserListReq *)(header + 1); if ((result=pack_username_ex(username, &req->username, false)) != 0) { return result; } out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoUserListReq) + username->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_LIST_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); sf_proto_pack_limit(limit, &req->limit); response.error.length = 0; if ((result=sf_send_and_recv_vary_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_USER_LIST_RESP, buffer, sizeof(FCFSAuthProtoListRespHeader))) != 0) { auth_log_network_error(&response, conn, result); return result; } resp_header = (FCFSAuthProtoListRespHeader *)buffer->buff; count = buff2int(resp_header->count); *is_last = resp_header->is_last; if ((result=fcfs_auth_user_check_realloc_array(array, array->count + count)) != 0) { return result; } p = (char *)(resp_header + 1); end = array->users + array->count + count; for (user=array->users+array->count; userpriv = buff2long(body_part->priv); if ((result=fast_mpool_alloc_string_ex(mpool, &user->name, body_part->username.str, body_part->username.len)) != 0) { return result; } p += sizeof(FCFSAuthProtoUserListRespBodyPart) + user->name.len; } if (response.header.body_len != p - buffer->buff) { logError("file: "__FILE__", line: %d, " "response body length: %d != expect: %d", __LINE__, response.header.body_len, (int)(p - buffer->buff)); return EINVAL; } array->count += count; return result; } static int client_proto_list_wrapper(client_proto_list_func list_func, FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, void *array, int *count) { int result; bool is_last; SFProtoRBufferFixedWrapper buffer_wrapper; SFListLimitInfo new_limit; sf_init_recv_buffer_by_wrapper(&buffer_wrapper); if (limit->count > 0 && limit->count <= 64) { result = list_func(client_ctx, conn, username, poolname, limit, &buffer_wrapper.buffer, mpool, array, &is_last); sf_free_recv_buffer(&buffer_wrapper.buffer); return result; } result = 0; is_last = false; while (!is_last) { new_limit.offset = limit->offset + *count; if (limit->count <= 0) { new_limit.count = 0; } else { new_limit.count = limit->count - *count; if (new_limit.count == 0) { break; } } if ((result=list_func(client_ctx, conn, username, poolname, &new_limit, &buffer_wrapper.buffer, mpool, array, &is_last)) != 0) { break; } } sf_free_recv_buffer(&buffer_wrapper.buffer); return result; } int fcfs_auth_client_proto_user_list(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthUserArray *array) { const string_t *poolname = NULL; return client_proto_list_wrapper((client_proto_list_func) client_proto_user_list_do, client_ctx, conn, username, poolname, limit, mpool, array, &array->count); } int fcfs_auth_client_proto_user_grant(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const int64_t priv) { FCFSAuthProtoHeader *header; FCFSAuthProtoUserGrantReq *req; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoUserGrantReq) + NAME_MAX]; SFResponseInfo response; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoUserGrantReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + username->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_GRANT_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); if ((result=pack_username(username, &req->username)) != 0) { return result; } long2buff(priv, req->priv); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_USER_GRANT_RESP)) != 0) { auth_log_network_error(&response, conn, result); } return result; } int fcfs_auth_client_proto_user_remove(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username) { FCFSAuthProtoHeader *header; FCFSAuthProtoUserRemoveReq *req; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoUserRemoveReq) + NAME_MAX]; SFResponseInfo response; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoUserRemoveReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + username->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); if ((result=pack_username(username, &req->username)) != 0) { return result; } response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_RESP)) != 0) { auth_log_network_error(&response, conn, result); } return result; } int fcfs_auth_client_proto_spool_create(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, FCFSAuthStoragePoolInfo *spool, const int name_size, const bool dryrun) { FCFSAuthProtoHeader *header; FCFSAuthProtoSPoolCreateReq *req; FCFSAuthProtoSPoolCreateResp *resp; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSPoolCreateReq) + NAME_MAX]; char in_buff[sizeof(FCFSAuthProtoSPoolCreateResp) + NAME_MAX]; SFResponseInfo response; int out_bytes; int body_len; int buff_size; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoSPoolCreateReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + spool->name.len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); if ((result=pack_poolname_ex(&spool->name, &req->poolname, 0, false)) != 0) { return result; } long2buff(spool->quota, req->quota); req->dryrun = (dryrun ? 1 : 0); if (name_size < sizeof(in_buff) - sizeof(*resp)) { buff_size = name_size; } else { buff_size = sizeof(in_buff); } response.error.length = 0; if ((result=sf_send_and_recv_response_ex1(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_RESP, in_buff, buff_size, &body_len)) != 0) { auth_log_network_error(&response, conn, result); return result; } resp = (FCFSAuthProtoSPoolCreateResp *)in_buff; if (body_len != sizeof(*resp) + resp->poolname.len) { logError("file: "__FILE__", line: %d, " "response body length: %d != expect: %d", __LINE__, body_len, (int)sizeof(*resp) + resp->poolname.len); return EINVAL; } spool->name.len = resp->poolname.len; memcpy(spool->name.str, resp->poolname.str, spool->name.len); return 0; } int client_proto_spool_list_do(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, SFProtoRecvBuffer *buffer, struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *array, bool *is_last) { FCFSAuthProtoHeader *header; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSPoolListReq) + 2 * NAME_MAX]; SFResponseInfo response; FCFSAuthProtoSPoolListReq *req; FCFSAuthProtoListRespHeader *resp_header; FCFSAuthProtoSPoolListRespBodyPart *body_part; FCFSAuthStoragePoolInfo *spool; FCFSAuthStoragePoolInfo *end; char *p; int out_bytes; int count; int result; header = (FCFSAuthProtoHeader *)out_buff; out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSPoolListReq) + username->len + poolname->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); req = (FCFSAuthProtoSPoolListReq *)(header + 1); if ((result=pack_user_pool_pair_ex(username, poolname, &req->up_pair, false)) != 0) { return result; } sf_proto_pack_limit(limit, &req->limit); response.error.length = 0; if ((result=sf_send_and_recv_vary_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_RESP, buffer, sizeof(FCFSAuthProtoListRespHeader))) != 0) { auth_log_network_error(&response, conn, result); return result; } resp_header = (FCFSAuthProtoListRespHeader *)buffer->buff; count = buff2int(resp_header->count); *is_last = resp_header->is_last; if ((result=fcfs_auth_spool_check_realloc_array(array, array->count + count)) != 0) { return result; } p = (char *)(resp_header + 1); end = array->spools + array->count + count; for (spool=array->spools+array->count; spoolquota = buff2long(body_part->quota); if ((result=fast_mpool_alloc_string_ex(mpool, &spool->name, body_part->poolname.str, body_part->poolname.len)) != 0) { return result; } p += sizeof(FCFSAuthProtoSPoolListRespBodyPart) + spool->name.len; } if (response.header.body_len != p - buffer->buff) { logError("file: "__FILE__", line: %d, " "response body length: %d != expect: %d", __LINE__, response.header.body_len, (int)(p - buffer->buff)); return EINVAL; } array->count += count; return result; } int fcfs_auth_client_proto_spool_list(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *array) { return client_proto_list_wrapper((client_proto_list_func) client_proto_spool_list_do, client_ctx, conn, username, poolname, limit, mpool, array, &array->count); } int fcfs_auth_client_proto_spool_remove(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *poolname) { FCFSAuthProtoHeader *header; FCFSAuthProtoSPoolRemoveReq *req; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSPoolRemoveReq) + NAME_MAX]; SFResponseInfo response; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoSPoolRemoveReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + poolname->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); if ((result=pack_poolname(poolname, &req->poolname)) != 0) { return result; } response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_RESP)) != 0) { auth_log_network_error(&response, conn, result); } return result; } int fcfs_auth_client_proto_spool_set_quota(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *poolname, const int64_t quota) { FCFSAuthProtoHeader *header; FCFSAuthProtoSPoolSetQuotaReq *req; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSPoolSetQuotaReq) + NAME_MAX]; SFResponseInfo response; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoSPoolSetQuotaReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + poolname->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); if ((result=pack_poolname(poolname, &req->poolname)) != 0) { return result; } long2buff(quota, req->quota); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_RESP)) != 0) { auth_log_network_error(&response, conn, result); } return result; } int fcfs_auth_client_proto_spool_get_quota(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *poolname, int64_t *quota) { FCFSAuthProtoHeader *header; FCFSAuthProtoSPoolGetQuotaReq *req; FCFSAuthProtoSPoolGetQuotaResp resp; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSPoolGetQuotaReq) + NAME_MAX]; SFResponseInfo response; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; req = (FCFSAuthProtoSPoolGetQuotaReq *)(header + 1); out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + poolname->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); if ((result=pack_poolname(poolname, &req->poolname)) != 0) { return result; } response.error.length = 0; if ((result=sf_send_and_recv_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_RESP, (char *)&resp, sizeof(resp))) == 0) { *quota = buff2long(resp.quota); } else { auth_log_network_error(&response, conn, result); } return result; } int fcfs_auth_client_proto_gpool_grant(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const FCFSAuthSPoolPriviledges *privs) { FCFSAuthProtoHeader *header; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSPoolGrantReq) + 2 * NAME_MAX]; SFResponseInfo response; FCFSAuthProtoSPoolGrantReq *req; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSPoolGrantReq) + username->len + poolname->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); req = (FCFSAuthProtoSPoolGrantReq *)(header + 1); if ((result=pack_user_pool_pair(username, poolname, &req->up_pair)) != 0) { return result; } int2buff(privs->fdir, req->privs.fdir); int2buff(privs->fstore, req->privs.fstore); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_RESP)) != 0) { auth_log_network_error(&response, conn, result); } return result; } int fcfs_auth_client_proto_gpool_withdraw(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname) { FCFSAuthProtoHeader *header; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSPoolWithdrawReq) + 2 * NAME_MAX]; SFResponseInfo response; FCFSAuthProtoSPoolWithdrawReq *req; int out_bytes; int result; header = (FCFSAuthProtoHeader *)out_buff; out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoSPoolWithdrawReq) + username->len + poolname->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); req = (FCFSAuthProtoSPoolWithdrawReq *)(header + 1); if ((result=pack_user_pool_pair(username, poolname, &req->up_pair)) != 0) { return result; } response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_RESP)) != 0) { auth_log_network_error(&response, conn, result); } return result; } static int client_proto_gpool_list_do(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, SFProtoRecvBuffer *buffer, struct fast_mpool_man *mpool, FCFSAuthGrantedPoolArray *array, bool *is_last) { FCFSAuthProtoHeader *header; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoGPoolListReq) + 2 * NAME_MAX]; SFResponseInfo response; FCFSAuthProtoGPoolListReq *req; FCFSAuthProtoListRespHeader *resp_header; FCFSAuthProtoGPoolListRespBodyPart *body_part; FCFSAuthProtoNameInfo *proto_pname; FCFSAuthGrantedPoolFullInfo *gpool; FCFSAuthGrantedPoolFullInfo *end; char *p; int out_bytes; int count; int result; header = (FCFSAuthProtoHeader *)out_buff; out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoGPoolListReq) + username->len + poolname->len; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ, out_bytes - sizeof(FCFSAuthProtoHeader)); req = (FCFSAuthProtoGPoolListReq *)(header + 1); if ((result=pack_user_pool_pair_ex(username, poolname, &req->up_pair, false)) != 0) { return result; } sf_proto_pack_limit(limit, &req->limit); response.error.length = 0; if ((result=sf_send_and_recv_vary_response(conn, out_buff, out_bytes, &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_RESP, buffer, sizeof(FCFSAuthProtoListRespHeader))) != 0) { auth_log_network_error(&response, conn, result); return result; } resp_header = (FCFSAuthProtoListRespHeader *)buffer->buff; count = buff2int(resp_header->count); *is_last = resp_header->is_last; if ((result=fcfs_auth_gpool_check_realloc_array(array, array->count + count)) != 0) { return result; } p = (char *)(resp_header + 1); end = array->gpools + array->count + count; for (gpool=array->gpools+array->count; gpoolgranted.privs.fdir = buff2int(body_part->privs.fdir); gpool->granted.privs.fstore = buff2int(body_part->privs.fstore); if ((result=fast_mpool_alloc_string_ex(mpool, &gpool->username, body_part->up_pair.username.str, body_part->up_pair.username.len)) != 0) { return result; } proto_pname = (FCFSAuthProtoNameInfo *)(body_part->up_pair. username.str + body_part->up_pair.username.len); if ((result=fast_mpool_alloc_string_ex(mpool, &gpool->pool_name, proto_pname->str, proto_pname->len)) != 0) { return result; } p += sizeof(FCFSAuthProtoGPoolListRespBodyPart) + gpool->username.len + gpool->pool_name.len; } if (response.header.body_len != p - buffer->buff) { logError("file: "__FILE__", line: %d, " "response body length: %d != expect: %d", __LINE__, response.header.body_len, (int)(p - buffer->buff)); return EINVAL; } array->count += count; return result; } int fcfs_auth_client_proto_gpool_list(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthGrantedPoolArray *array) { return client_proto_list_wrapper((client_proto_list_func) client_proto_gpool_list_do, client_ctx, conn, username, poolname, limit, mpool, array, &array->count); } int fcfs_auth_client_get_master(FCFSAuthClientContext *client_ctx, FCFSAuthClientServerEntry *master) { const bool shared = false; int result; ConnectionInfo *conn; FCFSAuthProtoHeader *header; SFResponseInfo response; FCFSAuthProtoGetServerResp server_resp; char out_buff[sizeof(FCFSAuthProtoHeader)]; conn = client_ctx->cm.ops.get_connection( &client_ctx->cm, 0, shared, &result); if (conn == NULL) { return result; } header = (FCFSAuthProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ, sizeof(out_buff) - sizeof(FCFSAuthProtoHeader)); response.error.length = 0; if ((result=sf_send_and_recv_response(conn, out_buff, sizeof(out_buff), &response, client_ctx->common_cfg.network_timeout, FCFS_AUTH_SERVICE_PROTO_GET_MASTER_RESP, (char *)&server_resp, sizeof(FCFSAuthProtoGetServerResp))) != 0) { auth_log_network_error(&response, conn, result); } else { master->server_id = buff2int(server_resp.server_id); memcpy(master->conn.ip_addr, server_resp.ip_addr, IP_ADDRESS_SIZE); *(master->conn.ip_addr + IP_ADDRESS_SIZE - 1) = '\0'; master->conn.port = buff2short(server_resp.port); master->conn.comm_type = conn->comm_type; } SF_CLIENT_RELEASE_CONNECTION(&client_ctx->cm, conn, result); return result; } int fcfs_auth_client_cluster_stat(FCFSAuthClientContext *client_ctx, FCFSAuthClientClusterStatEntry *stats, const int size, int *count) { const bool shared = false; FCFSAuthProtoHeader *header; FCFSAuthProtoClusterStatRespBodyHeader *body_header; FCFSAuthProtoClusterStatRespBodyPart *body_part; FCFSAuthProtoClusterStatRespBodyPart *body_end; FCFSAuthClientClusterStatEntry *stat; ConnectionInfo *conn; char out_buff[sizeof(FCFSAuthProtoHeader)]; char fixed_buff[8 * 1024]; char *in_buff; SFResponseInfo response; int result; int calc_size; if ((conn=client_ctx->cm.ops.get_master_connection(&client_ctx->cm, 0, shared, &result)) == NULL) { return result; } header = (FCFSAuthProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_REQ, sizeof(out_buff) - sizeof(FCFSAuthProtoHeader)); response.error.length = 0; in_buff = fixed_buff; if ((result=sf_send_and_check_response_header(conn, out_buff, sizeof(out_buff), &response, client_ctx->common_cfg. network_timeout, FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_RESP)) == 0) { if (response.header.body_len > sizeof(fixed_buff)) { in_buff = (char *)fc_malloc(response.header.body_len); if (in_buff == NULL) { response.error.length = sprintf(response.error.message, "malloc %d bytes fail", response.header.body_len); result = ENOMEM; } } if (result == 0) { result = tcprecvdata_nb(conn->sock, in_buff, response.header.body_len, client_ctx-> common_cfg.network_timeout); } } body_header = (FCFSAuthProtoClusterStatRespBodyHeader *)in_buff; body_part = (FCFSAuthProtoClusterStatRespBodyPart *)(in_buff + sizeof(FCFSAuthProtoClusterStatRespBodyHeader)); if (result == 0) { *count = buff2int(body_header->count); calc_size = sizeof(FCFSAuthProtoClusterStatRespBodyHeader) + (*count) * sizeof(FCFSAuthProtoClusterStatRespBodyPart); if (calc_size != response.header.body_len) { response.error.length = sprintf(response.error.message, "response body length: %d != calculate size: %d, " "server count: %d", response.header.body_len, calc_size, *count); result = EINVAL; } else if (size < *count) { response.error.length = sprintf(response.error.message, "entry size %d too small < %d", size, *count); *count = 0; result = ENOSPC; } } else { *count = 0; } if (result != 0) { auth_log_network_error(&response, conn, result); } else { body_end = body_part + (*count); for (stat=stats; body_partis_online = body_part->is_online; stat->is_master = body_part->is_master; stat->server_id = buff2int(body_part->server_id); memcpy(stat->ip_addr, body_part->ip_addr, IP_ADDRESS_SIZE); *(stat->ip_addr + IP_ADDRESS_SIZE - 1) = '\0'; stat->port = buff2short(body_part->port); } } SF_CLIENT_RELEASE_CONNECTION(&client_ctx->cm, conn, result); if (in_buff != fixed_buff) { if (in_buff != NULL) { free(in_buff); } } return result; } ================================================ FILE: src/auth/client/client_proto.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_CLIENT_PROTO_H #define _FCFS_AUTH_CLIENT_PROTO_H #include "fastcommon/fast_mpool.h" #include "sf/sf_proto.h" #include "client_types.h" typedef struct fcfs_auth_client_cluster_stat_entry { int server_id; bool is_online; bool is_master; uint16_t port; char ip_addr[IP_ADDRESS_SIZE]; } FCFSAuthClientClusterStatEntry; #ifdef __cplusplus extern "C" { #endif int fcfs_auth_client_get_master(FCFSAuthClientContext *client_ctx, FCFSAuthClientServerEntry *master); int fcfs_auth_client_cluster_stat(FCFSAuthClientContext *client_ctx, FCFSAuthClientClusterStatEntry *stats, const int size, int *count); int fcfs_auth_client_proto_session_subscribe( FCFSAuthClientContext *client_ctx, ConnectionInfo *conn); int fcfs_auth_client_proto_session_validate( FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *session_id, const string_t *validate_key, const FCFSAuthValidatePriviledgeType priv_type, const int64_t pool_id, const int64_t priv_required); int fcfs_auth_client_proto_user_login(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *passwd, const string_t *poolname, const int flags); int fcfs_auth_client_proto_user_create(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const FCFSAuthUserInfo *user); int fcfs_auth_client_proto_user_passwd(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *passwd); int fcfs_auth_client_proto_user_list(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthUserArray *array); int fcfs_auth_client_proto_user_grant(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const int64_t priv); int fcfs_auth_client_proto_user_remove(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username); int fcfs_auth_client_proto_spool_create(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, FCFSAuthStoragePoolInfo *spool, const int name_size, const bool dryrun); int fcfs_auth_client_proto_spool_list(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *array); int fcfs_auth_client_proto_spool_remove(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *poolname); int fcfs_auth_client_proto_spool_set_quota(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *poolname, const int64_t quota); int fcfs_auth_client_proto_spool_get_quota(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *poolname, int64_t *quota); int fcfs_auth_client_proto_gpool_grant(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const FCFSAuthSPoolPriviledges *privs); int fcfs_auth_client_proto_gpool_withdraw(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname); int fcfs_auth_client_proto_gpool_list(FCFSAuthClientContext *client_ctx, ConnectionInfo *conn, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthGrantedPoolArray *array); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/client/client_types.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_CLIENT_TYPES_H #define _FCFS_AUTH_CLIENT_TYPES_H #include "fastcommon/common_define.h" #include "sf/sf_configs.h" #include "sf/sf_connection_manager.h" #include "sf/sf_cluster_cfg.h" #include "auth_types.h" #define FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME "/etc/fastcfs/auth/client.conf" typedef struct fcfs_auth_client_user_key_pair { string_t username; string_t key_filename; } FCFSAuthClientUserKeyPair; typedef struct fcfs_auth_client_server_entry { int server_id; ConnectionInfo conn; char status; } FCFSAuthClientServerEntry; typedef struct fcfs_auth_server_group { int alloc_size; int count; ConnectionInfo *servers; } FCFSAuthServerGroup; typedef struct fcfs_auth_client_common_cfg { string_t username; string_t passwd; string_t secret_key_filename; unsigned char passwd_buff[FCFS_AUTH_PASSWD_LEN]; char *buff; //for username and secret_key_filename } FCFSAuthClientCommonCfg; typedef struct fcfs_auth_client_context { bool inited; SFClusterConfig cluster; SFConnectionManager cm; FCFSAuthClientCommonCfg auth_cfg; SFClientCommonConfig common_cfg; struct { string_t *poolname; char id[FCFS_AUTH_SESSION_ID_LEN]; } session; } FCFSAuthClientContext; typedef struct fcfs_auth_client_full_context { bool enabled; FCFSAuthClientContext *ctx; } FCFSAuthClientFullContext; #endif ================================================ FILE: src/auth/client/fcfs_auth_client.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "sf/idempotency/client/rpc_wrapper.h" #include "../common/auth_func.h" #include "fcfs_auth_client.h" #define GET_MASTER_CONNECTION(cm, arg1, result) \ (cm)->ops.get_master_connection(cm, arg1, true, result) static int load_auth_user_passwd(FCFSAuthClientCommonCfg *auth_cfg, IniContext *ini_context, const char *auth_config_filename) { int result; bool need_resolve; string_t username; string_t secret_key_filename; FilenameString new_key_filename; char full_secret_filename[PATH_MAX]; string_t new_filename; if (g_fcfs_auth_client_vars.client_ctx.auth_cfg.username.str == NULL) { username.str = iniGetStrValue(NULL, "username", ini_context); if (username.str == NULL || *(username.str) == '\0') { username.str = "admin"; } username.len = strlen(username.str); } else { username = g_fcfs_auth_client_vars.client_ctx.auth_cfg.username; } if (g_fcfs_auth_client_vars.client_ctx.auth_cfg. secret_key_filename.str == NULL) { secret_key_filename.str = iniGetStrValue(NULL, "secret_key_filename", ini_context); if (secret_key_filename.str == NULL || *(secret_key_filename.str) == '\0') { secret_key_filename.str = "keys/${username}.key"; } secret_key_filename.len = strlen(secret_key_filename.str); need_resolve = true; } else { secret_key_filename = g_fcfs_auth_client_vars. client_ctx.auth_cfg.secret_key_filename; need_resolve = false; } auth_cfg->passwd.str = (char *)auth_cfg->passwd_buff; auth_cfg->passwd.len = FCFS_AUTH_PASSWD_LEN; fcfs_auth_replace_filename_with_username(&secret_key_filename, &username, &new_key_filename); if (need_resolve) { new_filename.str = full_secret_filename; new_filename.len = resolve_path(auth_config_filename, FC_FILENAME_STRING_PTR(new_key_filename), full_secret_filename, sizeof(full_secret_filename)); } else { new_filename.str = FC_FILENAME_STRING_PTR(new_key_filename); new_filename.len = strlen(new_filename.str); } if (g_fcfs_auth_client_vars.need_load_passwd) { if ((result=fcfs_auth_load_passwd_ex(new_filename.str, auth_cfg->passwd_buff, g_fcfs_auth_client_vars. ignore_when_passwd_not_exist)) != 0) { logError("file: "__FILE__", line: %d, " "config file: %s, secret_key_filename: %s, " "load password fail, ret code: %d", __LINE__, auth_config_filename, new_filename.str, result); return result; } } else { memset(auth_cfg->passwd_buff, 0, FCFS_AUTH_PASSWD_LEN); } auth_cfg->buff = (char *)fc_malloc(username.len + 1 + new_filename.len + 1); if (auth_cfg->buff == NULL) { return ENOMEM; } auth_cfg->username.str = auth_cfg->buff; auth_cfg->username.len = username.len; memcpy(auth_cfg->username.str, username.str, username.len + 1); auth_cfg->secret_key_filename.str = auth_cfg->username.str + username.len + 1; auth_cfg->secret_key_filename.len = new_filename.len; memcpy(auth_cfg->secret_key_filename.str, new_filename.str, new_filename.len + 1); return 0; } static int load_auth_config(FCFSAuthClientFullContext *auth, const char *auth_config_filename, const SFServerGroupIndexType index_type) { int result; IniContext ini_context; char *client_config_filename; char full_config_filename[PATH_MAX]; if ((result=iniLoadFromFile(auth_config_filename, &ini_context)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, auth_config_filename, result); return result; } auth->enabled = iniGetBoolValue(NULL, "auth_enabled", &ini_context, false); do { if ((!auth->enabled) || auth->ctx->inited) { break; } if ((result=load_auth_user_passwd(&auth->ctx->auth_cfg, &ini_context, auth_config_filename)) != 0) { break; } client_config_filename = iniGetStrValue(NULL, "client_config_filename", &ini_context); if (client_config_filename == NULL || *client_config_filename == '\0') { logError("file: "__FILE__", line: %d, " "config file: %s, item \"client_config_filename\" " "not exist or empty", __LINE__, auth_config_filename); result = ENOENT; break; } resolve_path(auth_config_filename, client_config_filename, full_config_filename, sizeof(full_config_filename)); result = fcfs_auth_client_init_ex1(auth->ctx, full_config_filename, NULL, index_type); } while (0); iniFreeContext(&ini_context); return result; } int fcfs_auth_load_config_ex(FCFSAuthClientFullContext *auth, const char *config_filename, const char *section_name, const SFServerGroupIndexType index_type) { int result; IniContext ini_context; char *auth_config_filename; char full_auth_filename[PATH_MAX]; if ((result=iniLoadFromFile(config_filename, &ini_context)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, config_filename, result); return result; } auth_config_filename = iniGetStrValue(section_name, "auth_config_filename", &ini_context); if (auth_config_filename == NULL || *auth_config_filename == '\0') { logError("file: "__FILE__", line: %d, " "config file: %s, item \"auth_config_filename\" " "not exist or empty", __LINE__, config_filename); result = ENOENT; } else { resolve_path(config_filename, auth_config_filename, full_auth_filename, sizeof(full_auth_filename)); result = load_auth_config(auth, full_auth_filename, index_type); } iniFreeContext(&ini_context); return result; } void fcfs_auth_config_to_string_ex(const FCFSAuthClientFullContext *auth, const char *caption, char *output, const int size) { int len; len = snprintf(output, size, "%s{enabled: %d", caption, auth->enabled); if (auth->enabled) { len += snprintf(output + len, size - len, ", username: %s, " "secret_key_filename: %s", auth->ctx->auth_cfg.username.str, auth->ctx->auth_cfg.secret_key_filename.str); } snprintf(output + len, size - len, "}"); } int fcfs_auth_client_user_login_ex(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *passwd, const string_t *poolname, const int flags) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_login, username, passwd, poolname, flags); } int fcfs_auth_client_session_subscribe(FCFSAuthClientContext *client_ctx) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_session_subscribe); } int fcfs_auth_client_session_validate(FCFSAuthClientContext *client_ctx, const string_t *session_id, const string_t *validate_key, const FCFSAuthValidatePriviledgeType priv_type, const int64_t pool_id, const int64_t priv_required) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_session_validate, session_id, validate_key, priv_type, pool_id, priv_required); } int fcfs_auth_client_user_create(FCFSAuthClientContext *client_ctx, const FCFSAuthUserInfo *user) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_create, user); } int fcfs_auth_client_user_passwd(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *passwd) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_passwd, username, passwd); } int fcfs_auth_client_user_list(FCFSAuthClientContext *client_ctx, const string_t *username, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthUserArray *array) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_list, username, limit, mpool, array); } int fcfs_auth_client_user_grant(FCFSAuthClientContext *client_ctx, const string_t *username, const int64_t priv) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_grant, username, priv); } int fcfs_auth_client_user_remove(FCFSAuthClientContext *client_ctx, const string_t *username) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_remove, username); } int fcfs_auth_client_spool_create(FCFSAuthClientContext *client_ctx, FCFSAuthStoragePoolInfo *spool, const int name_size, const bool dryrun) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_spool_create, spool, name_size, dryrun); } int fcfs_auth_client_spool_list(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *array) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_spool_list, username, poolname, limit, mpool, array); } int fcfs_auth_client_spool_remove(FCFSAuthClientContext *client_ctx, const string_t *poolname) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_spool_remove, poolname); } int fcfs_auth_client_spool_set_quota(FCFSAuthClientContext *client_ctx, const string_t *poolname, const int64_t quota) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_spool_set_quota, poolname, quota); } int fcfs_auth_client_spool_get_quota(FCFSAuthClientContext *client_ctx, const string_t *poolname, int64_t *quota) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_spool_get_quota, poolname, quota); } int fcfs_auth_client_gpool_grant(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *poolname, const FCFSAuthSPoolPriviledges *privs) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_gpool_grant, username, poolname, privs); } int fcfs_auth_client_gpool_withdraw(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *poolname) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_gpool_withdraw, username, poolname); } int fcfs_auth_client_gpool_list(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthGrantedPoolArray *array) { SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm, GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_gpool_list, username, poolname, limit, mpool, array); } ================================================ FILE: src/auth/client/fcfs_auth_client.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_CLIENT_H #define _FCFS_AUTH_CLIENT_H #include "auth_proto.h" #include "client_types.h" #include "client_func.h" #include "client_global.h" #include "client_proto.h" #include "session_regenerate.h" #define fcfs_auth_load_config(auth, config_filename) \ fcfs_auth_load_config_ex(auth, config_filename, NULL, \ sf_server_group_index_type_service) #ifdef __cplusplus extern "C" { #endif int fcfs_auth_load_config_ex(FCFSAuthClientFullContext *auth, const char *config_filename, const char *section_name, const SFServerGroupIndexType index_type); void fcfs_auth_config_to_string_ex(const FCFSAuthClientFullContext *auth, const char *caption, char *output, const int size); static inline void fcfs_auth_config_to_string(const FCFSAuthClientFullContext *auth, char *output, const int size) { const char *caption = "auth "; fcfs_auth_config_to_string_ex(auth, caption, output, size); } int fcfs_auth_client_user_login_ex(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *passwd, const string_t *poolname, const int flags); static inline int fcfs_auth_client_user_login( FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *passwd) { const string_t poolname = {NULL, 0}; const int flags = 0; return fcfs_auth_client_user_login_ex(client_ctx, username, passwd, &poolname, flags); } static inline int fcfs_auth_client_session_create_ex( FCFSAuthClientFullContext *auth, string_t *poolname, const bool publish) { int result; int flags; /* logInfo("file: "__FILE__", line: %d, " "auth_enabled: %d, username: %s, secret_key_filename: %s", __LINE__, auth->enabled, auth->ctx->auth_cfg.username.str, auth->ctx->auth_cfg.secret_key_filename.str); */ if (auth->enabled) { auth->ctx->session.poolname = poolname; if (publish) { if ((result=session_regenerate_init()) != 0) { return result; } flags = FCFS_AUTH_SESSION_FLAGS_PUBLISH; } else { flags = 0; } return fcfs_auth_client_user_login_ex(auth->ctx, &auth-> ctx->auth_cfg.username, &auth->ctx->auth_cfg.passwd, poolname, flags); } else { return 0; } } static inline int fcfs_auth_client_session_create( FCFSAuthClientFullContext *auth, const bool publish) { #define AUTH_EMPTY_POOL_NAME SF_G_EMPTY_STRING return fcfs_auth_client_session_create_ex(auth, &AUTH_EMPTY_POOL_NAME, publish); } int fcfs_auth_client_session_subscribe(FCFSAuthClientContext *client_ctx); int fcfs_auth_client_session_validate(FCFSAuthClientContext *client_ctx, const string_t *session_id, const string_t *validate_key, const FCFSAuthValidatePriviledgeType priv_type, const int64_t pool_id, const int64_t priv_required); int fcfs_auth_client_user_create(FCFSAuthClientContext *client_ctx, const FCFSAuthUserInfo *user); int fcfs_auth_client_user_passwd(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *passwd); int fcfs_auth_client_user_list(FCFSAuthClientContext *client_ctx, const string_t *username, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthUserArray *array); int fcfs_auth_client_user_grant(FCFSAuthClientContext *client_ctx, const string_t *username, const int64_t priv); int fcfs_auth_client_user_remove(FCFSAuthClientContext *client_ctx, const string_t *username); /* storage pool operations */ int fcfs_auth_client_spool_create(FCFSAuthClientContext *client_ctx, FCFSAuthStoragePoolInfo *spool, const int name_size, const bool dryrun); int fcfs_auth_client_spool_list(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *array); int fcfs_auth_client_spool_remove(FCFSAuthClientContext *client_ctx, const string_t *poolname); int fcfs_auth_client_spool_set_quota(FCFSAuthClientContext *client_ctx, const string_t *poolname, const int64_t quota); int fcfs_auth_client_spool_get_quota(FCFSAuthClientContext *client_ctx, const string_t *poolname, int64_t *quota); /* pool granted operations */ int fcfs_auth_client_gpool_grant(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *poolname, const FCFSAuthSPoolPriviledges *privs); int fcfs_auth_client_gpool_withdraw(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *poolname); int fcfs_auth_client_gpool_list(FCFSAuthClientContext *client_ctx, const string_t *username, const string_t *poolname, const SFListLimitInfo *limit, struct fast_mpool_man *mpool, FCFSAuthGrantedPoolArray *array); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/client/fcfs_auth_for_server.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "sf/idempotency/client/rpc_wrapper.h" #include "../common/auth_func.h" #include "fcfs_auth_for_server.h" int fcfs_auth_for_server_check_priv(FCFSAuthClientContext *client_ctx, SFRequestInfo *request, SFResponseInfo *response, const FCFSAuthValidatePriviledgeType priv_type, const int64_t the_priv) { int result; bool validate; ServerSessionIdInfo session; string_t session_id; if ((result=sf_server_check_min_body_length(response, request-> header.body_len, FCFS_AUTH_SESSION_ID_LEN)) != 0) { return result; } session.id = buff2long(request->body); if (session.fields.publish) { switch (priv_type) { case fcfs_auth_validate_priv_type_user: result = server_session_user_priv_granted( session.id, the_priv); break; case fcfs_auth_validate_priv_type_pool_fdir: result = server_session_fdir_priv_granted( session.id, the_priv); break; default: result = server_session_fstore_priv_granted( session.id, the_priv); break; } if (result == SF_SESSION_ERROR_NOT_EXIST) { validate = (g_current_time - session.fields.ts <= g_server_session_cfg.validate_within_fresh_seconds); } else { validate = false; } } else { validate = true; } if (validate) { const int64_t pool_id = 0; FC_SET_STRING_EX(session_id, request->body, FCFS_AUTH_SESSION_ID_LEN); result = fcfs_auth_client_session_validate(client_ctx, &session_id, &g_server_session_cfg.validate_key, priv_type, pool_id, the_priv); } /* logInfo("check priv cmd: %d, session: %"PRId64", validate: %d, " "error no: %d", request->header.cmd, session.id, validate, result); */ if (result != 0) { return result; } request->body += FCFS_AUTH_SESSION_ID_LEN; request->header.body_len -= FCFS_AUTH_SESSION_ID_LEN; return 0; } ================================================ FILE: src/auth/client/fcfs_auth_for_server.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_FOR_SERVER_H #define _FCFS_AUTH_FOR_SERVER_H #include "fcfs_auth_client.h" #include "server_session.h" #include "session_sync.h" #define fcfs_auth_for_server_init(auth, ini_ctx, cluster_filename) \ fcfs_auth_for_server_init_ex(auth, ini_ctx, cluster_filename, NULL) #ifdef __cplusplus extern "C" { #endif int fcfs_auth_for_server_check_priv(FCFSAuthClientContext *client_ctx, SFRequestInfo *request, SFResponseInfo *response, const FCFSAuthValidatePriviledgeType priv_type, const int64_t the_priv); static inline int fcfs_auth_for_server_init_ex(FCFSAuthClientFullContext *auth, IniFullContext *ini_ctx, const char *cluster_filename, const char *section_name) { int result; g_fcfs_auth_client_vars.ignore_when_passwd_not_exist = true; if ((result=fcfs_auth_load_config_ex(auth, cluster_filename, section_name, sf_server_group_index_type_cluster)) != 0) { return result; } if (auth->enabled) { if ((result=server_session_init(ini_ctx)) != 0) { return result; } if ((result=session_sync_init()) != 0) { return result; } } return result; } static inline void fcfs_auth_for_server_cfg_to_string(const FCFSAuthClientFullContext *auth, char *output, const int size) { const char *caption = ""; char sz_session_config[512]; char sz_auth_config[1024]; fcfs_auth_config_to_string_ex(auth, caption, sz_auth_config, sizeof(sz_auth_config)); if (auth->enabled) { server_session_cfg_to_string_ex(sz_session_config, sizeof(sz_session_config), true); snprintf(output, size, "auth {%s, %s}", sz_auth_config, sz_session_config); } else { snprintf(output, size, "auth {%s}", sz_auth_config); } } static inline int fcfs_auth_for_server_start(const FCFSAuthClientFullContext *auth) { if (auth->enabled) { return session_sync_start(); } else { return 0; } } static inline void fcfs_auth_for_server_destroy( FCFSAuthClientFullContext *auth) { fcfs_auth_client_destroy_ex(auth->ctx); } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/client/session_regenerate.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/pthread_func.h" #include "fastcommon/logger.h" #include "sf/sf_proto.h" #include "sf/sf_global.h" #include "auth_func.h" #include "client_global.h" #include "fcfs_auth_client.h" #include "session_regenerate.h" typedef struct synced_session_array { bool inited; pthread_mutex_t lock; } SessionRegenerateContext; static SessionRegenerateContext session_ctx; static int session_regenerate_do() { char old_session_id[FCFS_AUTH_SESSION_ID_LEN]; int result; memcpy(old_session_id, g_fcfs_auth_client_vars.client_ctx. session.id, FCFS_AUTH_SESSION_ID_LEN); PTHREAD_MUTEX_LOCK(&session_ctx.lock); if (memcmp(old_session_id, g_fcfs_auth_client_vars.client_ctx. session.id, FCFS_AUTH_SESSION_ID_LEN) == 0) { result = fcfs_auth_client_user_login_ex(&g_fcfs_auth_client_vars. client_ctx, &g_fcfs_auth_client_vars.client_ctx.auth_cfg. username, &g_fcfs_auth_client_vars.client_ctx.auth_cfg. passwd, g_fcfs_auth_client_vars.client_ctx.session.poolname, FCFS_AUTH_SESSION_FLAGS_PUBLISH); } else { result = 0; } PTHREAD_MUTEX_UNLOCK(&session_ctx.lock); return result; } static int fcfs_auth_error_handler(const int errnum) { if (errnum == SF_SESSION_ERROR_NOT_EXIST) { if (session_regenerate_do() == EPERM) { return EPERM; } else { return SF_RETRIABLE_ERROR_NO_SERVER; } } else { return errnum; } } int session_regenerate_init() { int result; if (session_ctx.inited) { return 0; } if ((result=init_pthread_lock(&session_ctx.lock)) != 0) { return result; } sf_set_error_handler(fcfs_auth_error_handler); session_ctx.inited = true; return 0; } ================================================ FILE: src/auth/client/session_regenerate.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_SESSION_REGENERATE_H #define _FCFS_AUTH_SESSION_REGENERATE_H #ifdef __cplusplus extern "C" { #endif int session_regenerate_init(); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/client/session_sync.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "sf/sf_proto.h" #include "sf/sf_global.h" #include "auth_proto.h" #include "auth_func.h" #include "server_session.h" #include "client_global.h" #include "client_proto.h" #include "session_sync.h" typedef struct synced_session_entry { char operation; uint64_t session_id; SessionSyncedFields fields; } SyncedSessionEntry; typedef struct synced_session_array { int alloc; int count; SyncedSessionEntry *entries; SFProtoRecvBuffer buffer; } SyncedSessionArray; static SyncedSessionArray session_array; static int synced_session_array_init(SyncedSessionArray *array) { int result; if ((result=sf_init_recv_buffer(&array->buffer, 0)) != 0) { return result; } array->alloc = array->count = 0; array->entries = NULL; return 0; } static int check_realloc_session_array(SyncedSessionArray *array, const int target_count) { SyncedSessionEntry *new_entries; int new_alloc; int bytes; if (target_count <= array->alloc) { return 0; } if (array->alloc == 0) { new_alloc = 4 * 1024; } else { new_alloc = 2 * array->alloc; } while (new_alloc < target_count) { new_alloc *= 2; } bytes = sizeof(SyncedSessionEntry) * new_alloc; new_entries = (SyncedSessionEntry *)fc_malloc(bytes); if (new_entries == NULL) { logError("file: "__FILE__", line: %d, " "malloc %d bytes fail", __LINE__, bytes); return ENOMEM; } if (array->entries != NULL) { free(array->entries); } array->entries = new_entries; array->alloc = new_alloc; return 0; } static int parse_session_push(SFResponseInfo *response) { FCFSAuthProtoSessionPushRespBodyHeader *body_header; FCFSAuthProtoSessionPushRespBodyPart *body_part; SyncedSessionEntry *entry; SyncedSessionEntry *end; char *p; int count; int result; body_header = (FCFSAuthProtoSessionPushRespBodyHeader *) session_array.buffer.buff; p = (char *)(body_header + 1); count = buff2int(body_header->count); if ((result=check_realloc_session_array( &session_array, count)) != 0) { return result; } end = session_array.entries + count; for (entry=session_array.entries; entry response->header.body_len) { response->error.length = sprintf( response->error.message, "body length: %d is too small", response->header.body_len); return EINVAL; } body_part = (FCFSAuthProtoSessionPushRespBodyPart *)p; entry->operation = body_part->operation; entry->session_id = buff2long(body_part->session_id); if (entry->operation == FCFS_AUTH_SESSION_OP_TYPE_CREATE) { entry->fields.user.id = buff2long(body_part->entry->user.id); entry->fields.user.priv = buff2long(body_part->entry->user.priv); entry->fields.pool.id = buff2long(body_part->entry->pool.id); entry->fields.pool.available = body_part->entry->pool.available; entry->fields.pool.privs.fdir = buff2int( body_part->entry->pool.privs.fdir); entry->fields.pool.privs.fstore = buff2int( body_part->entry->pool.privs.fstore); p += sizeof(FCFSAuthProtoSessionPushRespBodyPart) + sizeof(FCFSAuthProtoSessionPushEntry); } else { p += sizeof(FCFSAuthProtoSessionPushRespBodyPart); } } if (response->header.body_len != (p - session_array.buffer.buff)) { response->error.length = sprintf(response->error.message, "body length: %d != expect: %d", response->header.body_len, (int)(p - session_array.buffer.buff)); return EINVAL; } session_array.count = count; return 0; } static void deal_session_array() { const bool publish = true; ServerSessionEntry session; SyncedSessionEntry *entry; SyncedSessionEntry *end; end = session_array.entries + session_array.count; for (entry=session_array.entries; entrysession_id, entry->operation); if (entry->operation == FCFS_AUTH_SESSION_OP_TYPE_CREATE) { len += sprintf(buff + len, ", user {id: %"PRId64 ", priv: %"PRId64"}", entry->fields.user.id, entry->fields.user.priv); if (entry->fields.pool.id != 0) { len += sprintf(buff + len, ", pool {id: %"PRId64 ", avail: %d, privs: {fdir: %d, fstore: %d}}", entry->fields.pool.id, entry->fields.pool.available, entry->fields.pool.privs.fdir, entry->fields.pool.privs.fstore); } } logInfo("%s", buff); } if (entry->operation == FCFS_AUTH_SESSION_OP_TYPE_CREATE) { session.id_info.id = entry->session_id; session.fields = &entry->fields; server_session_add(&session, publish); } else { server_session_delete(entry->session_id); } } } static int session_sync(ConnectionInfo *conn) { const int network_timeout = 2; int result; SFResponseInfo response; response.error.length = 0; while (SF_G_CONTINUE_FLAG) { result = sf_recv_vary_response(conn, &response, network_timeout, FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_REQ, &session_array. buffer, sizeof(FCFSAuthProtoSessionPushRespBodyHeader)); if (result == 0) { if ((result=parse_session_push(&response)) != 0) { if (response.error.length > 0) { auth_log_network_error(&response, conn, result); } break; } deal_session_array(); } else if (result == ETIMEDOUT) { if ((result=sf_active_test(conn, &response, g_fcfs_auth_client_vars.client_ctx. common_cfg.network_timeout)) != 0) { break; } } else { auth_log_network_error(&response, conn, result); break; } } if (SF_G_CONTINUE_FLAG) { server_session_clear(); } return result; } static void *session_sync_thread_func(void *arg) { const bool shared = false; SFConnectionManager *cm; ConnectionInfo *conn; int result; #ifdef OS_LINUX prctl(PR_SET_NAME, "session-sync"); #endif cm = &g_fcfs_auth_client_vars.client_ctx.cm; while (SF_G_CONTINUE_FLAG) { if ((conn=cm->ops.get_master_connection(cm, 0, shared, &result)) == NULL) { sleep(1); continue; } if ((result=fcfs_auth_client_proto_session_subscribe( &g_fcfs_auth_client_vars.client_ctx, conn)) == 0) { session_sync(conn); } else if (result == ENOENT) { sleep(5); } else if (result == EPERM) { if ((result=fcfs_auth_load_passwd_ex(g_fcfs_auth_client_vars. client_ctx.auth_cfg.secret_key_filename.str, g_fcfs_auth_client_vars.client_ctx.auth_cfg. passwd_buff, g_fcfs_auth_client_vars. ignore_when_passwd_not_exist)) != 0) { logError("file: "__FILE__", line: %d, " "secret_key_filename: %s, " "load password fail, ret code: %d", __LINE__, g_fcfs_auth_client_vars.client_ctx.auth_cfg. secret_key_filename.str, result); sleep(30); } sleep(10); } cm->ops.close_connection(cm, conn); sleep(1); } return NULL; } int session_sync_init() { int result; if ((result=synced_session_array_init(&session_array)) != 0) { return result; } return 0; } int session_sync_start() { pthread_t tid; return fc_create_thread(&tid, session_sync_thread_func, NULL, SF_G_THREAD_STACK_SIZE); } ================================================ FILE: src/auth/client/session_sync.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_SESSION_SYNC_H #define _FCFS_AUTH_SESSION_SYNC_H #ifdef __cplusplus extern "C" { #endif int session_sync_init(); int session_sync_start(); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/client/simple_connection_manager.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fastcommon/connection_pool.h" #include "client_global.h" #include "client_func.h" #include "client_proto.h" #include "simple_connection_manager.h" typedef struct fcfs_auth_cm_simple_extra { /* master connection cache */ struct { volatile bool valid; ConnectionInfo conn; } master_cache; ConnectionPool cpool; FCFSAuthClientContext *client_ctx; FCFSAuthServerGroup *cluster_sarray; } FCFSAuthCMSimpleExtra; static int check_realloc_group_servers(FCFSAuthServerGroup *server_group) { int bytes; int alloc_size; ConnectionInfo *servers; if (server_group->alloc_size > server_group->count) { return 0; } if (server_group->alloc_size > 0) { alloc_size = server_group->alloc_size * 2; } else { alloc_size = 4; } bytes = sizeof(ConnectionInfo) * alloc_size; servers = (ConnectionInfo *)fc_malloc(bytes); if (servers == NULL) { return errno != 0 ? errno : ENOMEM; } memset(servers, 0, bytes); if (server_group->count > 0) { memcpy(servers, server_group->servers, sizeof(ConnectionInfo) * server_group->count); } server_group->servers = servers; server_group->alloc_size = alloc_size; return 0; } static ConnectionInfo *get_spec_connection(SFConnectionManager *cm, const ConnectionInfo *target, const bool shared, int *err_no) { FCFSAuthCMSimpleExtra *extra; FCFSAuthServerGroup *cluster_sarray; ConnectionInfo *conn; ConnectionInfo *end; extra = (FCFSAuthCMSimpleExtra *)cm->extra; cluster_sarray = extra->cluster_sarray; end = cluster_sarray->servers + cluster_sarray->count; for (conn=cluster_sarray->servers; connservers + cluster_sarray->count++; conn_pool_set_server_info(conn, target->ip_addr, target->port); } return conn_pool_get_connection_ex(&extra->cpool, target, "fauth", shared, err_no); } static ConnectionInfo *get_connection(SFConnectionManager *cm, const int group_index, const bool shared, int *err_no) { int index; int i; FCFSAuthServerGroup *cluster_sarray; ConnectionInfo *conn; ConnectionInfo *server; cluster_sarray = ((FCFSAuthCMSimpleExtra *)cm->extra)->cluster_sarray; index = rand() % cluster_sarray->count; server = cluster_sarray->servers + index; if ((conn=get_spec_connection(cm, server, shared, err_no)) != NULL) { return conn; } i = (index + 1) % cluster_sarray->count; while (i != index) { server = cluster_sarray->servers + i; if ((conn=get_spec_connection(cm, server, shared, err_no)) != NULL) { return conn; } i = (i + 1) % cluster_sarray->count; } logError("file: "__FILE__", line: %d, " "get_connection fail, configured server count: %d", __LINE__, cluster_sarray->count); return NULL; } static ConnectionInfo *get_master_connection(SFConnectionManager *cm, const int group_index, const bool shared, int *err_no) { FCFSAuthCMSimpleExtra *extra; ConnectionInfo *conn; FCFSAuthClientServerEntry master; SFNetRetryIntervalContext net_retry_ctx; int i; extra = (FCFSAuthCMSimpleExtra *)cm->extra; if (extra->master_cache.valid) { if ((conn=conn_pool_get_connection_ex(&extra->cpool, &extra-> master_cache.conn, "fauth", shared, err_no)) != NULL) { return conn; } else { extra->master_cache.valid = false; } } sf_init_net_retry_interval_context(&net_retry_ctx, &cm->common_cfg->net_retry_cfg.interval_mm, &cm->common_cfg->net_retry_cfg.connect); i = 0; while (1) { if ((*err_no=fcfs_auth_client_get_master(extra-> client_ctx, &master)) != 0) { SF_NET_RETRY_CHECK_AND_SLEEP(net_retry_ctx, cm->common_cfg->net_retry_cfg. connect.times, ++i, *err_no); continue; } if ((conn=get_spec_connection(cm, &master.conn, shared, err_no)) == NULL) { break; } extra->master_cache.valid = true; extra->master_cache.conn = *conn; return conn; } logError("file: "__FILE__", line: %d, " "get_master_connection fail, errno: %d", __LINE__, *err_no); return NULL; } static void release_connection(SFConnectionManager *cm, ConnectionInfo *conn) { FCFSAuthCMSimpleExtra *extra; extra = (FCFSAuthCMSimpleExtra *)cm->extra; conn_pool_close_connection_ex(&extra->cpool, conn, false); } static void close_connection(SFConnectionManager *cm, ConnectionInfo *conn) { FCFSAuthCMSimpleExtra *extra; extra = (FCFSAuthCMSimpleExtra *)cm->extra; if (extra->master_cache.valid && FC_CONNECTION_SERVER_EQUAL1( extra->master_cache.conn, *conn)) { extra->master_cache.valid = false; } conn_pool_close_connection_ex(&extra->cpool, conn, true); } static void copy_to_server_group_array(FCFSAuthClientContext *client_ctx, FCFSAuthServerGroup *server_group, const int server_group_index) { FCServerInfo *server; FCServerInfo *end; ConnectionInfo *conn; int server_count; server_count = FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg); conn = server_group->servers; end = FC_SID_SERVERS(client_ctx->cluster.server_cfg) + server_count; for (server=FC_SID_SERVERS(client_ctx->cluster.server_cfg); servergroup_addrs[server_group_index]. address_array.addrs[0]->conn; } server_group->count = server_count; /* { char formatted_ip[FORMATTED_IP_SIZE]; printf("auth_server count: %d\n", server_group->count); for (conn=server_group->servers; connservers+ server_group->count; conn++) { format_ip_address(conn->ip_addr, formatted_ip); printf("auth_server=%s:%u\n", formatted_ip, conn->port); } } */ } int fcfs_auth_simple_connection_manager_init(FCFSAuthClientContext *client_ctx, SFConnectionManager *cm, const int server_group_index) { const int max_count_per_entry = 0; const int max_idle_time = 3600; FCFSAuthCMSimpleExtra *extra; FCFSAuthServerGroup *cluster_sarray; int server_count; int htable_init_capacity; int result; cluster_sarray = (FCFSAuthServerGroup *)fc_malloc( sizeof(FCFSAuthServerGroup)); if (cluster_sarray == NULL) { return ENOMEM; } server_count = FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg); if ((result=fcfs_auth_alloc_group_servers(cluster_sarray, server_count)) != 0) { return result; } copy_to_server_group_array(client_ctx, cluster_sarray, server_group_index); extra = (FCFSAuthCMSimpleExtra *)fc_malloc(sizeof(FCFSAuthCMSimpleExtra)); if (extra == NULL) { return ENOMEM; } memset(extra, 0, sizeof(FCFSAuthCMSimpleExtra)); htable_init_capacity = 4 * server_count; if (htable_init_capacity < 256) { htable_init_capacity = 256; } if ((result=conn_pool_init_ex1(&extra->cpool, client_ctx->common_cfg. connect_timeout, max_count_per_entry, max_idle_time, htable_init_capacity, NULL, client_ctx, sf_cm_validate_connection_callback, cm, sizeof(SFConnectionParameters), NULL)) != 0) { return result; } extra->cluster_sarray = cluster_sarray; extra->client_ctx = client_ctx; cm->server_group_index = server_group_index; cm->extra = extra; cm->common_cfg = &client_ctx->common_cfg; cm->ops.get_connection = get_connection; cm->ops.get_spec_connection = get_spec_connection; cm->ops.get_master_connection = get_master_connection; cm->ops.get_readable_connection = get_master_connection; cm->ops.release_connection = release_connection; cm->ops.close_connection = close_connection; cm->ops.get_connection_params = sf_cm_get_connection_params; return 0; } void fcfs_auth_simple_connection_manager_destroy(SFConnectionManager *cm) { FCFSAuthCMSimpleExtra *extra; extra = (FCFSAuthCMSimpleExtra *)cm->extra; if (extra->cluster_sarray != NULL) { if (extra->cluster_sarray->servers != NULL) { free(extra->cluster_sarray->servers); extra->cluster_sarray->servers = NULL; extra->cluster_sarray->count = 0; extra->cluster_sarray->alloc_size = 0; } free(extra->cluster_sarray); extra->cluster_sarray = NULL; } } ================================================ FILE: src/auth/client/simple_connection_manager.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_SIMPLE_CONNECTION_MANAGER_H #define _FCFS_AUTH_SIMPLE_CONNECTION_MANAGER_H #include "client_types.h" #ifdef __cplusplus extern "C" { #endif int fcfs_auth_simple_connection_manager_init(FCFSAuthClientContext *client_ctx, SFConnectionManager *cm, const int server_group_index); void fcfs_auth_simple_connection_manager_destroy(SFConnectionManager *cm); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/client/tools/Makefile.in ================================================ .SUFFIXES: .c .o .lo COMPILE = $(CC) $(CFLAGS) INC_PATH = -I../../common -I/usr/local/include LIB_PATH = -L.. $(LIBS) -lfcfsauthclient -lfastcommon -lserverframe TARGET_PATH = $(TARGET_PREFIX)/bin STATIC_OBJS = tool_func.o ALL_PRGS = fcfs_user fcfs_pool fauth_list_servers fauth_cluster_stat all: $(STATIC_OBJS) $(ALL_PRGS) .o: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c.o: $(COMPILE) -c -o $@ $< $(INC_PATH) install: mkdir -p $(TARGET_PATH) cp -f $(ALL_PRGS) $(TARGET_PATH) clean: rm -f $(STATIC_OBJS) $(ALL_PRGS) ================================================ FILE: src/auth/client/tools/fauth_cluster_stat.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "../fcfs_auth_client.h" static void usage(char *argv[]) { fprintf(stderr, "Usage: %s [-c config_filename=%s]\n", argv[0], FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME); } static void output(FCFSAuthClientClusterStatEntry *stats, const int count) { FCFSAuthClientClusterStatEntry *stat; FCFSAuthClientClusterStatEntry *end; char formatted_ip[FORMATTED_IP_SIZE]; end = stats + count; for (stat=stats; statip_addr, formatted_ip); printf( "server_id: %d, host: %s:%u, " "is_online: %d, is_master: %d\n", stat->server_id, formatted_ip, stat->port, stat->is_online, stat->is_master); } printf("\nserver count: %d\n\n", count); } int main(int argc, char *argv[]) { #define CLUSTER_MAX_SERVER_COUNT 16 int ch; const char *config_filename = FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME; int count; FCFSAuthClientClusterStatEntry stats[CLUSTER_MAX_SERVER_COUNT]; int result; while ((ch=getopt(argc, argv, "hc:")) != -1) { switch (ch) { case 'h': usage(argv); break; case 'c': config_filename = optarg; break; default: usage(argv); return 1; } } log_init(); //g_log_context.log_level = LOG_DEBUG; if ((result=fcfs_auth_client_init(config_filename)) != 0) { return result; } if ((result=fcfs_auth_client_cluster_stat(&g_fcfs_auth_client_vars.client_ctx, stats, CLUSTER_MAX_SERVER_COUNT, &count)) != 0) { fprintf(stderr, "fcfs_auth_client_cluster_stat fail, " "errno: %d, error info: %s\n", result, STRERROR(result)); return result; } output(stats, count); return 0; } ================================================ FILE: src/auth/client/tools/fauth_list_servers.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "sf/sf_cluster_cfg.h" #include "../client_types.h" static void usage(char *argv[]) { fprintf(stderr, "\nUsage: %s \n", argv[0]); } int main(int argc, char *argv[]) { const bool share_between_groups = false; const bool calc_sign = false; const char *config_filename; SFClusterConfig cluster; FCServerInfo *server; FCServerInfo *end; FCAddressInfo **addrs; int i; int result; if (argc < 2) { usage(argv); return EINVAL; } config_filename = argv[1]; log_init(); if ((result=sf_load_cluster_config_by_file(&cluster, config_filename, FCFS_AUTH_DEFAULT_CLUSTER_PORT, share_between_groups, calc_sign)) != 0) { return result; } printf("fauth_group=("); end = FC_SID_SERVERS(cluster.server_cfg) + FC_SID_SERVER_COUNT(cluster.server_cfg); for (server=FC_SID_SERVERS(cluster.server_cfg), i=0; servergroup_addrs[cluster.cluster_group_index]. address_array.addrs; if (i > 0) { printf(" "); } printf("%s", (*addrs)->conn.ip_addr); } printf(")\n"); return 0; } ================================================ FILE: src/auth/client/tools/fcfs_pool.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "tool_func.h" #include "../../common/auth_func.h" #include "../fcfs_auth_client.h" #define PARAM_OPTION_INCLUDED 1 #define PARAM_OPTION_REQUIRED 2 #define DRY_RUN_OPTION_STR "--dryrun" const int mpool_alloc_size_once = 16 * 1024; const int mpool_discard_size = 8; const SFListLimitInfo limit = {0, 0}; static int current_index; static FCFSAuthStoragePoolInfo spool; static struct { string_t fdir; string_t fstore; } privs; static string_t username = {0}; static bool dryrun; static void usage(char *argv[]) { fprintf(stderr, "\nUsage: %s [-c config_filename=%s]\n" "\t[-u admin_username=admin]\n" "\t[-k admin_secret_key_filename=/etc/fastcfs/auth/keys/" "${admin_username}.key]\n" "\t[-d fastdir_access=rw]\n" "\t[-s faststore_access=rw]\n" "\t [username] [pool_name] [quota]\n\n" "\tthe operations and following parameters: \n" "\t create [pool_name] [%s]\n" "\t quota \n" "\t delete | remove \n" "\t plist | pool-list [username] [pool_name]\n" "\t grant \n" "\t cancel | withdraw \n" "\t glist | grant-list | granted-list [username] [pool_name]\n\n" "\t* the pool name can contain %s for auto generated id when " "create pool, such as 'pool-%s'\n" "\t the pool name template configurated in the server side " "will be used when not specify the pool name\n" "\t you can set the initial value of auto increment id and " "the pool name template in server.conf of the server side\n\n" "\t* the quota parameter is required for create and quota operations\n" "\t the default unit of quota is GiB, %s for no limit\n\n" "\tFastDIR and FastStore accesses are:\n" "\t %c: read only\n" "\t %c: write only\n" "\t %c%c: read and write\n\n", argv[0], FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME, DRY_RUN_OPTION_STR, FCFS_AUTH_AUTO_ID_TAG_STR, FCFS_AUTH_AUTO_ID_TAG_STR, FCFS_AUTH_UNLIMITED_QUOTA_STR, POOL_ACCESS_NAME_READ_CHR, POOL_ACCESS_NAME_WRITE_CHR, POOL_ACCESS_NAME_READ_CHR, POOL_ACCESS_NAME_WRITE_CHR); } static int create_spool(int argc, char *argv[]) { int result; FILE *fp; char name_buff[NAME_MAX]; char prompt[32]; if (spool.name.len > sizeof(name_buff)) { fprintf(stderr, "pool name length: %d is too large, exceeds %d", spool.name.len, (int)sizeof(name_buff)); return ENAMETOOLONG; } memcpy(name_buff, spool.name.str, spool.name.len); spool.name.str = name_buff; if ((result=fcfs_auth_client_spool_create(&g_fcfs_auth_client_vars. client_ctx, &spool, sizeof(name_buff), dryrun)) == 0) { strcpy(prompt, "success"); fp = stdout; } else { strcpy(prompt, "fail"); fp = stderr; } fprintf(fp, "%screate pool %.*s %s\n", (dryrun ? "[DRYRUN] " : ""), spool.name.len, spool.name.str, prompt); return result; } static int quota_spool(int argc, char *argv[]) { int result; char buff[64]; if ((result=fcfs_auth_client_spool_set_quota(&g_fcfs_auth_client_vars. client_ctx, &spool.name, spool.quota)) == 0) { if (spool.quota == FCFS_AUTH_UNLIMITED_QUOTA_VAL) { strcpy(buff, FCFS_AUTH_UNLIMITED_QUOTA_STR); } else { long_to_comma_str(spool.quota / (1024 * 1024 * 1024), buff); strcat(buff, "GiB"); } printf("pool %s, set quota to %s success\n", spool.name.str, buff); } else { fprintf(stderr, "pool %s, set quota fail\n", spool.name.str); } return result; } static inline int parse_pool_access(const string_t *privs, const char *caption, int *pv) { if (privs->str == NULL) { *pv = FCFS_AUTH_POOL_ACCESS_ALL; return 0; } else { return fcfs_auth_parse_pool_access(privs, pv); } } static int grant_privilege(int argc, char *argv[]) { int result; FILE *fp; char prompt[32]; FCFSAuthSPoolPriviledges pvs; if ((result=parse_pool_access(&privs.fdir, "FastDIR", &pvs.fdir)) != 0) { usage(argv); return result; } if ((result=parse_pool_access(&privs.fstore, "FastStore", &pvs.fstore)) != 0) { usage(argv); return result; } if ((pvs.fdir == FCFS_AUTH_POOL_ACCESS_NONE) && (pvs.fstore == FCFS_AUTH_POOL_ACCESS_NONE)) { fprintf(stderr, "no access priviledges to grant\n"); usage(argv); return ENOENT; } if ((result=fcfs_auth_client_gpool_grant(&g_fcfs_auth_client_vars. client_ctx, &username, &spool.name, &pvs)) == 0) { strcpy(prompt, "success"); fp = stdout; } else { strcpy(prompt, "fail"); fp = stderr; } fprintf(fp, "grant access priviledge of pool %.*s to user %.*s %s\n", spool.name.len, spool.name.str, username.len, username.str, prompt); return result; } static int withdraw_privilege(int argc, char *argv[]) { int result; FILE *fp; char prompt[32]; if ((result=fcfs_auth_client_gpool_withdraw(&g_fcfs_auth_client_vars. client_ctx, &username, &spool.name)) == 0) { strcpy(prompt, "success"); fp = stdout; } else { strcpy(prompt, "fail"); fp = stderr; } fprintf(fp, "withdraw access priviledge of pool %.*s from user %.*s %s\n", spool.name.len, spool.name.str, username.len, username.str, prompt); return result; } static int remove_spool(int argc, char *argv[]) { int result; if ((result=fcfs_auth_client_spool_remove(&g_fcfs_auth_client_vars. client_ctx, &spool.name)) == 0) { printf("remove pool %s success\n", spool.name.str); } else { fprintf(stderr, "remove pool %s fail\n", spool.name.str); } return result; } static void output_spools(FCFSAuthStoragePoolArray *array) { FCFSAuthStoragePoolInfo *spool; FCFSAuthStoragePoolInfo *end; char buff[64]; printf("%5s %50s %16s\n", "No.", "pool_name", "quota (GiB)"); end = array->spools + array->count; for (spool=array->spools; spoolquota == FCFS_AUTH_UNLIMITED_QUOTA_VAL) { strcpy(buff, FCFS_AUTH_UNLIMITED_QUOTA_STR); } else { long_to_comma_str(spool->quota / (1024 * 1024 * 1024), buff); } printf("%4d. %50.*s %16s\n", (int)((spool - array->spools) + 1), spool->name.len, spool->name.str, buff); } } static void output_gpools(FCFSAuthGrantedPoolArray *array) { FCFSAuthGrantedPoolFullInfo *gpool; FCFSAuthGrantedPoolFullInfo *end; char buff1[64]; char buff2[64]; string_t priv_names_fdir; string_t priv_names_fstore; printf("%5s %32s %50s %16s %16s\n", "No.", "owner_name", "pool_name", "fdir_priv", "fstore_priv"); priv_names_fdir.str = buff1; priv_names_fstore.str = buff2; end = array->gpools + array->count; for (gpool=array->gpools; gpoolgpools) + 1), gpool->username.len, gpool->username.str, gpool->pool_name.len, gpool->pool_name.str, fcfs_auth_pool_access_to_string(gpool-> granted.privs.fdir, &priv_names_fdir), fcfs_auth_pool_access_to_string(gpool-> granted.privs.fstore, &priv_names_fstore)); } } static int list_spool(int argc, char *argv[]) { struct fast_mpool_man mpool; FCFSAuthStoragePoolArray spool_array; int result; if ((result=fast_mpool_init(&mpool, mpool_alloc_size_once, mpool_discard_size)) != 0) { return result; } fcfs_auth_spool_init_array(&spool_array); if ((result=fcfs_auth_client_spool_list(&g_fcfs_auth_client_vars. client_ctx, &username, &spool.name, &limit, &mpool, &spool_array)) == 0) { output_spools(&spool_array); } else { fprintf(stderr, "list storage pool fail\n"); } fcfs_auth_spool_free_array(&spool_array); fast_mpool_destroy(&mpool); return result; } static int list_gpool(int argc, char *argv[]) { struct fast_mpool_man mpool; FCFSAuthGrantedPoolArray gpool_array; int result; if ((result=fast_mpool_init(&mpool, mpool_alloc_size_once, mpool_discard_size)) != 0) { return result; } fcfs_auth_granted_init_array(&gpool_array); if ((result=fcfs_auth_client_gpool_list(&g_fcfs_auth_client_vars. client_ctx, &username, &spool.name, &limit, &mpool, &gpool_array)) == 0) { output_gpools(&gpool_array); } else { fprintf(stderr, "list storage pool fail\n"); } fcfs_auth_granted_free_array(&gpool_array); fast_mpool_destroy(&mpool); return result; } int main(int argc, char *argv[]) { int ch; int op_type; const char *config_filename = FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME; char *operation; FCFSAuthClientUserKeyPair admin; unsigned char passwd_buff[FCFS_AUTH_PASSWD_LEN + 1]; FilenameString admin_key_filename; string_t passwd; int username_options; int poolname_options; bool need_quota; int result; if (argc < 2) { usage(argv); return 1; } FC_SET_STRING(admin.username, "admin"); FC_SET_STRING(admin.key_filename, "/etc/fastcfs/auth/keys/${username}.key"); FC_SET_STRING_NULL(privs.fdir); FC_SET_STRING_NULL(privs.fstore); while ((ch=getopt(argc, argv, "hc:u:k:d:s:")) != -1) { switch (ch) { case 'h': usage(argv); return 0; case 'c': config_filename = optarg; break; case 'u': FC_SET_STRING_EX(admin.username, optarg, strlen(optarg)); break; case 'k': FC_SET_STRING_EX(admin.key_filename, optarg, strlen(optarg)); break; case 'd': FC_SET_STRING(privs.fdir, optarg); break; case 's': FC_SET_STRING(privs.fstore, optarg); break; default: usage(argv); return 1; } } current_index = optind; if (current_index >= argc) { fprintf(stderr, "expect operation\n"); usage(argv); return 1; } srand(time(NULL)); log_init(); //g_log_context.log_level = LOG_DEBUG; operation = argv[current_index++]; if (current_index < argc) { char *last; last = argv[argc - 1]; dryrun = (strcasecmp(last, DRY_RUN_OPTION_STR) == 0); if (dryrun) { --argc; //remove the last parameter } } if (strcasecmp(operation, "create") == 0) { op_type = FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ; username_options = 0; if (current_index + 1 < argc) { poolname_options = PARAM_OPTION_INCLUDED | PARAM_OPTION_REQUIRED; } else { poolname_options = 0; } need_quota = true; } else if (strcasecmp(operation, "grant") == 0) { op_type = FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ; username_options = PARAM_OPTION_INCLUDED | PARAM_OPTION_REQUIRED; poolname_options = PARAM_OPTION_INCLUDED | PARAM_OPTION_REQUIRED; need_quota = false; } else if (strcasecmp(operation, "quota") == 0) { op_type = FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ; username_options = 0; poolname_options = PARAM_OPTION_INCLUDED | PARAM_OPTION_REQUIRED; need_quota = true; } else if (strcasecmp(operation, "delete") == 0 || strcasecmp(operation, "remove") == 0) { op_type = FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ; username_options = 0; poolname_options = PARAM_OPTION_INCLUDED | PARAM_OPTION_REQUIRED; need_quota = false; } else if (strcasecmp(operation, "plist") == 0 || strcasecmp(operation, "pool-list") == 0 || strcasecmp(operation, "pool_list") == 0) { op_type = FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ; username_options = PARAM_OPTION_INCLUDED; poolname_options = PARAM_OPTION_INCLUDED; need_quota = false; } else if (strcasecmp(operation, "withdraw") == 0 || strcasecmp(operation, "cancel") == 0) { op_type = FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ; username_options = PARAM_OPTION_INCLUDED | PARAM_OPTION_REQUIRED; poolname_options = PARAM_OPTION_INCLUDED | PARAM_OPTION_REQUIRED; need_quota = false; } else if (strcasecmp(operation, "glist") == 0 || strcasecmp(operation, "grant-list") == 0 || strcasecmp(operation, "grant_list") == 0 || strcasecmp(operation, "granted-list") == 0 || strcasecmp(operation, "granted_list") == 0) { op_type = FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ; username_options = PARAM_OPTION_INCLUDED; poolname_options = PARAM_OPTION_INCLUDED; need_quota = false; } else if (strcasecmp(operation, "config-setid") == 0 || strcasecmp(operation, "config_setid") == 0 || strcasecmp(operation, "cfg-setid") == 0 || strcasecmp(operation, "cfg_setid") == 0) { op_type = FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ; username_options = PARAM_OPTION_INCLUDED; poolname_options = PARAM_OPTION_INCLUDED; need_quota = false; } else { fprintf(stderr, "unknow operation: %s\n", operation); usage(argv); return 1; } if ((username_options & PARAM_OPTION_INCLUDED) != 0) { if (current_index < argc) { FC_SET_STRING(username, argv[current_index]); current_index++; } else if ((username_options & PARAM_OPTION_REQUIRED) != 0) { fprintf(stderr, "expect username\n"); usage(argv); return 1; } } if ((poolname_options & PARAM_OPTION_INCLUDED) != 0) { if (current_index < argc) { FC_SET_STRING(spool.name, argv[current_index]); current_index++; } else if ((poolname_options & PARAM_OPTION_REQUIRED) != 0) { fprintf(stderr, "expect pool name\n"); usage(argv); return 1; } else { FC_SET_STRING_NULL(spool.name); } } if (current_index < argc) { if (strcasecmp(argv[current_index], FCFS_AUTH_UNLIMITED_QUOTA_STR) == 0) { spool.quota = FCFS_AUTH_UNLIMITED_QUOTA_VAL; } else if ((result=parse_bytes(argv[current_index], 1024 * 1024 * 1024, &spool.quota)) != 0) { fprintf(stderr, "parse quota field fail!\n"); if (op_type == FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ) { fprintf(stderr, "\nUsage: %s create [pool_name] " "[%s]\nFor example: %s create %s %s %s\n\n", argv[0], DRY_RUN_OPTION_STR, argv[0], (spool.name.str != NULL ? spool.name.str : argv[current_index]), FCFS_AUTH_UNLIMITED_QUOTA_STR, (dryrun ? DRY_RUN_OPTION_STR : "")); } return 1; } current_index++; } else if (need_quota) { fprintf(stderr, "expect quota\n"); usage(argv); return 1; } passwd.str = (char *)passwd_buff; passwd.len = FCFS_AUTH_PASSWD_LEN; fcfs_auth_replace_filename_with_username(&admin.key_filename, &admin.username, &admin_key_filename); if ((result=fcfs_auth_load_passwd( FC_FILENAME_STRING_PTR(admin_key_filename), passwd_buff)) != 0) { return result; } if ((result=fcfs_auth_client_init(config_filename)) != 0) { return result; } if ((result=fcfs_auth_client_user_login(&g_fcfs_auth_client_vars. client_ctx, &admin.username, &passwd)) != 0) { return result; } switch (op_type) { case FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ: return create_spool(argc, argv); case FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ: return list_spool(argc, argv); case FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ: return remove_spool(argc, argv); case FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ: return quota_spool(argc, argv); case FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ: return grant_privilege(argc, argv); case FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ: return withdraw_privilege(argc, argv); case FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ: return list_gpool(argc, argv); } return 0; } ================================================ FILE: src/auth/client/tools/fcfs_user.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "tool_func.h" #include "../../common/auth_func.h" #include "../fcfs_auth_client.h" const int mpool_alloc_size_once = 16 * 1024; const int mpool_discard_size = 8; const SFListLimitInfo limit = {0, 0}; static int current_index; static FCFSAuthUserInfo user; static bool priv_set = false; static bool confirmed = false; //for option -y static void usage(char *argv[]) { fprintf(stderr, "\nUsage: %s [-c config_filename=%s]\n" "\t[-u admin_username=admin]\n" "\t[-k admin_secret_key_filename=/etc/fastcfs/auth/keys/" "${username}.key]\n" "\t[-p priviledges=%s]\n" "\t [username]\n" "\t[user_secret_key_filename=keys/${username}.key]\n\n" "\tthe operations and parameters are: \n" "\t create [user_secret_key_filename]\n" "\t passwd | secret-key [user_secret_key_filename] " "[-y]: regenerate user's secret key\n" "\t grant , the option <-p priviledges> is required\n" "\t delete | remove \n" "\t list [username]\n\n" "\t[user_secret_key_filename]: specify the filename to store the " "generated secret key of the user\n" "\t[priviledges]: the granted priviledges seperate by comma, " "priviledges:\n" "\t %s: user management\n" "\t %s: create storage pool\n" "\t %s: monitor cluster\n" "\t %s: subscribe session for FastDIR and FastStore server side\n" "\t %s: for all priviledges\n\n", argv[0], FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME, USER_PRIV_NAME_CREATE_POOL_STR, USER_PRIV_NAME_USER_MANAGE_STR, USER_PRIV_NAME_CREATE_POOL_STR, USER_PRIV_NAME_MONITOR_CLUSTER_STR, USER_PRIV_NAME_SUBSCRIBE_SESSION_STR, USER_PRIV_NAME_ALL_PRIVS_STR); } static int user_create_or_passwd(int argc, char *argv[], const int cmd) { string_t input_key_filename; FilenameString user_key_filename; unsigned char passwd_buff[FCFS_AUTH_PASSWD_LEN + 1]; const char *caption; char *filename; char abs_path[PATH_MAX]; int result; if (current_index < argc) { FC_SET_STRING(input_key_filename, argv[current_index]); } else { FC_SET_STRING(input_key_filename, "keys/${username}.key"); } fcfs_auth_replace_filename_with_username(&input_key_filename, &user.name, &user_key_filename); filename = FC_FILENAME_STRING_PTR(user_key_filename); getAbsolutePath(filename, abs_path, sizeof(abs_path)); if ((result=fc_mkdirs(abs_path, 0755)) != 0) { return result; } fcfs_auth_generate_passwd(passwd_buff); FC_SET_STRING_EX(user.passwd, (char *)passwd_buff, FCFS_AUTH_PASSWD_LEN); if (cmd == FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ) { caption = "create user"; result = fcfs_auth_client_user_create(&g_fcfs_auth_client_vars. client_ctx, &user); } else { caption = "regenerate secret key for user"; result = fcfs_auth_client_user_passwd(&g_fcfs_auth_client_vars. client_ctx, &user.name, &user.passwd); } if (result == 0) { if ((result=fcfs_auth_save_passwd(filename, passwd_buff)) == 0) { printf("%s %s success, secret key store to file: %s\n", caption, user.name.str, filename); } else { char hex_buff[2 * FCFS_AUTH_PASSWD_LEN + 1]; bin2hex((char *)passwd_buff, FCFS_AUTH_PASSWD_LEN, hex_buff); printf("%s %s success, but secret key store to " "file: %s fail, the secret key is:\n%s\n", caption, user.name.str, filename, hex_buff); } } else { fprintf(stderr, "%s %s fail\n", caption, user.name.str); } return result; } static inline int create_user(int argc, char *argv[]) { return user_create_or_passwd(argc, argv, FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ); } static inline int passwd_user(int argc, char *argv[]) { return user_create_or_passwd(argc, argv, FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ); } static int grant_privilege(int argc, char *argv[]) { int result; if (!priv_set) { fprintf(stderr, "expect parameter: granted priviledges!\n"); usage(argv); return EINVAL; } if ((result=fcfs_auth_client_user_grant(&g_fcfs_auth_client_vars. client_ctx, &user.name, user.priv)) == 0) { printf("grant user priviledge success\n"); } else { fprintf(stderr, "grant user priviledge fail\n"); } return result; } static int remove_user(int argc, char *argv[]) { int result; if ((result=fcfs_auth_client_user_remove(&g_fcfs_auth_client_vars. client_ctx, &user.name)) == 0) { printf("remove user %s success\n", user.name.str); } else { fprintf(stderr, "remove user %s fail\n", user.name.str); } return result; } static void output_users(FCFSAuthUserArray *array) { FCFSAuthUserInfo *user; FCFSAuthUserInfo *end; char buff[256]; string_t priv_names; printf("%5s %32s %32s\n", "No.", "username", "priviledges"); priv_names.str = buff; end = array->users + array->count; for (user=array->users; userusers) + 1), user->name.len, user->name.str, fcfs_auth_user_priv_to_string(user->priv, &priv_names)); } } static int list_user(int argc, char *argv[]) { struct fast_mpool_man mpool; FCFSAuthUserArray user_array; int result; if ((result=fast_mpool_init(&mpool, mpool_alloc_size_once, mpool_discard_size)) != 0) { return result; } fcfs_auth_user_init_array(&user_array); if ((result=fcfs_auth_client_user_list(&g_fcfs_auth_client_vars. client_ctx, &user.name, &limit, &mpool, &user_array)) == 0) { output_users(&user_array); } else { fprintf(stderr, "list user fail\n"); } fcfs_auth_user_free_array(&user_array); fast_mpool_destroy(&mpool); return result; } int main(int argc, char *argv[]) { int ch; const char *config_filename = FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME; char *operation; FCFSAuthClientUserKeyPair admin; unsigned char passwd_buff[FCFS_AUTH_PASSWD_LEN + 1]; FilenameString admin_key_filename; string_t passwd; string_t privs; bool need_username; int result; if (argc < 2) { usage(argv); return 1; } FC_SET_STRING(admin.username, "admin"); FC_SET_STRING(admin.key_filename, "/etc/fastcfs/auth/keys/${username}.key"); FC_SET_STRING_NULL(privs); while ((ch=getopt(argc, argv, "hc:u:k:p:y")) != -1) { switch (ch) { case 'h': usage(argv); return 0; case 'c': config_filename = optarg; break; case 'u': FC_SET_STRING_EX(admin.username, optarg, strlen(optarg)); break; case 'k': FC_SET_STRING_EX(admin.key_filename, optarg, strlen(optarg)); break; case 'p': FC_SET_STRING(privs, optarg); break; case 'y': confirmed = true; break; default: usage(argv); return 1; } } current_index = optind; if (current_index >= argc) { fprintf(stderr, "expect operation\n"); usage(argv); return 1; } srand(time(NULL)); log_init(); //g_log_context.log_level = LOG_DEBUG; operation = argv[current_index++]; if (strcasecmp(operation, "create") == 0) { need_username = true; } else if (strcasecmp(operation, "passwd") == 0 || strcasecmp(operation, "secret-key") == 0) { need_username = true; } else if (strcasecmp(operation, "grant") == 0) { need_username = true; } else if (strcasecmp(operation, "delete") == 0 || strcasecmp(operation, "remove") == 0) { need_username = true; } else if (strcasecmp(operation, "list") == 0) { need_username = false; } else { fprintf(stderr, "unknow operation: %s\n", operation); usage(argv); return 1; } if (current_index < argc) { FC_SET_STRING(user.name, argv[current_index]); current_index++; } else if (need_username) { fprintf(stderr, "expect username\n"); usage(argv); return 1; } else { FC_SET_STRING_NULL(user.name); } if (privs.str == NULL) { user.priv = FCFS_AUTH_USER_PRIV_CREATE_POOL; } else { if ((result=fcfs_auth_parse_user_priv(&privs, &user.priv)) != 0) { usage(argv); return 1; } priv_set = true; } passwd.str = (char *)passwd_buff; passwd.len = FCFS_AUTH_PASSWD_LEN; fcfs_auth_replace_filename_with_username(&admin.key_filename, &admin.username, &admin_key_filename); if ((result=fcfs_auth_load_passwd( FC_FILENAME_STRING_PTR(admin_key_filename), passwd_buff)) != 0) { return result; } if ((result=fcfs_auth_client_init(config_filename)) != 0) { return result; } if ((result=fcfs_auth_client_user_login(&g_fcfs_auth_client_vars. client_ctx, &admin.username, &passwd)) != 0) { return result; } if (strcasecmp(operation, "create") == 0) { return create_user(argc, argv); } else if (strcasecmp(operation, "passwd") == 0 || strcasecmp(operation, "secret-key") == 0) { if (fc_string_equal(&admin.username, &user.name) && !confirmed) { fprintf(stderr, "you MUST use -y option to regenerate " "the secret key \nwhen the user is same with " "the admin user!\n\n"); return EAGAIN; } return passwd_user(argc, argv); } else if (strcasecmp(operation, "grant") == 0) { return grant_privilege(argc, argv); } else if (strcasecmp(operation, "delete") == 0 || strcasecmp(operation, "remove") == 0) { return remove_user(argc, argv); } else if (strcasecmp(operation, "list") == 0) { return list_user(argc, argv); } return 0; } ================================================ FILE: src/auth/client/tools/tool_func.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "tool_func.h" #define FCFS_USER_PRIV_ARRAY_COUNT (FCFS_AUTH_USER_PRIV_COUNT + 1) static id_name_pair_t user_priv_list[FCFS_USER_PRIV_ARRAY_COUNT] = { {FCFS_AUTH_USER_PRIV_USER_MANAGE, {USER_PRIV_NAME_USER_MANAGE_STR, USER_PRIV_NAME_USER_MANAGE_LEN}}, {FCFS_AUTH_USER_PRIV_CREATE_POOL, {USER_PRIV_NAME_CREATE_POOL_STR, USER_PRIV_NAME_CREATE_POOL_LEN}}, {FCFS_AUTH_USER_PRIV_SUBSCRIBE_SESSION, {USER_PRIV_NAME_SUBSCRIBE_SESSION_STR, USER_PRIV_NAME_SUBSCRIBE_SESSION_LEN}}, {FCFS_AUTH_USER_PRIV_MONITOR_CLUSTER, {USER_PRIV_NAME_MONITOR_CLUSTER_STR, USER_PRIV_NAME_MONITOR_CLUSTER_LEN}}, {FCFS_AUTH_USER_PRIV_ALL, {USER_PRIV_NAME_ALL_PRIVS_STR, USER_PRIV_NAME_ALL_PRIVS_LEN}} }; static inline int64_t fcfs_auth_get_user_priv(const string_t *str) { id_name_pair_t *pair; id_name_pair_t *end; end = user_priv_list + FCFS_USER_PRIV_ARRAY_COUNT; for (pair=user_priv_list; pairname, str)) { return pair->id; } } return FCFS_AUTH_USER_PRIV_NONE; } static inline const string_t *fcfs_auth_get_user_priv_name(const int64_t priv) { id_name_pair_t *pair; id_name_pair_t *end; end = user_priv_list + FCFS_USER_PRIV_ARRAY_COUNT; for (pair=user_priv_list; pairid == priv) { return &pair->name; } } return NULL; } int fcfs_auth_parse_user_priv(const string_t *str, int64_t *priv) { const bool ignore_empty = true; string_t parts[2 * FCFS_AUTH_USER_PRIV_COUNT]; string_t *p; string_t *end; int64_t n; int count; *priv = 0; count = split_string_ex(str, ',', parts, sizeof(parts) / sizeof(string_t), ignore_empty); end = parts + count; for (p=parts; plen, p->str); return EINVAL; } *priv |= n; } return 0; } const char *fcfs_auth_user_priv_to_string( const int64_t priv, string_t *str) { id_name_pair_t *pair; id_name_pair_t *end; char *p; const string_t *name; if ((name=fcfs_auth_get_user_priv_name(priv)) != NULL) { memcpy(str->str, name->str, name->len); str->len = name->len; } else { p = str->str; end = user_priv_list + FCFS_USER_PRIV_ARRAY_COUNT; for (pair=user_priv_list; pairid) == pair->id) { memcpy(p, pair->name.str, pair->name.len); p += pair->name.len; *p++ = ','; } } str->len = p - str->str; if (str->len > 0) { str->len--; //remove last comma } } *(str->str + str->len) = '\0'; return str->str; } int fcfs_auth_parse_pool_access(const string_t *str, int *priv) { const char *p; const char *end; *priv = FCFS_AUTH_POOL_ACCESS_NONE; end = str->str + str->len; for (p=str->str; pstr; if ((priv & FCFS_AUTH_POOL_ACCESS_READ) != 0) { *p++ = POOL_ACCESS_NAME_READ_CHR; } if ((priv & FCFS_AUTH_POOL_ACCESS_WRITE) != 0) { *p++ = POOL_ACCESS_NAME_WRITE_CHR; } *p = '\0'; str->len = p - str->str; return str->str; } ================================================ FILE: src/auth/client/tools/tool_func.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_TOOL_FUNC_H #define _FCFS_AUTH_TOOL_FUNC_H #include "sf/sf_proto.h" #include "auth_types.h" #define USER_PRIV_NAME_USER_MANAGE_STR "user" #define USER_PRIV_NAME_CREATE_POOL_STR "pool" #define USER_PRIV_NAME_SUBSCRIBE_SESSION_STR "session" #define USER_PRIV_NAME_MONITOR_CLUSTER_STR "cluster" #define USER_PRIV_NAME_ALL_PRIVS_STR "*" #define USER_PRIV_NAME_USER_MANAGE_LEN \ (sizeof(USER_PRIV_NAME_USER_MANAGE_STR) - 1) #define USER_PRIV_NAME_CREATE_POOL_LEN \ (sizeof(USER_PRIV_NAME_CREATE_POOL_STR) - 1) #define USER_PRIV_NAME_SUBSCRIBE_SESSION_LEN \ (sizeof(USER_PRIV_NAME_SUBSCRIBE_SESSION_STR) - 1) #define USER_PRIV_NAME_MONITOR_CLUSTER_LEN \ (sizeof(USER_PRIV_NAME_MONITOR_CLUSTER_STR) - 1) #define USER_PRIV_NAME_ALL_PRIVS_LEN \ (sizeof(USER_PRIV_NAME_ALL_PRIVS_STR) - 1) #define POOL_ACCESS_NAME_READ_CHR 'r' #define POOL_ACCESS_NAME_WRITE_CHR 'w' #ifdef __cplusplus extern "C" { #endif int fcfs_auth_parse_user_priv(const string_t *str, int64_t *priv); const char *fcfs_auth_user_priv_to_string( const int64_t priv, string_t *str); int fcfs_auth_parse_pool_access(const string_t *str, int *priv); const char *fcfs_auth_pool_access_to_string(const int priv, string_t *str); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/common/auth_func.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fastcommon/system_info.h" #include "fastcommon/sched_thread.h" #include "fastcommon/md5.h" #include "fastcommon/http_func.h" #include "sf/sf_global.h" #include "auth_func.h" void fcfs_auth_generate_passwd(unsigned char passwd[16]) { struct { time_t current_time; pid_t pid; int random; #if defined(OS_LINUX) || defined(OS_FREEBSD) struct fast_sysinfo si; #endif int hash_codes[4]; } input; input.current_time = get_current_time(); input.pid = getpid(); input.random = rand(); #if defined(OS_LINUX) || defined(OS_FREEBSD) get_sysinfo(&input.si); #endif /* logInfo("procs: %d, pid: %d, random: %d", input.si.procs, input.pid, input.random); */ INIT_HASH_CODES4(input.hash_codes); CALC_HASH_CODES4(&input, sizeof(input), input.hash_codes); FINISH_HASH_CODES4(input.hash_codes); my_md5_buffer((char *)&input, sizeof(input), passwd); } int fcfs_auth_save_passwd(const char *filename, unsigned char passwd[16]) { char hex_buff[2 * FCFS_AUTH_PASSWD_LEN + 1]; bin2hex((char *)passwd, FCFS_AUTH_PASSWD_LEN, hex_buff); return safeWriteToFile(filename, hex_buff, FCFS_AUTH_PASSWD_LEN * 2); } int fcfs_auth_load_passwd_ex(const char *filename, unsigned char passwd[16], const bool ignore_enoent) { int result; int len; int64_t file_size; char hex_buff[2 * FCFS_AUTH_PASSWD_LEN + 4]; file_size = sizeof(hex_buff); if (IS_URL_RESOURCE(filename)) { int http_status; int content_len; char buff[8 * 1024]; char *content; char error_info[512]; content = buff; content_len = sizeof(buff); if ((result=get_url_content_ex(filename, strlen(filename), SF_G_CONNECT_TIMEOUT, SF_G_NETWORK_TIMEOUT, &http_status, &content, &content_len, error_info)) != 0) { if (*error_info != '\0') { logError("file: "__FILE__", line: %d, " "%s fetch fail, %s", __LINE__, filename, error_info); } else { logError("file: "__FILE__", line: %d, " "%s fetch fail, errno: %d, error info: %s", __LINE__, filename, result, STRERROR(result)); } return result; } if (http_status != 200) { if (http_status == 404 && ignore_enoent) { memset(passwd, 0, FCFS_AUTH_PASSWD_LEN); return 0; } logError("file: "__FILE__", line: %d, " "HTTP status code: %d != 200, url: %s", __LINE__, http_status, filename); return http_status == 404 ? ENOENT : EACCES; } if (content_len >= sizeof(hex_buff)) { logError("file: "__FILE__", line: %d, " "%s is not a valid secret file because the content " "length: %d >= %d", __LINE__, filename, content_len, (int)sizeof(hex_buff)); return EOVERFLOW; } memcpy(hex_buff, content, content_len + 1); file_size = content_len; } else { if (access(filename, F_OK) != 0) { if (errno == ENOENT && ignore_enoent) { memset(passwd, 0, FCFS_AUTH_PASSWD_LEN); return 0; } } if ((result=getFileContentEx(filename, hex_buff, 0, &file_size)) != 0) { return result; } } if (file_size > 2 * FCFS_AUTH_PASSWD_LEN) { trim_right(hex_buff); file_size = strlen(hex_buff); } if (file_size != 2 * FCFS_AUTH_PASSWD_LEN) { logError("file: "__FILE__", line: %d, " "invalid secret filename: %s whose file size MUST be: %d", __LINE__, filename, 2 * FCFS_AUTH_PASSWD_LEN); return EINVAL; } hex2bin(hex_buff, (char *)passwd, &len); return 0; } int fcfs_auth_replace_filename_with_username(const string_t *src, const string_t *username, FilenameString *new_filename) { #define USERNAME_VARIABLE_STR "${username}" #define USERNAME_VARIABLE_LEN (sizeof(USERNAME_VARIABLE_STR) - 1) string_t tag; FC_INIT_FILENAME_STRING(*new_filename); FC_SET_STRING_EX(tag, USERNAME_VARIABLE_STR, USERNAME_VARIABLE_LEN); return str_replace(src, &tag, username, &new_filename->s, FC_FILENAME_BUFFER_SIZE(*new_filename)); } int fcfs_auth_user_check_realloc_array(FCFSAuthUserArray *array, const int target_count) { int new_alloc; FCFSAuthUserInfo *new_users; if (array->alloc >= target_count) { return 0; } new_alloc = array->alloc; while (new_alloc < target_count) { new_alloc *= 2; } new_users = (FCFSAuthUserInfo *)fc_malloc( sizeof(FCFSAuthUserInfo) * new_alloc); if (new_users == NULL) { return ENOMEM; } if (array->count > 0) { int bytes; bytes = sizeof(FCFSAuthUserInfo) * array->count; memcpy(new_users, array->users, bytes); } if (array->users != array->fixed) { free(array->users); } array->users = new_users; array->alloc = new_alloc; return 0; } int fcfs_auth_spool_check_realloc_array(FCFSAuthStoragePoolArray *array, const int target_count) { int new_alloc; FCFSAuthStoragePoolInfo *new_spools; if (array->alloc >= target_count) { return 0; } new_alloc = array->alloc; while (new_alloc < target_count) { new_alloc *= 2; } new_spools = (FCFSAuthStoragePoolInfo *)fc_malloc( sizeof(FCFSAuthStoragePoolInfo) * new_alloc); if (new_spools == NULL) { return ENOMEM; } if (array->count > 0) { int bytes; bytes = sizeof(FCFSAuthStoragePoolInfo) * array->count; memcpy(new_spools, array->spools, bytes); } if (array->spools != array->fixed) { free(array->spools); } array->spools = new_spools; array->alloc = new_alloc; return 0; } int fcfs_auth_gpool_check_realloc_array(FCFSAuthGrantedPoolArray *array, const int target_count) { int new_alloc; FCFSAuthGrantedPoolFullInfo *new_gpools; if (array->alloc >= target_count) { return 0; } new_alloc = array->alloc; while (new_alloc < target_count) { new_alloc *= 2; } new_gpools = (FCFSAuthGrantedPoolFullInfo *)fc_malloc( sizeof(FCFSAuthGrantedPoolFullInfo) * new_alloc); if (new_gpools == NULL) { return ENOMEM; } if (array->count > 0) { int bytes; bytes = sizeof(FCFSAuthGrantedPoolFullInfo) * array->count; memcpy(new_gpools, array->gpools, bytes); } if (array->gpools != array->fixed) { free(array->gpools); } array->gpools = new_gpools; array->alloc = new_alloc; return 0; } ================================================ FILE: src/auth/common/auth_func.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_FUNC_H #define _FCFS_AUTH_FUNC_H #include "sf/sf_proto.h" #include "auth_types.h" #define fcfs_auth_load_passwd(filename, passwd) \ fcfs_auth_load_passwd_ex(filename, passwd, false) #ifdef __cplusplus extern "C" { #endif static inline void fcfs_auth_user_init_array(FCFSAuthUserArray *array) { array->users = array->fixed; array->alloc = FCFS_AUTH_FIXED_USER_COUNT; array->count = 0; } static inline void fcfs_auth_user_free_array(FCFSAuthUserArray *array) { if (array->users != array->fixed) { if (array->users != NULL) { free(array->users); } array->users = array->fixed; array->alloc = FCFS_AUTH_FIXED_USER_COUNT; } } static inline void fcfs_auth_spool_init_array( FCFSAuthStoragePoolArray *array) { array->spools = array->fixed; array->alloc = FCFS_AUTH_FIXED_USER_COUNT; array->count = 0; } static inline void fcfs_auth_spool_free_array( FCFSAuthStoragePoolArray *array) { if (array->spools != array->fixed) { if (array->spools != NULL) { free(array->spools); } array->spools = array->fixed; array->alloc = FCFS_AUTH_FIXED_USER_COUNT; } } static inline void fcfs_auth_granted_init_array( FCFSAuthGrantedPoolArray *array) { array->gpools = array->fixed; array->alloc = FCFS_AUTH_FIXED_USER_COUNT; array->count = 0; } static inline void fcfs_auth_granted_free_array( FCFSAuthGrantedPoolArray *array) { if (array->gpools != array->fixed) { if (array->gpools != NULL) { free(array->gpools); } array->gpools = array->fixed; array->alloc = FCFS_AUTH_FIXED_USER_COUNT; } } int fcfs_auth_user_check_realloc_array(FCFSAuthUserArray *array, const int target_count); int fcfs_auth_spool_check_realloc_array(FCFSAuthStoragePoolArray *array, const int target_count); int fcfs_auth_gpool_check_realloc_array(FCFSAuthGrantedPoolArray *array, const int target_count); void fcfs_auth_generate_passwd(unsigned char passwd[16]); int fcfs_auth_save_passwd(const char *filename, unsigned char passwd[16]); int fcfs_auth_load_passwd_ex(const char *filename, unsigned char passwd[16], const bool ignore_enoent); int fcfs_auth_replace_filename_with_username(const string_t *src, const string_t *username, FilenameString *new_filename); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/common/auth_global.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "auth_global.h" FCFSAuthGlobalVars g_fcfs_auth_global_vars = { {5, 5, 0} }; ================================================ FILE: src/auth/common/auth_global.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_GLOBAL_H #define _FCFS_AUTH_GLOBAL_H #include "fastcommon/common_define.h" typedef struct fcfs_auth_global_vars { Version version; } FCFSAuthGlobalVars; #ifdef __cplusplus extern "C" { #endif extern FCFSAuthGlobalVars g_fcfs_auth_global_vars; #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/common/auth_proto.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "auth_proto.h" void fcfs_auth_proto_init() { } const char *fcfs_auth_get_cmd_caption(const int cmd) { switch (cmd) { case FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_REQ: return "USER_LOGIN_REQ"; case FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_RESP: return "USER_LOGIN_RESP"; case FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_REQ: return "SESSION_SUBSCRIBE_REQ"; case FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_RESP: return "SESSION_SUBSCRIBE_RESP"; case FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_REQ: return "SESSION_PUSH_REQ"; case FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_RESP: return "SESSION_PUSH_RESP"; case FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_REQ: return "SESSION_VALIDATE_REQ"; case FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_RESP: return "SESSION_VALIDATE_RESP"; case FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ: return "USER_CREATE_REQ"; case FCFS_AUTH_SERVICE_PROTO_USER_CREATE_RESP: return "USER_CREATE_RESP"; case FCFS_AUTH_SERVICE_PROTO_USER_LIST_REQ: return "USER_LIST_REQ"; case FCFS_AUTH_SERVICE_PROTO_USER_LIST_RESP: return "USER_LIST_RESP"; case FCFS_AUTH_SERVICE_PROTO_USER_GRANT_REQ: return "USER_GRANT_REQ"; case FCFS_AUTH_SERVICE_PROTO_USER_GRANT_RESP: return "USER_GRANT_RESP"; case FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_REQ: return "USER_REMOVE_REQ"; case FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_RESP: return "USER_REMOVE_RESP"; case FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ: return "USER_PASSWD_REQ"; case FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_RESP: return "USER_PASSWD_RESP"; case FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ: return "SPOOL_CREATE_REQ"; case FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_RESP: return "SPOOL_CREATE_RESP"; case FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ: return "SPOOL_LIST_REQ"; case FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_RESP: return "SPOOL_LIST_RESP"; case FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ: return "SPOOL_REMOVE_REQ"; case FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_RESP: return "SPOOL_REMOVE_RESP"; case FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ: return "SPOOL_SET_QUOTA_REQ"; case FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_RESP: return "SPOOL_SET_QUOTA_RESP"; case FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_REQ: return "SPOOL_GET_QUOTA_REQ"; case FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_RESP: return "SPOOL_GET_QUOTA_RESP"; case FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ: return "GPOOL_GRANT_REQ"; case FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_RESP: return "GPOOL_GRANT_RESP"; case FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ: return "GPOOL_WITHDRAW_REQ"; case FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_RESP: return "GPOOL_WITHDRAW_RESP"; case FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ: return "GPOOL_LIST_REQ"; case FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_RESP: return "GPOOL_LIST_RESP"; case FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ: return "GET_MASTER_REQ"; case FCFS_AUTH_SERVICE_PROTO_GET_MASTER_RESP: return "GET_MASTER_RESP"; case FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_REQ: return "CLUSTER_STAT_REQ"; case FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_RESP: return "CLUSTER_STAT_RESP"; case FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_REQ: return "GET_SERVER_STATUS_REQ"; case FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_RESP: return "GET_SERVER_STATUS_RESP"; case FCFS_AUTH_CLUSTER_PROTO_JOIN_MASTER: return "JOIN_MASTER"; case FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_REQ: return "PING_MASTER_REQ"; case FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_RESP: return "PING_MASTER_RESP"; case FCFS_AUTH_CLUSTER_PROTO_PRE_SET_NEXT_MASTER: return "PRE_SET_NEXT_MASTER"; case FCFS_AUTH_CLUSTER_PROTO_COMMIT_NEXT_MASTER: return "COMMIT_NEXT_MASTER"; default: return sf_get_cmd_caption(cmd); } } ================================================ FILE: src/auth/common/auth_proto.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_PROTO_H #define _FCFS_AUTH_PROTO_H #include "sf/sf_proto.h" #include "auth_types.h" #define FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_REQ 9 #define FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_RESP 10 #define FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_REQ 11 #define FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_RESP 12 #define FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_REQ 13 #define FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_RESP 14 #define FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_REQ 15 #define FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_RESP 16 #define FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ 21 #define FCFS_AUTH_SERVICE_PROTO_USER_CREATE_RESP 22 #define FCFS_AUTH_SERVICE_PROTO_USER_LIST_REQ 23 #define FCFS_AUTH_SERVICE_PROTO_USER_LIST_RESP 24 #define FCFS_AUTH_SERVICE_PROTO_USER_GRANT_REQ 25 #define FCFS_AUTH_SERVICE_PROTO_USER_GRANT_RESP 26 #define FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_REQ 27 #define FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_RESP 28 #define FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ 29 #define FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_RESP 30 #define FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ 61 #define FCFS_AUTH_SERVICE_PROTO_GET_MASTER_RESP 62 #define FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_REQ 63 #define FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_RESP 64 #define FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ 71 #define FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_RESP 72 #define FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ 73 #define FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_RESP 74 #define FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ 75 #define FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_RESP 76 #define FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ 77 #define FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_RESP 78 #define FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_REQ 79 #define FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_RESP 80 #define FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ 91 #define FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_RESP 92 #define FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ 93 #define FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_RESP 94 #define FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ 95 #define FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_RESP 96 //cluster commands #define FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_REQ 201 #define FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_RESP 202 #define FCFS_AUTH_CLUSTER_PROTO_JOIN_MASTER 203 //slave -> master #define FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_REQ 205 #define FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_RESP 206 #define FCFS_AUTH_CLUSTER_PROTO_PRE_SET_NEXT_MASTER 207 //notify next leader to other servers #define FCFS_AUTH_CLUSTER_PROTO_COMMIT_NEXT_MASTER 208 //commit next leader to other servers typedef SFCommonProtoHeader FCFSAuthProtoHeader; typedef struct fcfs_auth_proto_name_info { unsigned char len; char str[0]; } FCFSAuthProtoNameInfo; typedef struct fcfs_auth_proto_user_passwd_pair { char passwd[FCFS_AUTH_PASSWD_LEN]; FCFSAuthProtoNameInfo username; } FCFSAuthProtoUserPasswdPair; typedef struct fcfs_auth_proto_user_pool_pair { FCFSAuthProtoNameInfo username; FCFSAuthProtoNameInfo poolname; } FCFSAuthProtoUserPoolPair; typedef struct fcfs_auth_proto_pool_priviledges { char fdir[4]; char fstore[4]; } FCFSAuthProtoPoolPriviledges; typedef struct fcfs_auth_proto_user_login_req { unsigned char flags; FCFSAuthProtoUserPasswdPair up_pair; FCFSAuthProtoNameInfo poolname; } FCFSAuthProtoUserLoginReq; typedef struct fcfs_auth_proto_user_login_resp { char session_id[FCFS_AUTH_SESSION_ID_LEN]; } FCFSAuthProtoUserLoginResp; typedef struct fcfs_auth_proto_session_subscribe_req { FCFSAuthProtoUserPasswdPair up_pair; } FCFSAuthProtoSessionSubscribeReq; typedef struct fcfs_auth_proto_session_validate_req { char session_id[FCFS_AUTH_SESSION_ID_LEN]; char validate_key[FCFS_AUTH_PASSWD_LEN]; char priv_type; char padding[7]; char pool_id[8]; char priv_required[8]; } FCFSAuthProtoSessionValidateReq; typedef struct fcfs_auth_proto_session_validate_resp { char result[4]; } FCFSAuthProtoSessionValidateResp; typedef struct fcfs_auth_proto_session_push_resp_body_header { char count[4]; } FCFSAuthProtoSessionPushRespBodyHeader; typedef struct fcfs_auth_proto_session_push_entry { struct { char available; char id[8]; FCFSAuthProtoPoolPriviledges privs; } pool; struct { char id[8]; char priv[8]; } user; } FCFSAuthProtoSessionPushEntry; typedef struct fcfs_auth_proto_session_push_resp_body_part { char session_id[FCFS_AUTH_SESSION_ID_LEN]; char operation; FCFSAuthProtoSessionPushEntry entry[0]; } FCFSAuthProtoSessionPushRespBodyPart; typedef struct fcfs_auth_proto_user_create_req { char priv[8]; FCFSAuthProtoUserPasswdPair up_pair; } FCFSAuthProtoUserCreateReq; typedef struct fcfs_auth_proto_user_passwd_req { FCFSAuthProtoUserPasswdPair up_pair; } FCFSAuthProtoUserPasswdReq; typedef struct fcfs_auth_proto_user_list_req { SFProtoLimitInfo limit; FCFSAuthProtoNameInfo username; } FCFSAuthProtoUserListReq; typedef struct fcfs_auth_proto_list_resp_header { char count[4]; char is_last; char padding[3]; } FCFSAuthProtoListRespHeader; typedef struct fcfs_auth_proto_user_list_resp_body_part { char priv[8]; FCFSAuthProtoNameInfo username; } FCFSAuthProtoUserListRespBodyPart; typedef struct fcfs_auth_proto_user_grant_req { char priv[8]; FCFSAuthProtoNameInfo username; } FCFSAuthProtoUserGrantReq; typedef struct fcfs_auth_proto_user_remove_req { FCFSAuthProtoNameInfo username; } FCFSAuthProtoUserRemoveReq; typedef struct fcfs_auth_proto_spool_create_req { char quota[8]; char dryrun; FCFSAuthProtoNameInfo poolname; } FCFSAuthProtoSPoolCreateReq; typedef struct fcfs_auth_proto_spool_create_resp { FCFSAuthProtoNameInfo poolname; } FCFSAuthProtoSPoolCreateResp; typedef struct fcfs_auth_proto_spool_list_req { SFProtoLimitInfo limit; FCFSAuthProtoUserPoolPair up_pair; } FCFSAuthProtoSPoolListReq; typedef struct fcfs_auth_proto_spool_list_resp_body_part { char quota[8]; FCFSAuthProtoNameInfo poolname; } FCFSAuthProtoSPoolListRespBodyPart; typedef struct fcfs_auth_proto_spool_remove_req { FCFSAuthProtoNameInfo poolname; } FCFSAuthProtoSPoolRemoveReq; typedef struct fcfs_auth_proto_spool_set_quota_req { char quota[8]; FCFSAuthProtoNameInfo poolname; } FCFSAuthProtoSPoolSetQuotaReq; typedef struct fcfs_auth_proto_spool_get_quota_req { FCFSAuthProtoNameInfo poolname; } FCFSAuthProtoSPoolGetQuotaReq; typedef struct fcfs_auth_proto_spool_get_quota_resp { char quota[8]; } FCFSAuthProtoSPoolGetQuotaResp; typedef struct fcfs_auth_proto_spool_grant_req { FCFSAuthProtoPoolPriviledges privs; FCFSAuthProtoUserPoolPair up_pair; } FCFSAuthProtoSPoolGrantReq; typedef struct fcfs_auth_proto_spool_withdraw_req { FCFSAuthProtoUserPoolPair up_pair; } FCFSAuthProtoSPoolWithdrawReq; typedef FCFSAuthProtoSPoolListReq FCFSAuthProtoGPoolListReq; typedef struct fcfs_auth_proto_gpool_list_resp_body_part { FCFSAuthProtoPoolPriviledges privs; FCFSAuthProtoUserPoolPair up_pair; } FCFSAuthProtoGPoolListRespBodyPart; typedef struct fcfs_auth_proto_get_server_resp { char server_id[4]; char port[2]; char padding[2]; char ip_addr[IP_ADDRESS_SIZE]; } FCFSAuthProtoGetServerResp; typedef struct fcfs_auth_proto_get_server_status_req { char server_id[4]; char config_sign[SF_CLUSTER_CONFIG_SIGN_LEN]; } FCFSAuthProtoGetServerStatusReq; typedef struct fcfs_auth_proto_get_server_status_resp { char server_id[4]; char is_master; char padding[3]; } FCFSAuthProtoGetServerStatusResp; typedef struct fcfs_auth_proto_join_master_req { char server_id[4]; //the slave server id char padding[4]; char config_sign[SF_CLUSTER_CONFIG_SIGN_LEN]; } FCFSAuthProtoJoinMasterReq; typedef struct fcfs_auth_proto_cluster_stat_resp_body_header { char count[4]; char padding[4]; } FCFSAuthProtoClusterStatRespBodyHeader; typedef struct fcfs_auth_proto_cluster_stat_resp_body_part { char server_id[4]; char is_online; char is_master; char padding[1]; char port[2]; char ip_addr[IP_ADDRESS_SIZE]; } FCFSAuthProtoClusterStatRespBodyPart; #define auth_log_network_error_ex(response, conn, result, log_level) \ sf_log_network_error_ex(response, conn, "fauth", result, log_level) #define auth_log_network_error(response, conn, result) \ sf_log_network_error(response, conn, "fauth", result) #ifdef __cplusplus extern "C" { #endif void fcfs_auth_proto_init(); const char *fcfs_auth_get_cmd_caption(const int cmd); static inline void fcfs_auth_parse_user_pool_pair(FCFSAuthProtoUserPoolPair *up_pair, string_t *username, string_t *poolname) { FCFSAuthProtoNameInfo *proto_pname; FC_SET_STRING_EX(*username, up_pair->username.str, up_pair->username.len); proto_pname = (FCFSAuthProtoNameInfo *)(up_pair->username.str + up_pair->username.len); FC_SET_STRING_EX(*poolname, proto_pname->str, proto_pname->len); } static inline void fcfs_auth_pack_user_pool_pair(const string_t *username, const string_t *poolname, FCFSAuthProtoUserPoolPair *up_pair) { FCFSAuthProtoNameInfo *proto_pname; up_pair->username.len = username->len; if (username->len > 0) { memcpy(up_pair->username.str, username->str, username->len); } proto_pname = (FCFSAuthProtoNameInfo *)(up_pair-> username.str + username->len); proto_pname->len = poolname->len; if (poolname->len > 0) { memcpy(proto_pname->str, poolname->str, poolname->len); } } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/common/auth_types.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_TYPES_H #define _FCFS_AUTH_TYPES_H #include "fastcommon/common_define.h" #include "sf/sf_types.h" #define FCFS_AUTH_DEFAULT_CLUSTER_PORT 31011 #define FCFS_AUTH_DEFAULT_SERVICE_PORT 31012 #define FCFS_AUTH_SESSION_FLAGS_PUBLISH 1 #define FCFS_AUTH_USERNAME_MAX_LEN 64 #define FCFS_AUTH_PASSWD_LEN 16 #define FCFS_AUTH_SESSION_ID_LEN 8 #define FCFS_AUTH_FIXED_USER_COUNT 256 #define FCFS_AUTH_FIXED_POOL_COUNT 256 #define FCFS_AUTH_USER_STATUS_NORMAL 0 #define FCFS_AUTH_USER_STATUS_DELETED 1 #define FCFS_AUTH_POOL_STATUS_NORMAL 0 #define FCFS_AUTH_POOL_STATUS_DELETED 1 #define FCFS_AUTH_USER_PRIV_NONE 0 #define FCFS_AUTH_USER_PRIV_USER_MANAGE (1 << 0) #define FCFS_AUTH_USER_PRIV_CREATE_POOL (1 << 1) #define FCFS_AUTH_USER_PRIV_MONITOR_CLUSTER (1 << 2) #define FCFS_AUTH_USER_PRIV_SUBSCRIBE_SESSION (1 << 3) #define FCFS_AUTH_USER_PRIV_ALL (FCFS_AUTH_USER_PRIV_USER_MANAGE | \ FCFS_AUTH_USER_PRIV_CREATE_POOL | \ FCFS_AUTH_USER_PRIV_MONITOR_CLUSTER | \ FCFS_AUTH_USER_PRIV_SUBSCRIBE_SESSION) #define FCFS_AUTH_USER_PRIV_COUNT 4 #define FCFS_AUTH_POOL_ACCESS_NONE 0 #define FCFS_AUTH_POOL_ACCESS_WRITE (1 << 1) #define FCFS_AUTH_POOL_ACCESS_READ (1 << 2) #define FCFS_AUTH_POOL_ACCESS_ALL (FCFS_AUTH_POOL_ACCESS_WRITE | \ FCFS_AUTH_POOL_ACCESS_READ) #define FCFS_AUTH_POOL_ACCESS_COUNT 2 #define FCFS_AUTH_UNLIMITED_QUOTA_STR "unlimited" #define FCFS_AUTH_UNLIMITED_QUOTA_VAL -1 #define FCFS_AUTH_AUTO_ID_TAG_STR "${auto_id}" #define FCFS_AUTH_AUTO_ID_TAG_LEN (sizeof(FCFS_AUTH_AUTO_ID_TAG_STR) - 1) #define FCFS_AUTH_PUSH_OPERATION_CREATE_SESSION 'C' #define FCFS_AUTH_PUSH_OPERATION_UPDATE_SESSION 'U' #define FCFS_AUTH_PUSH_OPERATION_REMOVE_SESSION 'R' #define FCFS_AUTH_POOL_AVAILABLE(pool) \ ((pool.quota == FCFS_AUTH_UNLIMITED_QUOTA_VAL) || \ (pool.used < pool.quota)) typedef enum { fcfs_auth_validate_priv_type_user = 'u', fcfs_auth_validate_priv_type_pool_fdir = 'd', fcfs_auth_validate_priv_type_pool_fstore = 's' } FCFSAuthValidatePriviledgeType; typedef struct fcfs_auth_spool_priviledges { int fdir; int fstore; } FCFSAuthSPoolPriviledges; typedef struct fcfs_auth_storage_pool_info { int64_t id; string_t name; int64_t quota; //bytes int64_t used; //bytes int status; } FCFSAuthStoragePoolInfo; typedef struct fcfs_auth_granted_pool_info { int64_t id; int64_t pool_id; FCFSAuthSPoolPriviledges privs; } FCFSAuthGrantedPoolInfo; typedef struct fcfs_auth_granted_pool_full_info { FCFSAuthGrantedPoolInfo granted; string_t username; string_t pool_name; } FCFSAuthGrantedPoolFullInfo; typedef struct fcfs_auth_user_info { int64_t id; string_t name; string_t passwd; int64_t priv; int status; } FCFSAuthUserInfo; typedef struct fcfs_auth_storage_pool_array { FCFSAuthStoragePoolInfo *spools; FCFSAuthStoragePoolInfo fixed[FCFS_AUTH_FIXED_POOL_COUNT]; int count; int alloc; } FCFSAuthStoragePoolArray; typedef struct fcfs_auth_granted_pool_array { FCFSAuthGrantedPoolFullInfo *gpools; FCFSAuthGrantedPoolFullInfo fixed[FCFS_AUTH_FIXED_POOL_COUNT]; int count; int alloc; } FCFSAuthGrantedPoolArray; typedef struct fcfs_auth_user_array { FCFSAuthUserInfo *users; FCFSAuthUserInfo fixed[FCFS_AUTH_FIXED_USER_COUNT]; int count; int alloc; } FCFSAuthUserArray; #endif ================================================ FILE: src/auth/common/server_session.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/shared_func.h" #include "fastcommon/pthread_func.h" #include "fastcommon/fast_mblock.h" #include "auth_func.h" #include "server_session.h" #define SESSION_MIN_SHARED_ALLOCATOR_COUNT 1 #define SESSION_MAX_SHARED_ALLOCATOR_COUNT 1000 #define SESSION_DEFAULT_SHARED_ALLOCATOR_COUNT 11 #define SESSION_MIN_HASHTABLE_CAPACITY 1361 #define SESSION_MAX_HASHTABLE_CAPACITY 11229331 #define SESSION_DEFAULT_HASHTABLE_CAPACITY 10949 #define SESSION_MIN_SHARED_LOCK_COUNT 1 #define SESSION_MAX_SHARED_LOCK_COUNT 10000 #define SESSION_DEFAULT_SHARED_LOCK_COUNT 163 #define SESSION_MIN_VALIDATE_WITHIN_FRESH_SECONDS 1 #define SESSION_MAX_VALIDATE_WITHIN_FRESH_SECONDS 31536000 #define SESSION_DEFAULT_VALIDATE_WITHIN_FRESH_SECONDS 5 typedef struct { ServerSessionHashEntry **buckets; int capacity; } ServerSessionHashtable; typedef struct { int count; volatile uint32_t next; struct fast_mblock_man *allocators; } ServerSessionAllocatorArray; typedef struct { int count; pthread_mutex_t *locks; } ServerSessionLockArray; typedef struct { int fields_size; volatile uint16_t sn; //for generate session id ServerSessionAllocatorArray allocator_array; ServerSessionLockArray lock_array; ServerSessionHashtable htable; ServerSessionCallbacks callbacks; } ServerSessionContext; ServerSessionConfig g_server_session_cfg; static ServerSessionContext session_ctx = {0, 0, {0, 0, NULL}}; #define SESSION_SET_HASHTABLE_LOCK(htable, session_id) \ int32_t bucket_index; \ pthread_mutex_t *lock; \ bucket_index = session_id % (htable).capacity; \ lock = session_ctx.lock_array.locks + (bucket_index % \ session_ctx.lock_array.count) #define SESSION_SET_BUCKET_AND_LOCK(htable, session_id) \ ServerSessionHashEntry **bucket; \ SESSION_SET_HASHTABLE_LOCK(htable, session_id); \ bucket = (htable).buckets + bucket_index void server_session_cfg_to_string_ex(char *buff, const int size, const bool output_all) { int len; len = snprintf(buff, size, "session {shared_allocator_count: %d, " "shared_lock_count: %d, hashtable_capacity: %d, " "validate_key_filename: %s", g_server_session_cfg.shared_allocator_count, g_server_session_cfg.shared_lock_count, g_server_session_cfg.hashtable_capacity, g_server_session_cfg.validate_key_filename.str); if (output_all) { snprintf(buff + len, size - len, ", validate_within_fresh_seconds: %d}", g_server_session_cfg.validate_within_fresh_seconds); } else { snprintf(buff + len, size - len, "}"); } } static int load_session_validate_key(IniFullContext *ini_ctx) { int result; char *key_filename; char full_key_filename[PATH_MAX]; string_t validate_key_filename; key_filename = iniGetStrValue(ini_ctx->section_name, "validate_key_filename", ini_ctx->context); if (key_filename == NULL || *key_filename == '\0') { key_filename = "keys/session_validate.key"; } validate_key_filename.str = full_key_filename; validate_key_filename.len = resolve_path(ini_ctx->filename, key_filename, full_key_filename, sizeof(full_key_filename)); if ((result=fcfs_auth_load_passwd(validate_key_filename.str, g_server_session_cfg.validate_key_buff)) != 0) { logError("file: "__FILE__", line: %d, " "config file: %s, validate_key_filename: %s, " "load password fail, ret code: %d", __LINE__, ini_ctx->filename, validate_key_filename.str, result); return result; } g_server_session_cfg.validate_key.str = (char *) g_server_session_cfg.validate_key_buff; g_server_session_cfg.validate_key.len = FCFS_AUTH_PASSWD_LEN; g_server_session_cfg.validate_key_filename.str = (char *) fc_malloc(validate_key_filename.len + 1); if (g_server_session_cfg.validate_key_filename.str == NULL) { return ENOMEM; } g_server_session_cfg.validate_key_filename.len = validate_key_filename.len; memcpy(g_server_session_cfg.validate_key_filename.str, validate_key_filename.str, validate_key_filename.len + 1); return 0; } static int do_load_session_cfg(const char *session_filename) { IniContext ini_context; IniFullContext ini_ctx; int result; if ((result=iniLoadFromFile(session_filename, &ini_context)) != 0) { logError("file: "__FILE__", line: %d, " "load session config file \"%s\" fail, ret code: %d", __LINE__, session_filename, result); return result; } FAST_INI_SET_FULL_CTX_EX(ini_ctx, session_filename, NULL, &ini_context); session_ctx.allocator_array.count = iniGetIntCorrectValue( &ini_ctx, "shared_allocator_count", SESSION_DEFAULT_SHARED_ALLOCATOR_COUNT, SESSION_MIN_SHARED_ALLOCATOR_COUNT, SESSION_MAX_SHARED_ALLOCATOR_COUNT); session_ctx.lock_array.count = iniGetInt64CorrectValue( &ini_ctx, "shared_lock_count", SESSION_DEFAULT_SHARED_LOCK_COUNT, SESSION_MIN_SHARED_LOCK_COUNT, SESSION_MAX_SHARED_LOCK_COUNT); session_ctx.htable.capacity = iniGetIntCorrectValue( &ini_ctx, "hashtable_capacity", SESSION_DEFAULT_HASHTABLE_CAPACITY, SESSION_MIN_HASHTABLE_CAPACITY, SESSION_MAX_HASHTABLE_CAPACITY); g_server_session_cfg.validate_within_fresh_seconds = iniGetIntCorrectValue(&ini_ctx, "validate_within_fresh_seconds", SESSION_DEFAULT_VALIDATE_WITHIN_FRESH_SECONDS, SESSION_MIN_VALIDATE_WITHIN_FRESH_SECONDS, SESSION_MAX_VALIDATE_WITHIN_FRESH_SECONDS); g_server_session_cfg.shared_allocator_count = session_ctx.allocator_array.count; g_server_session_cfg.shared_lock_count = session_ctx.lock_array.count; g_server_session_cfg.hashtable_capacity = session_ctx.htable.capacity; result = load_session_validate_key(&ini_ctx); iniFreeContext(&ini_context); return result; } static int load_session_config(IniFullContext *ini_ctx) { char *session_config_filename; char full_session_filename[PATH_MAX]; session_config_filename = iniGetStrValue(NULL, "session_config_filename", ini_ctx->context); if (session_config_filename == NULL || *session_config_filename == '\0') { logError("file: "__FILE__", line: %d, " "config file: %s, item \"session_config_filename\" " "not exist or empty", __LINE__, ini_ctx->filename); return ENOENT; } resolve_path(ini_ctx->filename, session_config_filename, full_session_filename, sizeof(full_session_filename)); return do_load_session_cfg(full_session_filename); } static int server_session_alloc_init(ServerSessionHashEntry *session, struct fast_mblock_man *allocator) { session->allocator = allocator; session->entry.fields = session + 1; return 0; } static int init_allocator_array(ServerSessionAllocatorArray *array) { int result; int bytes; int element_size; struct fast_mblock_man *mblock; struct fast_mblock_man *end; bytes = sizeof(struct fast_mblock_man) * array->count; array->allocators = (struct fast_mblock_man *)fc_malloc(bytes); if (array->allocators == NULL) { return ENOMEM; } element_size = sizeof(ServerSessionHashEntry) + session_ctx.fields_size; end = array->allocators + array->count; for (mblock=array->allocators; mblockcount; array->locks = (pthread_mutex_t *)fc_malloc(bytes); if (array->locks == NULL) { return ENOMEM; } end = array->locks + array->count; for (lock=array->locks; lockcapacity; htable->buckets = (ServerSessionHashEntry **)fc_malloc(bytes); if (htable->buckets == NULL) { return ENOMEM; } memset(htable->buckets, 0, bytes); return 0; } int server_session_init_ex(IniFullContext *ini_ctx, const int fields_size, ServerSessionCallbacks *callbacks) { int result; if ((result=load_session_config(ini_ctx)) != 0) { return result; } session_ctx.fields_size = fields_size; if ((result=init_allocator_array(&session_ctx.allocator_array)) != 0) { return result; } if ((result=init_lock_array(&session_ctx.lock_array)) != 0) { return result; } if ((result=init_hashtable(&session_ctx.htable)) != 0) { return result; } if (callbacks != NULL) { session_ctx.callbacks = *callbacks; } else { session_ctx.callbacks.add_func = NULL; session_ctx.callbacks.del_func = NULL; } srand(time(NULL)); return 0; } static inline ServerSessionHashEntry *session_htable_find(ServerSessionHashEntry **bucket, const uint64_t session_id, ServerSessionHashEntry **prev) { ServerSessionHashEntry *current; if (*bucket == NULL) { *prev = NULL; return NULL; } else if ((*bucket)->entry.id_info.id == session_id) { *prev = NULL; return *bucket; } else if ((*bucket)->entry.id_info.id > session_id) { *prev = NULL; return NULL; } *prev = *bucket; while ((current=(*prev)->next) != NULL) { if (current->entry.id_info.id == session_id) { return current; } else if (current->entry.id_info.id > session_id) { return NULL; } *prev = current; } return NULL; } static int session_htable_insert(ServerSessionHashEntry *se, const bool replace) { int result; ServerSessionHashEntry *previous; ServerSessionHashEntry *found; SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, se->entry.id_info.id); PTHREAD_MUTEX_LOCK(lock); if ((found=session_htable_find(bucket, se->entry. id_info.id, &previous)) == NULL) { if (previous == NULL) { se->next = *bucket; *bucket = se; } else { se->next = previous->next; previous->next = se; } result = 0; } else { if (replace) { found->entry = se->entry; fast_mblock_free_object(se->allocator, se); result = 0; } else { result = EEXIST; } } PTHREAD_MUTEX_UNLOCK(lock); return result; } ServerSessionEntry *server_session_add_ex(const ServerSessionEntry *entry, const bool publish, const bool persistent) { int result; bool replace; struct fast_mblock_man *allocator; ServerSessionHashEntry *se; allocator = session_ctx.allocator_array.allocators + (__sync_fetch_and_add(&session_ctx.allocator_array.next, 1) % session_ctx.allocator_array.count); se = (ServerSessionHashEntry *)fast_mblock_alloc_object(allocator); if (se == NULL) { return NULL; } memcpy(se->entry.fields, entry->fields, session_ctx.fields_size); if (entry->id_info.id == 0) { replace = false; do { se->entry.id_info.fields.ts = g_current_time; se->entry.id_info.fields.publish = (publish ? 1 : 0); se->entry.id_info.fields.persistent = (persistent ? 1 : 0); se->entry.id_info.fields.rn = (int64_t)rand() * 16384LL / RAND_MAX; se->entry.id_info.fields.sn = __sync_add_and_fetch( &session_ctx.sn, 1); /* logInfo("session_id: %"PRId64", ts: %d, publish: %d, " "rand: %u, sn: %u", se->entry.id_info.id, se->entry.id_info.fields.ts, publish, se->entry.id_info.fields.rn, se->entry.id_info.fields.sn); */ result = session_htable_insert(se, replace); } while (result == EEXIST); } else { replace = true; se->entry.id_info.id = entry->id_info.id; session_htable_insert(se, replace); } if (session_ctx.callbacks.add_func != NULL) { session_ctx.callbacks.add_func(&se->entry); } return &se->entry; } int server_session_get_fields(const uint64_t session_id, void *fields) { int result; ServerSessionHashEntry *previous; ServerSessionHashEntry *found; SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, session_id); PTHREAD_MUTEX_LOCK(lock); if ((found=session_htable_find(bucket, session_id, &previous)) != NULL) { memcpy(fields, found->entry.fields, session_ctx.fields_size); result = 0; } else { result = SF_SESSION_ERROR_NOT_EXIST; } PTHREAD_MUTEX_UNLOCK(lock); return result; } int server_session_user_priv_granted(const uint64_t session_id, const int64_t the_priv) { int result; ServerSessionHashEntry *previous; ServerSessionHashEntry *found; SessionSyncedFields *fields; SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, session_id); PTHREAD_MUTEX_LOCK(lock); if ((found=session_htable_find(bucket, session_id, &previous)) != NULL) { fields = (SessionSyncedFields *)found->entry.fields; if ((fields->user.priv & the_priv) == the_priv) { result = 0; } else { result = EPERM; } } else { result = SF_SESSION_ERROR_NOT_EXIST; } PTHREAD_MUTEX_UNLOCK(lock); return result; } int server_session_fstore_priv_granted(const uint64_t session_id, const int the_priv) { int result; ServerSessionHashEntry *previous; ServerSessionHashEntry *found; SessionSyncedFields *fields; SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, session_id); PTHREAD_MUTEX_LOCK(lock); if ((found=session_htable_find(bucket, session_id, &previous)) != NULL) { fields = (SessionSyncedFields *)found->entry.fields; if ((fields->pool.privs.fstore & the_priv) == the_priv) { result = 0; } else { result = EPERM; } } else { result = SF_SESSION_ERROR_NOT_EXIST; } PTHREAD_MUTEX_UNLOCK(lock); return result; } int server_session_fdir_priv_granted(const uint64_t session_id, const int the_priv) { int result; ServerSessionHashEntry *previous; ServerSessionHashEntry *found; SessionSyncedFields *fields; SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, session_id); PTHREAD_MUTEX_LOCK(lock); if ((found=session_htable_find(bucket, session_id, &previous)) != NULL) { fields = (SessionSyncedFields *)found->entry.fields; if ((fields->pool.privs.fdir & the_priv) == the_priv) { result = 0; } else { result = EPERM; } } else { result = SF_SESSION_ERROR_NOT_EXIST; } PTHREAD_MUTEX_UNLOCK(lock); return result; } static inline void free_hash_entry(ServerSessionHashEntry *entry) { if (session_ctx.callbacks.del_func != NULL) { session_ctx.callbacks.del_func(&entry->entry); } fast_mblock_free_object(entry->allocator, entry); } int server_session_delete(const uint64_t session_id) { ServerSessionHashEntry *previous; ServerSessionHashEntry *found; SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, session_id); PTHREAD_MUTEX_LOCK(lock); if ((found=session_htable_find(bucket, session_id, &previous)) != NULL) { if (previous == NULL) { *bucket = (*bucket)->next; } else { previous->next = found->next; } } PTHREAD_MUTEX_UNLOCK(lock); if (found != NULL) { free_hash_entry(found); return 0; } else { return ENOENT; } } #define SERVER_SESSION_ADD_TO_CHAIN(chain, node) \ if (chain.tail == NULL) { \ chain.head = node; \ } else { \ chain.tail->next = node; \ } \ chain.tail = node void server_session_clear() { typedef struct { ServerSessionHashEntry *head; ServerSessionHashEntry *tail; } ServerSessionHashChain; ServerSessionHashEntry *current; ServerSessionHashEntry *deleted; ServerSessionHashEntry **bucket; ServerSessionHashEntry **end; ServerSessionHashChain keep; ServerSessionHashChain remove; pthread_mutex_t *lock; int64_t keep_count; int64_t remove_count; keep_count = remove_count = 0; current = NULL; end = session_ctx.htable.buckets + session_ctx.htable.capacity; for (bucket=session_ctx.htable.buckets; bucketentry.id_info.fields.persistent) { SERVER_SESSION_ADD_TO_CHAIN(keep, current); keep_count++; } else { SERVER_SESSION_ADD_TO_CHAIN(remove, current); remove_count++; } current = current->next; } while (current != NULL); *bucket = keep.head; if (keep.tail != NULL) { keep.tail->next = NULL; } current = remove.head; if (remove.tail != NULL) { remove.tail->next = NULL; } } PTHREAD_MUTEX_UNLOCK(lock); while (current != NULL) { deleted = current; current = current->next; free_hash_entry(deleted); } } if (keep_count > 0 || remove_count > 0) { char keep_buff[256]; char remove_buff[256]; if (keep_count > 0) { sprintf(keep_buff, "%"PRId64" sessions persisted", keep_count); } else { *keep_buff = '\0'; } if (remove_count > 0) { sprintf(remove_buff, "%"PRId64" sessions cleared", remove_count); } else { *remove_buff = '\0'; } if (keep_count > 0 && remove_count > 0) { logInfo("file: "__FILE__", line: %d, " "%s, %s", __LINE__, keep_buff, remove_buff); } else { logInfo("file: "__FILE__", line: %d, " "%s", __LINE__, (*keep_buff != '\0') ? keep_buff : remove_buff); } } } ================================================ FILE: src/auth/common/server_session.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_SERVER_SESSION_H #define _FCFS_AUTH_SERVER_SESSION_H #include "fastcommon/ini_file_reader.h" #include "auth_types.h" #define FCFS_AUTH_SESSION_OP_TYPE_CREATE 'C' #define FCFS_AUTH_SESSION_OP_TYPE_REMOVE 'R' typedef struct server_session_config { int shared_allocator_count; int shared_lock_count; int hashtable_capacity; int validate_within_fresh_seconds; string_t validate_key; string_t validate_key_filename; unsigned char validate_key_buff[FCFS_AUTH_PASSWD_LEN]; } ServerSessionConfig; typedef union server_session_id_info { uint64_t id; struct { unsigned int ts; bool publish : 1; bool persistent : 1; unsigned int rn : 14; unsigned int sn : 16; } fields; } ServerSessionIdInfo; typedef struct session_synced_fields { struct { int64_t id; int64_t priv; } user; struct { int64_t id; bool available; FCFSAuthSPoolPriviledges privs; } pool; } SessionSyncedFields; typedef struct server_session_entry { ServerSessionIdInfo id_info; void *fields; } ServerSessionEntry; typedef struct server_session_hash_entry { ServerSessionEntry entry; struct fast_mblock_man *allocator; //for free struct server_session_hash_entry *next; //for hashtable } ServerSessionHashEntry; typedef void (*server_session_callback)(ServerSessionEntry *session); typedef struct server_session_callbacks { server_session_callback add_func; server_session_callback del_func; } ServerSessionCallbacks; #define FCFS_AUTH_SERVER_SESSION_BY_FIELDS(fields) \ &((ServerSessionHashEntry *)fields - 1)->entry #ifdef __cplusplus extern "C" { #endif extern ServerSessionConfig g_server_session_cfg; #define server_session_init(ini_ctx) \ server_session_init_ex(ini_ctx, sizeof(SessionSyncedFields), NULL) #define server_session_cfg_to_string(buff, size) \ server_session_cfg_to_string_ex(buff, size, false) #define server_session_add(entry, publish) \ server_session_add_ex(entry, publish, false) int server_session_init_ex(IniFullContext *ini_ctx, const int fields_size, ServerSessionCallbacks *callbacks); void server_session_cfg_to_string_ex(char *buff, const int size, const bool output_all); ServerSessionEntry *server_session_add_ex(const ServerSessionEntry *entry, const bool publish, const bool persistent); int server_session_get_fields(const uint64_t session_id, void *fields); int server_session_user_priv_granted(const uint64_t session_id, const int64_t the_priv); int server_session_fstore_priv_granted(const uint64_t session_id, const int the_priv); int server_session_fdir_priv_granted(const uint64_t session_id, const int the_priv); int server_session_delete(const uint64_t session_id); void server_session_clear(); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/conf/auth.conf ================================================ # enable / disable authentication # default value is false auth_enabled = false # the username for login # default value is admin username = admin # the secret key filename of the user # variable ${username} will be replaced with the value of username # default value is keys/${username}.key secret_key_filename = keys/${username}.key # the config filename of auth client client_config_filename = client.conf ================================================ FILE: src/auth/conf/client.conf ================================================ # the base path to store log files base_path = /opt/fastcfs/auth # config the cluster servers cluster_config_filename = cluster.conf ================================================ FILE: src/auth/conf/cluster.conf ================================================ [master-election] # the quorum for master election # set quorum to majority to avoid brain-split # value list: ## any: no requirement ## majority: more than half ## auto: set to majority when the number of nodes is odd, ## otherwise set to any # default value is auto quorum = auto # if enable vote node when the number of servers is even # the default value is false vote_node_enabled = false # the cluster config filename of the vote node # this parameter is valid when vote_node_enabled is true vote_node_cluster_filename = ../vote/cluster.conf [group-cluster] # the default cluster port port = 31011 [group-service] # the default service port port = 31012 ## Important:server group mark, don't modify this line. # config a server # section format: [server-$id] # server id is a 32 bits natural number (1, 2, 3 etc.), [server-1] # format: host[:port] # host can be an IP address or a hostname, IPv6 is supported # IP address is recommended # can occur more than once host = 172.16.168.128 ================================================ FILE: src/auth/conf/full/auth.conf ================================================ # enable / disable authentication # default value is false auth_enabled = false # the username for login # default value is admin username = admin # the secret key filename of the user # variable ${username} will be replaced with the value of username # default value is keys/${username}.key secret_key_filename = keys/${username}.key # the config filename of auth client client_config_filename = client.conf ================================================ FILE: src/auth/conf/full/client.conf ================================================ # connect timeout in seconds # default value is 10 # Note: in the intranet network (LAN), 10 seconds is enough. connect_timeout = 10 # network timeout in seconds # default value is 60 network_timeout = 60 # the base path to store log files base_path = /opt/fastcfs/auth # config the cluster servers cluster_config_filename = cluster.conf #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level = info ================================================ FILE: src/auth/conf/full/cluster.conf ================================================ [master-election] # the quorum for master election # set quorum to majority to avoid brain-split # value list: ## any: no requirement ## majority: more than half ## auto: set to majority when the number of nodes is odd, ## otherwise set to any # default value is auto quorum = auto # the timeout to determinate master lost # the default value is 3 seconds master_lost_timeout = 3 # the max wait time for master election # this parameter is for the master restart # the default value is 5 seconds max_wait_time = 5 # if enable vote node when the number of servers is even # the default value is false vote_node_enabled = false # the cluster config filename of the vote node # this parameter is valid when vote_node_enabled is true vote_node_cluster_filename = ../vote/cluster.conf [group-cluster] # the default cluster port port = 31011 [group-service] # the default service port port = 31012 ## Important:server group mark, don't modify this line. # config a server # section format: [server-$id] # server id is a 32 bits natural number (1, 2, 3 etc.), [server-1] # format: host[:port] # host can be an IP address or a hostname, IPv6 is supported # IP address is recommended # can occur more than once host = 172.16.168.128 ================================================ FILE: src/auth/conf/full/server.conf ================================================ # connect timeout in seconds # default value is 10 # Note: in the intranet network (LAN), 10 seconds is enough. # do NOT set to 1 second because of time accuracy! connect_timeout = 10 # network timeout in seconds for send and recv # default value is 60 network_timeout = 60 # the base path to store log files # this path must be exist base_path = /opt/fastcfs/auth # max concurrent connections this server support # you should set this parameter larger, eg. 10240 # default value is 256 max_connections = 10240 # the min network buff size # default value 8KB min_buff_size = 64KB # the max network buff size # default value 128KB max_buff_size = 256KB # max pkg size # default value is 16K max_pkg_size = 256KB # if io_uring send with zero copy when Linux kernel version >= 5.19 # set to true when the NIC support zero copy # can be overwritten in sections: [cluster] and [service] # default value is true use_send_zc = true # TCP quick ack for Linux (setsockopt with TCP_QUICKACK option) # default value is true tcp_quick_ack = true # config the cluster servers and groups cluster_config_filename = cluster.conf # the session config filename session_config_filename = session.conf #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level = info #unix group name to run this program, #not set (empty) means run by the group of current user run_by_group= #unix username to run this program, #not set (empty) means run by current user run_by_user = # thread stack size, should >= 64KB # default value is 256KB thread_stack_size = 256KB # NOTE: following global parameters for error log and slow log # which can be overwritten in [error-log] and [slow-log] sections # sync log buff to disk every interval seconds # default value is 1 seconds sync_log_buff_interval = 1 # if rotate the log file every day # set to true for rotate the log file anyway at the rotate time # default value is true log_file_rotate_everyday = true # the time to rotate the log file, format is Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # valid only when log_file_rotate_everyday is true # default value is 00:00 log_file_rotate_time = 00:00 # if compress the old log file by gzip # default value is false log_file_compress_old = false # compress the log file days before # default value is 1 log_file_compress_days_before = 7 # rotate the log file when the log file exceeds this size # 0 means never rotates log file by log file size # default value is 0 log_file_rotate_on_size = 0 # keep days of the log files # 0 means do not delete the old log files # default value is 15 log_file_keep_days = 15 # the time to delete the old log files, format is Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # valid only when log_file_keep_days > 0 # default value is 01:30 log_file_delete_old_time = 01:30 [error-log] # global log parameters can be overwritten here for error log [slow-log] # global log parameters can be overwritten here for slow log # if enable the slow log # default value is false enabled = true # the filename prefix of the slow log # default value is slow filename_prefix = slow # log the request to the slow log whose response time exceeds this parameter # default value is 100ms log_slower_than_ms = 50 [cluster] # bind an address of this host # empty for bind all addresses of this host bind_addr = # the listen port port = 31011 # the accept thread count # default value is 1 which is recommended accept_threads = 1 # the network thread count # these threads deal network io # dispatched by the incoming socket fd # default value is 4 work_threads = 4 # if io_uring send with zero copy when Linux kernel version >= 5.19 # set to true when the NIC support zero copy # default value set by global config use_send_zc = true [service] bind_addr = port = 31012 accept_threads = 1 work_threads = 4 use_send_zc = true [admin-generate] # the generate mode for creating the admin user when not exist, value list: ## first: the new created user will be the first user (no other users) ## always: always create this admin user when not exist # default value is first mode = first # the admin user to auto generate # you should set this parameter to what you want before authd first run # default value is admin username = admin # the filename of the secret key which can be absolute path or relative path # the relative path based on the path of this config file, # such as /etc/fastcfs/auth/server.conf # ${username} will be replaced with the value of above "username" parameter # Note: this secret key file will be auto generated, do NOT modify it! secret_key_filename = keys/${username}.key [pool-generate] # the initial/first value for auto increment id referred as ${auto_id} # eg. set 100001 to generate six digital id (the first generated id is 100001) # default value is 1 auto_id_initial = 1 # the pool name template can contain ${auto_id} for creating pool, # such as pool-${auto_id} # default value is ${auto_id} pool_name_template = ${auto_id} [FastDIR] # config the FastDIR client config filename client_config_filename = ../fdir/client.conf # the interval to refresh/fetch the usage (used bytes) of the storage pools # unit: seconds # default value is 3 pool_usage_refresh_interval = 3 ================================================ FILE: src/auth/conf/full/session.conf ================================================ # the capacity (bucket count) of session hashtable # default value is 10949 hashtable_capacity = 10949 # the shared allocators for hashtabe entry # NO more than the CPU cores is recommended # default value is 11 shared_allocator_count = 11 # the shared locks for session hashtable # default value is 163 shared_lock_count = 163 # the fresh seconds/threshold to validate session from the auth server # this parameter solves the problem of the new created session # synchronization delay # # if the session does not exist in local and this session generated # within X seconds, then request the auth server to validate the session # # default value is 5 validate_within_fresh_seconds = 5 # the secret key filename for session validation # the file content is 32 bytes hex characters validate_key_filename = keys/session_validate.key ================================================ FILE: src/auth/conf/keys/session_validate.key ================================================ dd7702749e369c7e8139d6ca3bf443ce ================================================ FILE: src/auth/conf/server.conf ================================================ # the base path to store log files # this path must be exist base_path = /opt/fastcfs/auth # max concurrent connections this server support # you should set this parameter larger, eg. 10240 # default value is 256 max_connections = 10240 # config the cluster servers and groups cluster_config_filename = cluster.conf # the session config filename session_config_filename = session.conf [cluster] # the listen port port = 31011 # the network thread count # these threads deal network io # dispatched by the incoming socket fd # default value is 4 work_threads = 4 [service] # the listen port port = 31012 # the network thread count # these threads deal network io # dispatched by the incoming socket fd # default value is 4 work_threads = 4 [admin-generate] # the generate mode for creating the admin user when not exist, value list: ## first: the new created user will be the first user (no other users) ## always: always create this admin user when not exist # default value is first mode = first # the admin user to auto generate # you should set this parameter to what you want before authd first run # default value is admin username = admin # the filename of the secret key which can be absolute path or relative path # the relative path based on the path of this config file, # such as /etc/fastcfs/auth/server.conf # ${username} will be replaced with the value of above "username" parameter # Note: this secret key file will be auto generated, do NOT modify it! secret_key_filename = keys/${username}.key [FastDIR] # config the FastDIR client config filename client_config_filename = ../fdir/client.conf ================================================ FILE: src/auth/conf/session.conf ================================================ # the secret key filename for session validation # the file content is 32 bytes hex characters validate_key_filename = keys/session_validate.key ================================================ FILE: src/auth/server/Makefile.in ================================================ .SUFFIXES: .c .o .lo COMPILE = $(CC) $(CFLAGS) INC_PATH = -I.. -I../common -I../../include LIB_PATH = $(LIBS) -lfdirclient -lfastcommon -lserverframe -lfcfsauthclient -lfcfsvoteclient TARGET_PATH = $(TARGET_PREFIX)/bin STATIC_OBJS = ../common/auth_global.o ../common/auth_proto.o \ ../common/auth_func.o ../common/server_session.o \ db/dao/dao.o db/dao/func.o db/dao/user.o \ db/dao/storage_pool.o db/dao/granted_pool.o \ db/auth_db.o db/pool_usage_updater.o server_global.o \ server_func.o common_handler.o service_handler.o \ cluster_handler.o session_subscribe.o cluster_relationship.o \ cluster_info.o ALL_PRGS = fcfs_authd all: $(STATIC_OBJS) $(ALL_PRGS) $(ALL_PRGS): $(STATIC_OBJS) .o: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c.o: $(COMPILE) -c -o $@ $< $(INC_PATH) install: mkdir -p $(TARGET_PATH) cp -f $(ALL_PRGS) $(TARGET_PATH) clean: rm -f $(STATIC_OBJS) $(ALL_PRGS) ================================================ FILE: src/auth/server/cluster_handler.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //cluster_handler.c #include #include #include #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/shared_func.h" #include "fastcommon/pthread_func.h" #include "fastcommon/sched_thread.h" #include "fastcommon/ioevent_loop.h" #include "sf/sf_util.h" #include "sf/sf_func.h" #include "sf/sf_nio.h" #include "sf/sf_global.h" #include "common/auth_proto.h" #include "db/auth_db.h" #include "server_global.h" #include "server_func.h" #include "session_subscribe.h" #include "cluster_info.h" #include "cluster_relationship.h" #include "common_handler.h" #include "cluster_handler.h" int cluster_handler_init() { return 0; } int cluster_handler_destroy() { return 0; } int cluster_recv_timeout_callback(struct fast_task_info *task) { if (SERVER_TASK_TYPE == AUTH_SERVER_TASK_TYPE_RELATIONSHIP && CLUSTER_PEER != NULL) { logWarning("file: "__FILE__", line: %d, " "cluster client ip: %s, server id: %d, recv timeout", __LINE__, task->client_ip, CLUSTER_PEER->server->id); return ETIMEDOUT; } else if (SERVER_TASK_TYPE == AUTH_SERVER_TASK_TYPE_SUBSCRIBE && SESSION_SUBSCRIBER != NULL) { logWarning("file: "__FILE__", line: %d, " "subscribe client ip: %s, recv timeout", __LINE__, task->client_ip); return ETIMEDOUT; } return 0; } static void session_subscriber_cleanup(AuthServerContext *server_ctx, ServerSessionSubscriber *subscriber) { session_subscribe_unregister(subscriber); if (__sync_bool_compare_and_swap(&subscriber->nio.in_queue, 1, 0)) { locked_list_del(&subscriber->nio.dlink, &server_ctx->cluster.subscribers); } session_subscribe_release(subscriber); } void cluster_task_finish_cleanup(struct fast_task_info *task) { char formatted_ip[FORMATTED_IP_SIZE]; switch (SERVER_TASK_TYPE) { case AUTH_SERVER_TASK_TYPE_RELATIONSHIP: if (CLUSTER_PEER != NULL) { CLUSTER_PEER->is_online = false; cluster_relationship_add_to_inactive_sarray(CLUSTER_PEER); CLUSTER_PEER = NULL; } else { logError("file: "__FILE__", line: %d, " "mistake happen! task: %p, SERVER_TASK_TYPE: %d, " "CLUSTER_PEER is NULL", __LINE__, task, SERVER_TASK_TYPE); } SERVER_TASK_TYPE = SF_SERVER_TASK_TYPE_NONE; break; case AUTH_SERVER_TASK_TYPE_SUBSCRIBE: if (SESSION_SUBSCRIBER != NULL) { session_subscriber_cleanup(SERVER_CTX, SESSION_SUBSCRIBER); SESSION_SUBSCRIBER = NULL; } format_ip_address(task->client_ip, formatted_ip); logInfo("file: "__FILE__", line: %d, " "session subscriber %s:%u offline", __LINE__, formatted_ip, task->port); SERVER_TASK_TYPE = SF_SERVER_TASK_TYPE_NONE; break; default: break; } sf_task_finish_clean_up(task); } void cluster_subscriber_queue_push_ex(ServerSessionSubscriber *subscriber, const bool notify) { AuthServerContext *server_ctx; if (__sync_bool_compare_and_swap(&subscriber->nio.in_queue, 0, 1)) { server_ctx = (AuthServerContext *)subscriber-> nio.task->thread_data->arg; locked_list_add_tail(&subscriber->nio.dlink, &server_ctx->cluster.subscribers); if (notify) { ioevent_notify_thread(subscriber->nio.task->thread_data); } } } static inline ServerSessionSubscriber *cluster_subscriber_queue_pop( AuthServerContext *server_context) { ServerSessionSubscriber *subscriber; PTHREAD_MUTEX_LOCK(&server_context->cluster.subscribers.lock); subscriber = fc_list_first_entry(&server_context->cluster.subscribers.head, ServerSessionSubscriber, nio.dlink); if (subscriber != NULL) { fc_list_del_init(&subscriber->nio.dlink); __sync_bool_compare_and_swap(&subscriber->nio.in_queue, 1, 0); } PTHREAD_MUTEX_UNLOCK(&server_context->cluster.subscribers.lock); return subscriber; } static int cluster_deal_session_subscribe(struct fast_task_info *task) { FCFSAuthProtoSessionSubscribeReq *req; const DBUserInfo *dbuser; char formatted_ip[FORMATTED_IP_SIZE]; string_t username; string_t passwd; int result; if ((result=server_check_min_body_length(sizeof( FCFSAuthProtoSessionSubscribeReq) + 1)) != 0) { return result; } req = (FCFSAuthProtoSessionSubscribeReq *)REQUEST.body; FC_SET_STRING_EX(username, req->up_pair.username.str, req->up_pair.username.len); FC_SET_STRING_EX(passwd, req->up_pair.passwd, FCFS_AUTH_PASSWD_LEN); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoSessionSubscribeReq) + username.len)) != 0) { return result; } if (SERVER_TASK_TYPE != SF_SERVER_TASK_TYPE_NONE) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "task type: %d != %d", SERVER_TASK_TYPE, SF_SERVER_TASK_TYPE_NONE); return EEXIST; } if (!((dbuser=adb_user_get(SERVER_CTX, &username)) != NULL && fc_string_equal(&dbuser->user.passwd, &passwd))) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "user login fail, username or password not correct"); return EPERM; } if ((dbuser->user.priv & FCFS_AUTH_USER_PRIV_SUBSCRIBE_SESSION) == 0) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "username: %.*s, permission denied for session " "subscription", username.len, username.str); return EPERM; } if ((SESSION_SUBSCRIBER=session_subscribe_alloc()) == NULL) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "session subscribe register fail"); return ENOMEM; } SESSION_SUBSCRIBER->nio.task = task; session_subscribe_register(SESSION_SUBSCRIBER); if (!fc_queue_empty(&SESSION_SUBSCRIBER->queue)) { cluster_subscriber_queue_push_ex(SESSION_SUBSCRIBER, false); } format_ip_address(task->client_ip, formatted_ip); logInfo("file: "__FILE__", line: %d, " "session subscriber %s:%u joined", __LINE__, formatted_ip, task->port); SERVER_TASK_TYPE = AUTH_SERVER_TASK_TYPE_SUBSCRIBE; RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_RESP; return 0; } static int session_validate(const int64_t session_id, const FCFSAuthValidatePriviledgeType priv_type, const int64_t pool_id, const int64_t priv_required) { int result; int64_t session_priv; ServerSessionFields fields; if ((result=server_session_get_fields(session_id, &fields)) != 0) { return result; } switch (priv_type) { case fcfs_auth_validate_priv_type_user: session_priv = (fields.dbuser != NULL) ? fields.dbuser-> user.priv : (FCFS_AUTH_USER_PRIV_MONITOR_CLUSTER | FCFS_AUTH_USER_PRIV_SUBSCRIBE_SESSION); break; case fcfs_auth_validate_priv_type_pool_fdir: /* //TODO if (fields.dbpool != NULL) { if (fields.dbpool->pool.id != pool_id) { return EACCES; } } */ session_priv = fields.pool_privs.fdir; break; case fcfs_auth_validate_priv_type_pool_fstore: session_priv = fields.pool_privs.fstore; break; default: session_priv = 0; break; } /* logInfo("session_id: %"PRId64", session_priv: %"PRId64", " "priv_required: %"PRId64", check result: %d", session_id, session_priv, priv_required, ((session_priv & priv_required) == priv_required) ? 0 : EPERM); */ return ((session_priv & priv_required) == priv_required) ? 0 : EPERM; } static int cluster_deal_session_validate(struct fast_task_info *task) { FCFSAuthProtoSessionValidateReq *req; FCFSAuthProtoSessionValidateResp *resp; int64_t session_id; int64_t pool_id; string_t validate_key; int64_t priv_required; FCFSAuthValidatePriviledgeType priv_type; int result; if ((result=server_expect_body_length(sizeof(*req))) != 0) { return result; } req = (FCFSAuthProtoSessionValidateReq *)REQUEST.body; session_id = buff2long(req->session_id); FC_SET_STRING_EX(validate_key, req->validate_key, FCFS_AUTH_PASSWD_LEN); priv_type = req->priv_type; pool_id = buff2long(req->pool_id); priv_required = buff2long(req->priv_required); if (!fc_string_equal(&validate_key, &g_server_session_cfg.validate_key)) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "session validate key not correct"); return EACCES; } if ((result=session_validate(session_id, priv_type, pool_id, priv_required)) == SF_SESSION_ERROR_NOT_EXIST) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "session id: %"PRId64" not exist", session_id); TASK_CTX.common.log_level = LOG_WARNING; return result; } resp = (FCFSAuthProtoSessionValidateResp *)SF_PROTO_SEND_BODY(task); int2buff(result, resp->result); RESPONSE.header.body_len = sizeof(*resp); RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_RESP; TASK_ARG->context.common.response_done = true; return 0; } static int cluster_check_config_sign(struct fast_task_info *task, const int server_id, const char *config_sign) { if (memcmp(config_sign, CLUSTER_CONFIG_SIGN_BUF, SF_CLUSTER_CONFIG_SIGN_LEN) != 0) { char peer_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1]; char my_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1]; bin2hex(config_sign, SF_CLUSTER_CONFIG_SIGN_LEN, peer_hex); bin2hex((const char *)CLUSTER_CONFIG_SIGN_BUF, SF_CLUSTER_CONFIG_SIGN_LEN, my_hex); RESPONSE.error.length = sprintf(RESPONSE.error.message, "server #%d 's cluster config md5: %s != mine: %s", server_id, peer_hex, my_hex); return EFAULT; } return 0; } static int cluster_deal_get_server_status(struct fast_task_info *task) { int result; int server_id; FCFSAuthProtoGetServerStatusReq *req; FCFSAuthProtoGetServerStatusResp *resp; if ((result=server_expect_body_length(sizeof( FCFSAuthProtoGetServerStatusReq))) != 0) { return result; } req = (FCFSAuthProtoGetServerStatusReq *)REQUEST.body; server_id = buff2int(req->server_id); if ((result=cluster_check_config_sign(task, server_id, req->config_sign)) != 0) { return result; } resp = (FCFSAuthProtoGetServerStatusResp *)SF_PROTO_SEND_BODY(task); resp->is_master = MYSELF_IS_MASTER; int2buff(CLUSTER_MY_SERVER_ID, resp->server_id); RESPONSE.header.body_len = sizeof(FCFSAuthProtoGetServerStatusResp); RESPONSE.header.cmd = FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_RESP; TASK_CTX.common.response_done = true; return 0; } static int cluster_deal_join_master(struct fast_task_info *task) { int result; int server_id; FCFSAuthProtoJoinMasterReq *req; FCFSAuthClusterServerInfo *peer; if ((result=server_expect_body_length(sizeof( FCFSAuthProtoJoinMasterReq))) != 0) { return result; } req = (FCFSAuthProtoJoinMasterReq *)REQUEST.body; server_id = buff2int(req->server_id); peer = fcfs_auth_get_server_by_id(server_id); if (peer == NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "peer server id: %d not exist", server_id); return ENOENT; } if ((result=cluster_check_config_sign(task, server_id, req->config_sign)) != 0) { return result; } if (CLUSTER_MYSELF_PTR != CLUSTER_MASTER_ATOM_PTR && CLUSTER_MYSELF_PTR != CLUSTER_NEXT_MASTER) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am not master"); return SF_RETRIABLE_ERROR_NOT_MASTER; } if (peer == CLUSTER_MYSELF_PTR) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "can't join self"); return EINVAL; } if (CLUSTER_PEER != NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "peer server id: %d already joined", server_id); return EEXIST; } SERVER_TASK_TYPE = AUTH_SERVER_TASK_TYPE_RELATIONSHIP; CLUSTER_PEER = peer; CLUSTER_PEER->is_online = true; cluster_relationship_remove_from_inactive_sarray(peer); return 0; } static int cluster_deal_ping_master(struct fast_task_info *task) { int result; if ((result=server_expect_body_length(0)) != 0) { return result; } if (CLUSTER_PEER == NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "please join first"); return EINVAL; } if (CLUSTER_MYSELF_PTR != CLUSTER_MASTER_ATOM_PTR && CLUSTER_MYSELF_PTR != CLUSTER_NEXT_MASTER) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am not master"); return SF_RETRIABLE_ERROR_NOT_MASTER; } RESPONSE.header.cmd = FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_RESP; return 0; } static int cluster_deal_next_master(struct fast_task_info *task) { int result; int master_id; FCFSAuthClusterServerInfo *master; if ((result=server_expect_body_length(4)) != 0) { return result; } if (CLUSTER_MYSELF_PTR == CLUSTER_MASTER_ATOM_PTR) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am already master"); cluster_relationship_trigger_reselect_master(); return EEXIST; } master_id = buff2int(REQUEST.body); master = fcfs_auth_get_server_by_id(master_id); if (master == NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "master server id: %d not exist", master_id); return ENOENT; } if (REQUEST.header.cmd == FCFS_AUTH_CLUSTER_PROTO_PRE_SET_NEXT_MASTER) { return cluster_relationship_pre_set_master(master); } else { return cluster_relationship_commit_master(master); } } static int cluster_process(struct fast_task_info *task) { int result; int cmd; cmd = REQUEST.header.cmd; if (!MYSELF_IS_MASTER) { if (cmd == FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_REQ) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am not master"); return SF_RETRIABLE_ERROR_NOT_MASTER; } if (cmd == FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_REQ) { if (CLUSTER_MYSELF_PTR != CLUSTER_NEXT_MASTER) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am not the next master"); return SF_RETRIABLE_ERROR_NOT_MASTER; } } } switch (cmd) { case SF_PROTO_ACTIVE_TEST_REQ: if (SERVER_TASK_TYPE == AUTH_SERVER_TASK_TYPE_SUBSCRIBE && !MYSELF_IS_MASTER) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am not master"); return SF_RETRIABLE_ERROR_NOT_MASTER; } RESPONSE.header.cmd = SF_PROTO_ACTIVE_TEST_RESP; return sf_proto_deal_active_test(task, &REQUEST, &RESPONSE); case FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_REQ: return cluster_deal_session_subscribe(task); case FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_REQ: return cluster_deal_session_validate(task); case FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_REQ: return cluster_deal_get_server_status(task); case FCFS_AUTH_CLUSTER_PROTO_PRE_SET_NEXT_MASTER: case FCFS_AUTH_CLUSTER_PROTO_COMMIT_NEXT_MASTER: return cluster_deal_next_master(task); case FCFS_AUTH_CLUSTER_PROTO_JOIN_MASTER: return cluster_deal_join_master(task); case FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_REQ: return cluster_deal_ping_master(task); case FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ: if ((result=fcfs_auth_deal_get_master(task, CLUSTER_GROUP_INDEX)) == 0) { RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_GET_MASTER_RESP; } return result; case SF_SERVICE_PROTO_GET_LEADER_REQ: if ((result=fcfs_auth_deal_get_master(task, CLUSTER_GROUP_INDEX)) == 0) { RESPONSE.header.cmd = SF_SERVICE_PROTO_GET_LEADER_RESP; } return result; default: RESPONSE.error.length = sprintf(RESPONSE.error.message, "unkown cmd: %d", REQUEST.header.cmd); return -EINVAL; } } int cluster_deal_task(struct fast_task_info *task, const int stage) { int result; if (stage == SF_NIO_STAGE_CONTINUE) { if (task->continue_callback != NULL) { result = task->continue_callback(task); } else { result = RESPONSE_STATUS; if (result == TASK_STATUS_CONTINUE) { logError("file: "__FILE__", line: %d, " "unexpect status: %d", __LINE__, result); result = EBUSY; } } } else { sf_proto_init_task_context(task, &TASK_CTX.common); result = cluster_process(task); } if (result == TASK_STATUS_CONTINUE) { return 0; } else { RESPONSE_STATUS = result; return sf_proto_deal_task_done(task, "cluster", &TASK_CTX.common); } } static int cluster_deal_queue(AuthServerContext *server_context, ServerSessionSubscriber *subscriber) { struct fast_task_info *task; struct fc_queue_info qinfo; ServerSessionSubscribeEntry *previous; ServerSessionSubscribeEntry *entry; FCFSAuthProtoHeader *proto_header; FCFSAuthProtoSessionPushRespBodyHeader *body_header; FCFSAuthProtoSessionPushRespBodyPart *body_part; char *p; char *end; int count; int result; task = subscriber->nio.task; if (ioevent_is_canceled(task) || !sf_nio_task_send_done( subscriber->nio.task)) { return EAGAIN; } fc_queue_try_pop_to_queue(&subscriber->queue, &qinfo); if (qinfo.head == NULL) { return 0; } previous = NULL; entry = (ServerSessionSubscribeEntry *)qinfo.head; proto_header = (FCFSAuthProtoHeader *)task->send.ptr->data; body_header = (FCFSAuthProtoSessionPushRespBodyHeader *) (proto_header + 1); p = (char *)(body_header + 1); end = SF_SEND_BUFF_END(task); count = 0; while (entry != NULL) { body_part = (FCFSAuthProtoSessionPushRespBodyPart *)p; if (p + sizeof(FCFSAuthProtoSessionPushRespBodyPart) + sizeof(FCFSAuthProtoSessionPushEntry) > end) { break; } long2buff(entry->session_id, body_part->session_id); body_part->operation = entry->operation; if (entry->operation == FCFS_AUTH_SESSION_OP_TYPE_CREATE) { long2buff(entry->fields.user.id, body_part->entry->user.id); long2buff(entry->fields.user.priv, body_part->entry->user.priv); long2buff(entry->fields.pool.id, body_part->entry->pool.id); body_part->entry->pool.available = entry->fields.pool.available; int2buff(entry->fields.pool.privs.fdir, body_part->entry->pool.privs.fdir); int2buff(entry->fields.pool.privs.fstore, body_part->entry->pool.privs.fstore); p += sizeof(FCFSAuthProtoSessionPushRespBodyPart) + sizeof(FCFSAuthProtoSessionPushEntry); } else { p += sizeof(FCFSAuthProtoSessionPushRespBodyPart); } ++count; previous = entry; entry = entry->next; } if (entry == NULL) { result = 0; } else { struct fc_queue_info remain_qinfo; previous->next = NULL; remain_qinfo.head = entry; remain_qinfo.tail = qinfo.tail; fc_queue_push_queue_to_head_silence( &subscriber->queue, &remain_qinfo); result = EAGAIN; } session_subscribe_free_entries(qinfo.head); int2buff(count, body_header->count); task->send.ptr->length = p - task->send.ptr->data; SF_PROTO_SET_HEADER(proto_header, FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_REQ, task->send.ptr->length - sizeof(FCFSAuthProtoHeader)); sf_send_add_event(task); return result; } int cluster_thread_loop_callback(struct nio_thread_data *thread_data) { AuthServerContext *server_context; ServerSessionSubscriber *subscriber; int count; int i; server_context = (AuthServerContext *)thread_data->arg; count = locked_list_count(&server_context->cluster.subscribers); for (i=0; icluster.subscribers) != 0) { return NULL; } return server_context; } ================================================ FILE: src/auth/server/cluster_handler.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //cluster_handler.h #ifndef FCFS_AUTH_CLUSTER_HANDLER_H #define FCFS_AUTH_CLUSTER_HANDLER_H #include #include #include #include "fastcommon/fast_task_queue.h" #include "server_types.h" #ifdef __cplusplus extern "C" { #endif int cluster_handler_init(); int cluster_handler_destroy(); int cluster_deal_task(struct fast_task_info *task, const int stage); void cluster_task_finish_cleanup(struct fast_task_info *task); void *cluster_alloc_thread_extra_data(const int thread_index); int cluster_thread_loop_callback(struct nio_thread_data *thread_data); int cluster_recv_timeout_callback(struct fast_task_info *task); void cluster_subscriber_queue_push_ex(ServerSessionSubscriber *subscriber, const bool notify); #define cluster_subscriber_queue_push(subscriber) \ cluster_subscriber_queue_push_ex(subscriber, true) #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/cluster_info.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/shared_func.h" #include "fastcommon/sched_thread.h" #include "fastcommon/local_ip_func.h" #include "server_global.h" #include "cluster_info.h" static int init_cluster_server_array() { int bytes; FCFSAuthClusterServerInfo *cs; FCServerInfo *server; FCServerInfo *end; bytes = sizeof(FCFSAuthClusterServerInfo) * FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG); if ((CLUSTER_SERVER_ARRAY.servers=(FCFSAuthClusterServerInfo *) fc_malloc(bytes)) == NULL) { return ENOMEM; } memset(CLUSTER_SERVER_ARRAY.servers, 0, bytes); end = FC_SID_SERVERS(CLUSTER_SERVER_CONFIG) + FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG); for (server=FC_SID_SERVERS(CLUSTER_SERVER_CONFIG), cs=CLUSTER_SERVER_ARRAY.servers; serverserver = server; } CLUSTER_SERVER_ARRAY.count = FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG); return 0; } static int find_myself_in_cluster_config(const char *filename) { const char *local_ip; struct { const char *ip_addr; int port; } found; FCServerInfo *server; FCFSAuthClusterServerInfo *myself; SFNetworkHandler *service_handler; SFNetworkHandler *cluster_handler; char formatted_found_ip[FORMATTED_IP_SIZE]; char formatted_local_ip[FORMATTED_IP_SIZE]; int ports[4]; int count; int i; service_handler = SERVICE_SF_CTX.handlers[SF_IPV4_ADDRESS_FAMILY_INDEX]. handlers + SF_SOCKET_NETWORK_HANDLER_INDEX; cluster_handler = CLUSTER_SF_CTX.handlers[SF_IPV4_ADDRESS_FAMILY_INDEX]. handlers + SF_SOCKET_NETWORK_HANDLER_INDEX; count = 0; ports[count++] = service_handler->inner.port; if (service_handler->outer.port != service_handler->inner.port) { ports[count++] = service_handler->outer.port; } ports[count++] = cluster_handler->inner.port; if (cluster_handler->outer.port != cluster_handler->inner.port) { ports[count++] = cluster_handler->outer.port; } found.ip_addr = NULL; found.port = 0; local_ip = get_first_local_ip(); while (local_ip != NULL) { for (i=0; iserver->id); return EEXIST; } found.ip_addr = local_ip; found.port = ports[i]; } } local_ip = get_next_local_ip(local_ip); } if (CLUSTER_MYSELF_PTR == NULL) { logError("file: "__FILE__", line: %d, " "cluster config file: %s, can't find myself " "by my local ip and listen port", __LINE__, filename); return ENOENT; } return 0; } FCFSAuthClusterServerInfo *fcfs_auth_get_server_by_id(const int server_id) { FCServerInfo *server; server = fc_server_get_by_id(&CLUSTER_SERVER_CONFIG, server_id); if (server == NULL) { return NULL; } return CLUSTER_SERVER_ARRAY.servers + (server - FC_SID_SERVERS(CLUSTER_SERVER_CONFIG)); } int cluster_info_init(const char *cluster_config_filename) { int result; if ((result=init_cluster_server_array()) != 0) { return result; } if ((result=find_myself_in_cluster_config(cluster_config_filename)) != 0) { return result; } return 0; } ================================================ FILE: src/auth/server/cluster_info.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //cluster_info.h #ifndef _CLUSTER_INFO_H_ #define _CLUSTER_INFO_H_ #include #include #include "server_global.h" #ifdef __cplusplus extern "C" { #endif int cluster_info_init(const char *cluster_config_filename); int cluster_info_destroy(); FCFSAuthClusterServerInfo *fcfs_auth_get_server_by_id(const int server_id); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/cluster_relationship.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/shared_func.h" #include "fastcommon/pthread_func.h" #include "fastcommon/sched_thread.h" #include "sf/sf_configs.h" #include "sf/sf_service.h" #include "sf/sf_func.h" #include "fastcfs/vote/fcfs_vote_client.h" #include "common/auth_proto.h" #include "db/auth_db.h" #include "db/pool_usage_updater.h" #include "server_global.h" #include "session_subscribe.h" #include "cluster_relationship.h" #define NEED_REQUEST_VOTE_NODE(active_count) \ SF_ELECTION_QUORUM_NEED_REQUEST_VOTE_NODE(MASTER_ELECTION_QUORUM, \ VOTE_NODE_ENABLED, CLUSTER_SERVER_ARRAY.count, active_count) #define NEED_CHECK_VOTE_NODE() \ SF_ELECTION_QUORUM_NEED_CHECK_VOTE_NODE(MASTER_ELECTION_QUORUM, \ VOTE_NODE_ENABLED, CLUSTER_SERVER_ARRAY.count) typedef struct fcfs_auth_cluster_server_status { FCFSAuthClusterServerInfo *cs; bool is_master; int server_id; } FCFSAuthClusterServerStatus; typedef struct fcfs_auth_cluster_server_detect_entry { FCFSAuthClusterServerInfo *cs; int next_time; } FCFSAuthClusterServerDetectEntry; typedef struct fcfs_auth_cluster_server_detect_array { FCFSAuthClusterServerDetectEntry *entries; int count; int alloc; pthread_mutex_t lock; } FCFSAuthClusterServerDetectArray; typedef struct fcfs_auth_cluster_relationship_context { FCFSAuthClusterServerDetectArray inactive_server_array; ConnectionInfo vote_connection; } FCFSAuthClusterRelationshipContext; #define INACTIVE_SERVER_ARRAY relationship_ctx.inactive_server_array #define VOTE_CONNECTION relationship_ctx.vote_connection static FCFSAuthClusterRelationshipContext relationship_ctx = { {NULL, 0, 0} }; #define SET_SERVER_DETECT_ENTRY(entry, server) \ do { \ entry->cs = server; \ entry->next_time = g_current_time + 1; \ } while (0) static inline void proto_unpack_server_status( FCFSAuthProtoGetServerStatusResp *resp, FCFSAuthClusterServerStatus *server_status) { server_status->is_master = resp->is_master; server_status->server_id = buff2int(resp->server_id); } static int proto_get_server_status(ConnectionInfo *conn, const int network_timeout, FCFSAuthClusterServerStatus *server_status) { int result; FCFSAuthProtoHeader *header; FCFSAuthProtoGetServerStatusReq *req; FCFSAuthProtoGetServerStatusResp *resp; SFResponseInfo response; char out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoGetServerStatusReq)]; char in_body[sizeof(FCFSAuthProtoGetServerStatusResp)]; char formatted_ip[FORMATTED_IP_SIZE]; header = (FCFSAuthProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_REQ, sizeof(out_buff) - sizeof(FCFSAuthProtoHeader)); req = (FCFSAuthProtoGetServerStatusReq *)(out_buff + sizeof(FCFSAuthProtoHeader)); int2buff(CLUSTER_MY_SERVER_ID, req->server_id); memcpy(req->config_sign, CLUSTER_CONFIG_SIGN_BUF, SF_CLUSTER_CONFIG_SIGN_LEN); response.error.length = 0; if ((result=sf_send_and_check_response_header(conn, out_buff, sizeof(out_buff), &response, network_timeout, FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_RESP)) != 0) { auth_log_network_error(&response, conn, result); return result; } if (response.header.body_len != sizeof(FCFSAuthProtoGetServerStatusResp)) { format_ip_address(conn->ip_addr, formatted_ip); logError("file: "__FILE__", line: %d, " "server %s:%u, recv body length: %d != %d", __LINE__, formatted_ip, conn->port, response.header.body_len, (int) sizeof(FCFSAuthProtoGetServerStatusResp)); return EINVAL; } if ((result=tcprecvdata_nb(conn->sock, in_body, response. header.body_len, network_timeout)) != 0) { format_ip_address(conn->ip_addr, formatted_ip); logError("file: "__FILE__", line: %d, " "recv from server %s:%u fail, " "errno: %d, error info: %s", __LINE__, formatted_ip, conn->port, result, STRERROR(result)); return result; } resp = (FCFSAuthProtoGetServerStatusResp *)in_body; proto_unpack_server_status(resp, server_status); return 0; } static void init_inactive_server_array() { FCFSAuthClusterServerInfo *cs; FCFSAuthClusterServerInfo *end; FCFSAuthClusterServerDetectEntry *entry; PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock); entry = INACTIVE_SERVER_ARRAY.entries; end = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count; for (cs=CLUSTER_SERVER_ARRAY.servers; csserver_id); memcpy(req->config_sign, CLUSTER_CONFIG_SIGN_BUF, SF_CLUSTER_CONFIG_SIGN_LEN); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, sizeof(out_buff), &response, network_timeout, SF_PROTO_ACK)) != 0) { auth_log_network_error(&response, conn, result); } return result; } static int proto_ping_master(ConnectionInfo *conn, const int network_timeout) { FCFSAuthProtoHeader header; SFResponseInfo response; int result; SF_PROTO_SET_HEADER(&header, FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_REQ, 0); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, (char *)&header, sizeof(header), &response, network_timeout, FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_RESP)) != 0) { auth_log_network_error(&response, conn, result); } return result; } static int cluster_cmp_server_status(const void *p1, const void *p2) { FCFSAuthClusterServerStatus *status1; FCFSAuthClusterServerStatus *status2; int sub; status1 = (FCFSAuthClusterServerStatus *)p1; status2 = (FCFSAuthClusterServerStatus *)p2; sub = (int)status1->is_master - (int)status2->is_master; if (sub != 0) { return sub; } return (int)status1->server_id - (int)status2->server_id; } #define cluster_get_server_status(server_status) \ cluster_get_server_status_ex(server_status, true) static int cluster_get_server_status_ex(FCFSAuthClusterServerStatus *server_status, const bool log_connect_error) { const int connect_timeout = 2; const int network_timeout = 2; ConnectionInfo conn; int result; if (server_status->cs == CLUSTER_MYSELF_PTR) { server_status->is_master = MYSELF_IS_MASTER; server_status->server_id = CLUSTER_MY_SERVER_ID; return 0; } else { if ((result=fc_server_make_connection_ex(&CLUSTER_GROUP_ADDRESS_ARRAY( server_status->cs->server), &conn, "fauth", connect_timeout, NULL, log_connect_error)) != 0) { return result; } result = proto_get_server_status(&conn, network_timeout, server_status); conn_pool_disconnect_server(&conn); return result; } } static int do_check_brainsplit(FCFSAuthClusterServerInfo *cs) { int result; const bool log_connect_error = false; FCFSAuthClusterServerStatus server_status; char formatted_ip[FORMATTED_IP_SIZE]; server_status.cs = cs; server_status.is_master = false; server_status.server_id = 0; if ((result=cluster_get_server_status_ex(&server_status, log_connect_error)) != 0) { return result; } if (server_status.is_master) { format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( cs->server), formatted_ip); logWarning("file: "__FILE__", line: %d, " "two masters occurs, anonther master id: %d, %s:%u, " "trigger re-select master ...", __LINE__, cs->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(cs->server)); cluster_relationship_trigger_reselect_master(); return EEXIST; } return 0; } static int cluster_check_brainsplit(int *inactive_count) { FCFSAuthClusterServerDetectEntry *entry; FCFSAuthClusterServerDetectEntry *end; int result; end = INACTIVE_SERVER_ARRAY.entries + *inactive_count; for (entry=INACTIVE_SERVER_ARRAY.entries; entry= INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count) { break; } if (entry->next_time > g_current_time) { continue; } result = do_check_brainsplit(entry->cs); if (result == 0) { //success --(*inactive_count); } else if (result == EEXIST) { //brain-split occurs return result; } entry->next_time = g_current_time + 1; } return 0; } static inline void fill_join_request(FCFSVoteClientJoinRequest *join_request, const bool persistent) { join_request->server_id = CLUSTER_MY_SERVER_ID; join_request->is_leader = (CLUSTER_MYSELF_PTR == CLUSTER_MASTER_ATOM_PTR ? 1 : 0); join_request->group_id = 1; join_request->response_size = sizeof(FCFSAuthProtoGetServerStatusResp); join_request->service_id = FCFS_VOTE_SERVICE_ID_FAUTH; join_request->persistent = persistent; } static inline int vote_node_active_check() { int result; FCFSVoteClientJoinRequest join_request; if (VOTE_CONNECTION.sock < 0) { fill_join_request(&join_request, true); if ((result=fcfs_vote_client_join(&VOTE_CONNECTION, &join_request)) != 0) { return result; } } if ((result=vote_client_proto_active_check(&VOTE_CONNECTION)) != 0) { vote_client_proto_close_connection(&VOTE_CONNECTION); } return result; } static int master_check() { int result; int active_count; int inactive_count; int vote_node_active; if (NEED_CHECK_VOTE_NODE()) { if ((result=vote_node_active_check()) == 0) { vote_node_active = 1; } else { if (result == SF_CLUSTER_ERROR_LEADER_INCONSISTENT) { logWarning("file: "__FILE__", line: %d, " "trigger re-select master because master " "inconsistent with the vote node", __LINE__); cluster_relationship_trigger_reselect_master(); return EBUSY; } vote_node_active = 0; } } else { vote_node_active = 0; } PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock); inactive_count = INACTIVE_SERVER_ARRAY.count; PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock); if (inactive_count > 0) { if ((result=cluster_check_brainsplit(&inactive_count)) != 0) { return result; } active_count = (CLUSTER_SERVER_ARRAY.count - inactive_count) + vote_node_active; if (!sf_election_quorum_check(MASTER_ELECTION_QUORUM, VOTE_NODE_ENABLED, CLUSTER_SERVER_ARRAY.count, active_count)) { logWarning("file: "__FILE__", line: %d, " "trigger re-select master because alive server " "count: %d < half of total server count: %d ...", __LINE__, active_count, CLUSTER_SERVER_ARRAY.count); cluster_relationship_trigger_reselect_master(); return EBUSY; } } return 0; } static int get_vote_server_status(FCFSAuthClusterServerStatus *server_status) { FCFSVoteClientJoinRequest join_request; SFGetServerStatusRequest status_request; FCFSAuthProtoGetServerStatusResp resp; int result; if (VOTE_CONNECTION.sock >= 0) { status_request.servers_sign = CLUSTER_CONFIG_SIGN_BUF; status_request.cluster_sign = NULL; status_request.server_id = CLUSTER_MY_SERVER_ID; status_request.is_leader = (CLUSTER_MYSELF_PTR == CLUSTER_MASTER_ATOM_PTR ? 1 : 0); result = vote_client_proto_get_vote(&VOTE_CONNECTION, &status_request, (char *)&resp, sizeof(resp)); if (result != 0) { vote_client_proto_close_connection(&VOTE_CONNECTION); } } else { fill_join_request(&join_request, false); result = fcfs_vote_client_get_vote(&join_request, CLUSTER_CONFIG_SIGN_BUF, NULL, (char *)&resp, sizeof(resp)); } if (result == 0) { proto_unpack_server_status(&resp, server_status); } return result; } static int notify_vote_next_leader(FCFSAuthClusterServerStatus *server_status, const unsigned char vote_req_cmd) { FCFSVoteClientJoinRequest join_request; fill_join_request(&join_request, false); return fcfs_vote_client_notify_next_leader(&join_request, vote_req_cmd); } static int cluster_get_master(FCFSAuthClusterServerStatus *server_status, const bool log_connect_error, int *active_count) { #define STATUS_ARRAY_FIXED_COUNT 8 FCFSAuthClusterServerInfo *server; FCFSAuthClusterServerInfo *end; FCFSAuthClusterServerStatus *current_status; FCFSAuthClusterServerStatus *cs_status; FCFSAuthClusterServerStatus status_array[STATUS_ARRAY_FIXED_COUNT]; char formatted_ip[FORMATTED_IP_SIZE]; int result; int r; int i; memset(server_status, 0, sizeof(FCFSAuthClusterServerStatus)); if (CLUSTER_SERVER_ARRAY.count < STATUS_ARRAY_FIXED_COUNT) { cs_status = status_array; } else { int bytes; bytes = sizeof(FCFSAuthClusterServerStatus) * CLUSTER_SERVER_ARRAY.count; cs_status = (FCFSAuthClusterServerStatus *)fc_malloc(bytes); if (cs_status == NULL) { return ENOMEM; } } current_status = cs_status; result = 0; end = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count; for (server=CLUSTER_SERVER_ARRAY.servers; servercs = server; r = cluster_get_server_status_ex(current_status, log_connect_error); if (r == 0) { current_status++; } else if (r != ENOENT) { result = r; } } *active_count = current_status - cs_status; if (*active_count == 0) { logError("file: "__FILE__", line: %d, " "get server status fail, " "server count: %d", __LINE__, CLUSTER_SERVER_ARRAY.count); return result == 0 ? ENOENT : result; } if (NEED_REQUEST_VOTE_NODE(*active_count)) { current_status->cs = NULL; if (get_vote_server_status(current_status) == 0) { ++(*active_count); } } qsort(cs_status, *active_count, sizeof(FCFSAuthClusterServerStatus), cluster_cmp_server_status); if (FC_LOG_BY_LEVEL(LOG_DEBUG)) { for (i=0; i<*active_count; i++) { if (cs_status[i].cs == NULL) { logDebug("file: "__FILE__", line: %d, " "%d. status from vote server", __LINE__, i + 1); } else { format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( cs_status[i].cs->server), formatted_ip); logDebug("file: "__FILE__", line: %d, " "server_id: %d, ip and port %s:%u, is_master: %d", __LINE__, cs_status[i].server_id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(cs_status[i].cs->server), cs_status[i].is_master); } } } memcpy(server_status, cs_status + (*active_count - 1), sizeof(FCFSAuthClusterServerStatus)); if (cs_status != status_array) { free(cs_status); } return 0; } static int do_notify_master_changed(FCFSAuthClusterServerInfo *cs, FCFSAuthClusterServerInfo *master, const unsigned char cmd, bool *bConnectFail) { int connect_timeout; char out_buff[sizeof(FCFSAuthProtoHeader) + 4]; ConnectionInfo conn; FCFSAuthProtoHeader *header; SFResponseInfo response; int result; connect_timeout = FC_MIN(CLUSTER_CONNECT_TIMEOUT, 2); if ((result=fc_server_make_connection(&CLUSTER_GROUP_ADDRESS_ARRAY( cs->server), &conn, "fauth", connect_timeout)) != 0) { *bConnectFail = true; return result; } *bConnectFail = false; header = (FCFSAuthProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, cmd, sizeof(out_buff) - sizeof(FCFSAuthProtoHeader)); int2buff(master->server->id, out_buff + sizeof(FCFSAuthProtoHeader)); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(&conn, out_buff, sizeof(out_buff), &response, CLUSTER_NETWORK_TIMEOUT, SF_PROTO_ACK)) != 0) { auth_log_network_error(&response, &conn, result); } conn_pool_disconnect_server(&conn); return result; } int cluster_relationship_pre_set_master(FCFSAuthClusterServerInfo *master) { FCFSAuthClusterServerInfo *next_master; next_master = CLUSTER_NEXT_MASTER; if (next_master == NULL) { CLUSTER_NEXT_MASTER = master; } else if (next_master != master) { logError("file: "__FILE__", line: %d, " "try to set next master id: %d, " "but next master: %d already exist", __LINE__, master->server->id, next_master->server->id); CLUSTER_NEXT_MASTER = NULL; return EEXIST; } return 0; } static int load_data() { int result; if ((result=adb_load_data((AuthServerContext *) sf_get_random_thread_data()->arg)) != 0) { return result; } if ((result=adb_check_generate_admin_user((AuthServerContext *) sf_get_random_thread_data()->arg)) != 0) { return result; } return 0; } static int cluster_relationship_set_master(FCFSAuthClusterServerInfo *new_master, const time_t start_time) { int result; FCFSAuthClusterServerInfo *old_master; char formatted_ip[FORMATTED_IP_SIZE]; old_master = CLUSTER_MASTER_ATOM_PTR; if (new_master == old_master) { format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( new_master->server), formatted_ip); logDebug("file: "__FILE__", line: %d, " "the server id: %d, %s:%u already is master", __LINE__, new_master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(new_master->server)); return 0; } if (CLUSTER_MYSELF_PTR == new_master) { if ((result=load_data()) != 0) { sf_terminate_myself(); return result; } } else { char time_used[128]; if (start_time > 0) { sprintf(time_used, ", election time used: %ds", (int)(g_current_time - start_time)); } else { *time_used = '\0'; } format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( new_master->server), formatted_ip); logInfo("file: "__FILE__", line: %d, " "the master server id: %d, %s:%u%s", __LINE__, new_master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(new_master->server), time_used); } do { if (__sync_bool_compare_and_swap(&CLUSTER_MASTER_PTR, old_master, new_master)) { break; } old_master = CLUSTER_MASTER_ATOM_PTR; } while (old_master != new_master); if (CLUSTER_MYSELF_PTR == new_master) { if ((result=pool_usage_updater_start()) != 0) { sf_terminate_myself(); return result; } } return 0; } int cluster_relationship_commit_master(FCFSAuthClusterServerInfo *master) { const time_t start_time = 0; FCFSAuthClusterServerInfo *next_master; int result; next_master = CLUSTER_NEXT_MASTER; if (next_master == NULL) { logError("file: "__FILE__", line: %d, " "next master is NULL", __LINE__); return EBUSY; } if (next_master != master) { logError("file: "__FILE__", line: %d, " "next master server id: %d != expected server id: %d", __LINE__, next_master->server->id, master->server->id); CLUSTER_NEXT_MASTER = NULL; return EBUSY; } result = cluster_relationship_set_master(master, start_time); CLUSTER_NEXT_MASTER = NULL; return result; } void cluster_relationship_trigger_reselect_master() { FCFSAuthClusterServerInfo *master; master = CLUSTER_MASTER_ATOM_PTR; if (CLUSTER_MYSELF_PTR != master) { return; } if (!cluster_unset_master(master)) { return; } if (NEED_CHECK_VOTE_NODE()) { vote_client_proto_close_connection(&VOTE_CONNECTION); } session_subscribe_clear_session(); server_session_clear(); pool_usage_updater_terminate(); auth_db_destroy(); } static int cluster_pre_set_next_master(FCFSAuthClusterServerInfo *cs, FCFSAuthClusterServerStatus *server_status, bool *bConnectFail) { FCFSAuthClusterServerInfo *master; int result; master = server_status->cs; if (cs == CLUSTER_MYSELF_PTR) { if ((result=cluster_relationship_pre_set_master(master)) == 0) { init_inactive_server_array(); } return result; } else { return do_notify_master_changed(cs, master, FCFS_AUTH_CLUSTER_PROTO_PRE_SET_NEXT_MASTER, bConnectFail); } } static int cluster_commit_next_master(FCFSAuthClusterServerInfo *cs, FCFSAuthClusterServerStatus *server_status, bool *bConnectFail) { FCFSAuthClusterServerInfo *master; master = server_status->cs; if (cs == CLUSTER_MYSELF_PTR) { return cluster_relationship_commit_master(master); } else { return do_notify_master_changed(cs, master, FCFS_AUTH_CLUSTER_PROTO_COMMIT_NEXT_MASTER, bConnectFail); } } typedef int (*cluster_notify_next_master_func)(FCFSAuthClusterServerInfo *cs, FCFSAuthClusterServerStatus *server_status, bool *bConnectFail); static int notify_next_master(cluster_notify_next_master_func notify_func, FCFSAuthClusterServerStatus *server_status, const unsigned char vote_req_cmd, int *success_count) { FCFSAuthClusterServerInfo *server; FCFSAuthClusterServerInfo *send; int result; bool bConnectFail; result = ENOENT; *success_count = 0; send = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count; for (server=CLUSTER_SERVER_ARRAY.servers; server 1) { need_log = true; last_log_time = g_current_time; } if (g_current_time - last_log_time > 8) { need_log = ((i + 1) % 10 == 0); if (need_log) { last_log_time = g_current_time; } } else { need_log = false; } if ((result=cluster_get_master(&server_status, need_log, &active_count)) != 0) { return result; } ++i; if (!sf_election_quorum_check(MASTER_ELECTION_QUORUM, VOTE_NODE_ENABLED, CLUSTER_SERVER_ARRAY.count, active_count)) { sleep_secs = 1; if (need_log) { logWarning("file: "__FILE__", line: %d, " "round %dth select master fail because alive server " "count: %d < half of total server count: %d, " "try again after %d seconds.", __LINE__, i, active_count, CLUSTER_SERVER_ARRAY.count, sleep_secs); } sleep(sleep_secs); continue; } if ((active_count == CLUSTER_SERVER_ARRAY.count) || (active_count >= 2 && server_status.is_master)) { break; } remain_time = ELECTION_MAX_WAIT_TIME - (g_current_time - start_time); if (remain_time <= 0) { break; } sleep_secs = FC_MIN(remain_time, max_sleep_secs); logWarning("file: "__FILE__", line: %d, " "round %dth select master, alive server count: %d " "< server count: %d, try again after %d seconds.", __LINE__, i, active_count, CLUSTER_SERVER_ARRAY.count, sleep_secs); sleep(sleep_secs); if ((i % 2 == 0) && (max_sleep_secs < 8)) { max_sleep_secs *= 2; } } next_master = CLUSTER_MASTER_ATOM_PTR; if (next_master != NULL) { format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( next_master->server), formatted_ip); logInfo("file: "__FILE__", line: %d, " "abort election because the master exists, " "master id: %d, %s:%u, election time used: %ds", __LINE__, next_master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(next_master->server), (int)(g_current_time - start_time)); return 0; } next_master = server_status.cs; if (CLUSTER_MYSELF_PTR == next_master) { if ((result=cluster_notify_master_changed( &server_status)) != 0) { return result; } format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( next_master->server), formatted_ip); logInfo("file: "__FILE__", line: %d, " "I am the new master, id: %d, %s:%u, election " "time used: %ds", __LINE__, next_master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(next_master-> server), (int)(g_current_time - start_time)); } else { if (server_status.is_master) { cluster_relationship_set_master(next_master, start_time); } else if (CLUSTER_MASTER_ATOM_PTR == NULL) { format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( next_master->server), formatted_ip); logInfo("file: "__FILE__", line: %d, " "election time used: %ds, waiting for the candidate " "master server id: %d, %s:%u notify ...", __LINE__, (int)(g_current_time - start_time), next_master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT( next_master->server)); return ENOENT; } } return 0; } static int cluster_ping_master(FCFSAuthClusterServerInfo *master, ConnectionInfo *conn, const int timeout, bool *is_ping) { int connect_timeout; int network_timeout; int result; if (CLUSTER_MYSELF_PTR == master) { *is_ping = false; return master_check(); } network_timeout = FC_MIN(CLUSTER_NETWORK_TIMEOUT, timeout); *is_ping = true; if (conn->sock < 0) { connect_timeout = FC_MIN(CLUSTER_CONNECT_TIMEOUT, timeout); if ((result=fc_server_make_connection(&CLUSTER_GROUP_ADDRESS_ARRAY( master->server), conn, "fauth", connect_timeout)) != 0) { return result; } if ((result=proto_join_master(conn, network_timeout)) != 0) { conn_pool_disconnect_server(conn); return result; } } if ((result=proto_ping_master(conn, network_timeout)) != 0) { conn_pool_disconnect_server(conn); } return result; } static void *cluster_thread_entrance(void* arg) { #define MAX_SLEEP_SECONDS 10 int result; int fail_count; int sleep_seconds; int ping_remain_time; time_t ping_start_time; bool is_ping; FCFSAuthClusterServerInfo *master; ConnectionInfo mconn; //master connection char formatted_ip[FORMATTED_IP_SIZE]; #ifdef OS_LINUX prctl(PR_SET_NAME, "relationship"); #endif memset(&mconn, 0, sizeof(mconn)); mconn.sock = -1; fail_count = 0; sleep_seconds = 1; ping_start_time = g_current_time; while (SF_G_CONTINUE_FLAG) { master = CLUSTER_MASTER_ATOM_PTR; if (master == NULL) { if (cluster_select_master() != 0) { sleep_seconds = 1 + (int)((double)rand() * (double)MAX_SLEEP_SECONDS / RAND_MAX); } else { if (mconn.sock >= 0) { conn_pool_disconnect_server(&mconn); } ping_start_time = g_current_time; sleep_seconds = 1; } } else { ping_remain_time = ELECTION_MASTER_LOST_TIMEOUT - (g_current_time - ping_start_time); if (ping_remain_time < 2) { ping_remain_time = 2; } if ((result=cluster_ping_master(master, &mconn, ping_remain_time, &is_ping)) == 0) { fail_count = 0; ping_start_time = g_current_time; sleep_seconds = 1; } else if (is_ping) { ++fail_count; format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( master->server), formatted_ip); logError("file: "__FILE__", line: %d, " "%dth ping master id: %d, %s:%u fail", __LINE__, fail_count, master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(master->server)); if (result == SF_RETRIABLE_ERROR_NOT_MASTER) { cluster_unset_master(master); fail_count = 0; sleep_seconds = 0; } else if (g_current_time - ping_start_time > ELECTION_MASTER_LOST_TIMEOUT) { if (fail_count > 1) { cluster_unset_master(master); fail_count = 0; } sleep_seconds = 0; } else { sleep_seconds = 1; } } else { sleep_seconds = 0; //master check fail } } if (sleep_seconds > 0) { sleep(sleep_seconds); } } return NULL; } int cluster_relationship_init() { int result; int bytes; pthread_t tid; VOTE_CONNECTION.sock = -1; bytes = sizeof(FCFSAuthClusterServerDetectEntry) * CLUSTER_SERVER_ARRAY.count; INACTIVE_SERVER_ARRAY.entries = (FCFSAuthClusterServerDetectEntry *) fc_malloc(bytes); if (INACTIVE_SERVER_ARRAY.entries == NULL) { return ENOMEM; } if ((result=init_pthread_lock(&INACTIVE_SERVER_ARRAY.lock)) != 0) { logError("file: "__FILE__", line: %d, " "init_pthread_lock fail, errno: %d, error info: %s", __LINE__, result, STRERROR(result)); return result; } INACTIVE_SERVER_ARRAY.alloc = CLUSTER_SERVER_ARRAY.count; return fc_create_thread(&tid, cluster_thread_entrance, NULL, SF_G_THREAD_STACK_SIZE); } int cluster_relationship_destroy() { return 0; } void cluster_relationship_add_to_inactive_sarray(FCFSAuthClusterServerInfo *cs) { FCFSAuthClusterServerDetectEntry *entry; FCFSAuthClusterServerDetectEntry *end; bool found; found = false; PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock); if (INACTIVE_SERVER_ARRAY.count > 0) { end = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count; for (entry=INACTIVE_SERVER_ARRAY.entries; entrycs == cs) { found = true; break; } } } if (!found) { if (INACTIVE_SERVER_ARRAY.count < INACTIVE_SERVER_ARRAY.alloc) { entry = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count; SET_SERVER_DETECT_ENTRY(entry, cs); INACTIVE_SERVER_ARRAY.count++; } else { logError("file: "__FILE__", line: %d, " "server id: %d, add to inactive array fail " "because array is full", __LINE__, cs->server->id); } } else { logWarning("file: "__FILE__", line: %d, " "server id: %d, already in inactive array!", __LINE__, cs->server->id); } PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock); } void cluster_relationship_remove_from_inactive_sarray(FCFSAuthClusterServerInfo *cs) { FCFSAuthClusterServerDetectEntry *entry; FCFSAuthClusterServerDetectEntry *p; FCFSAuthClusterServerDetectEntry *end; PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock); end = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count; for (entry=INACTIVE_SERVER_ARRAY.entries; entrycs == cs) { break; } } if (entry < end) { for (p=entry+1; p * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //cluster_relationship.h #ifndef _CLUSTER_RELATIONSHIP_H_ #define _CLUSTER_RELATIONSHIP_H_ #include #include #include "server_types.h" #ifdef __cplusplus extern "C" { #endif int cluster_relationship_init(); int cluster_relationship_destroy(); int cluster_relationship_pre_set_master(FCFSAuthClusterServerInfo *master); int cluster_relationship_commit_master(FCFSAuthClusterServerInfo *master); void cluster_relationship_trigger_reselect_master(); void cluster_relationship_add_to_inactive_sarray(FCFSAuthClusterServerInfo *cs); void cluster_relationship_remove_from_inactive_sarray(FCFSAuthClusterServerInfo *cs); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/common_handler.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //common_handler.c #include #include #include #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/shared_func.h" #include "fastcommon/sched_thread.h" #include "common/auth_proto.h" #include "server_global.h" #include "common_handler.h" #define LOG_LEVEL_FOR_DEBUG LOG_DEBUG static int fcfs_auth_get_cmd_log_level(const int cmd) { switch (cmd) { case SF_PROTO_ACTIVE_TEST_REQ: case SF_PROTO_ACTIVE_TEST_RESP: return LOG_NOTHING; case SF_SERVICE_PROTO_REPORT_REQ_RECEIPT_REQ: return LOG_DEBUG; default: return LOG_LEVEL_FOR_DEBUG; } } void common_handler_init() { SFHandlerContext handler_ctx; fcfs_auth_proto_init(); handler_ctx.slow_log = &SLOW_LOG; handler_ctx.callbacks.get_cmd_caption = fcfs_auth_get_cmd_caption; if (FC_LOG_BY_LEVEL(LOG_LEVEL_FOR_DEBUG)) { handler_ctx.callbacks.get_cmd_log_level = fcfs_auth_get_cmd_log_level; } else { handler_ctx.callbacks.get_cmd_log_level = NULL; } sf_proto_set_handler_context(&handler_ctx); } int fcfs_auth_deal_get_master(struct fast_task_info *task, const int group_index) { int result; FCFSAuthProtoGetServerResp *resp; FCFSAuthClusterServerInfo *master; const FCAddressInfo *addr; if ((result=server_expect_body_length(0)) != 0) { return result; } master = (CLUSTER_MASTER_ATOM_PTR != NULL) ? CLUSTER_MASTER_ATOM_PTR : CLUSTER_NEXT_MASTER; if (master == NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "the master NOT exist"); return SF_RETRIABLE_ERROR_NO_SERVER; } resp = (FCFSAuthProtoGetServerResp *)SF_PROTO_SEND_BODY(task); if (group_index == SERVICE_GROUP_INDEX) { addr = fc_server_get_address_by_peer(&SERVICE_GROUP_ADDRESS_ARRAY( master->server), task->client_ip); } else { addr = fc_server_get_address_by_peer(&CLUSTER_GROUP_ADDRESS_ARRAY( master->server), task->client_ip); } int2buff(master->server->id, resp->server_id); fc_safe_strcpy(resp->ip_addr, addr->conn.ip_addr); short2buff(addr->conn.port, resp->port); RESPONSE.header.body_len = sizeof(FCFSAuthProtoGetServerResp); TASK_CTX.common.response_done = true; return 0; } ================================================ FILE: src/auth/server/common_handler.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //common_handler.h #ifndef FCFS_AUTH_COMMON_HANDLER_H #define FCFS_AUTH_COMMON_HANDLER_H #include #include #include #include "server_types.h" #ifdef __cplusplus extern "C" { #endif void common_handler_init(); int fcfs_auth_deal_get_master(struct fast_task_info *task, const int group_index); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/db/auth_db.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fastcommon/uniq_skiplist.h" #include "fastcommon/fast_allocator.h" #include "fastcommon/fc_atomic.h" #include "sf/sf_global.h" #include "../../common/auth_func.h" #include "../server_global.h" #include "dao/user.h" #include "dao/storage_pool.h" #include "dao/granted_pool.h" #include "auth_db.h" #define SKIPLIST_INIT_LEVEL_COUNT 2 static int auth_db_init(); typedef struct auth_db_context { pthread_mutex_t lock; struct fast_allocator_context name_acontext; bool inited; struct { int count; UniqSkiplistPair sl_pair; //element: DBUserInfo * struct fast_mblock_man allocator; //element: DBUserInfo } user; struct { volatile int64_t auto_id; struct { UniqSkiplistFactory created; UniqSkiplistFactory granted; } factories; struct { struct fast_mblock_man created; //element: DBStoragePoolInfo struct fast_mblock_man granted; //element: DBGrantedPoolInfo } allocators; struct { UniqSkiplistPair by_id; //element: DBStoragePoolInfo * UniqSkiplistPair by_name; //element: DBStoragePoolInfo * } sl_pairs; } pool; } AuthDBContext; static AuthDBContext adb_ctx; static int user_compare(const DBUserInfo *user1, const DBUserInfo *user2) { return fc_string_compare(&user1->user.name, &user2->user.name); } static int pool_name_cmp(const DBStoragePoolInfo *sp1, const DBStoragePoolInfo *sp2) { return fc_string_compare(&sp1->pool.name, &sp2->pool.name); } static int pool_id_cmp(const DBStoragePoolInfo *sp1, const DBStoragePoolInfo *sp2) { return fc_compare_int64(sp1->pool.id, sp2->pool.id); } static int granted_pool_cmp(const DBGrantedPoolInfo *pg1, const DBGrantedPoolInfo *pg2) { return fc_compare_int64(pg1->granted.pool_id, pg2->granted.pool_id); } int user_alloc_init(void *element, void *args) { DBUserInfo *user; user = (DBUserInfo *)element; if ((user->storage_pools.created=uniq_skiplist_new(&adb_ctx.pool. factories.created, SKIPLIST_INIT_LEVEL_COUNT)) == NULL) { return ENOMEM; } if ((user->storage_pools.granted=uniq_skiplist_new(&adb_ctx.pool. factories.granted, SKIPLIST_INIT_LEVEL_COUNT)) == NULL) { return ENOMEM; } return 0; } static inline int init_name_allocators( struct fast_allocator_context *name_acontext) { #define NAME_REGION_COUNT 1 const int obj_size = 0; struct fast_region_info regions[NAME_REGION_COUNT]; FAST_ALLOCATOR_INIT_REGION(regions[0], 0, 256, 8, 1024); return fast_allocator_init_ex(name_acontext, "name", obj_size, NULL, regions, NAME_REGION_COUNT, 0, 0.00, 0, false); } static int init_allocators() { int result; if ((result=fast_mblock_init_ex1(&adb_ctx.user.allocator, "user_allocator", sizeof(DBUserInfo), 256, 0, user_alloc_init, NULL, true)) != 0) { return result; } if ((result=fast_mblock_init_ex1(&adb_ctx.pool.allocators.created, "spool_created", sizeof(DBStoragePoolInfo), 1024, 0, NULL, NULL, true)) != 0) { return result; } if ((result=fast_mblock_init_ex1(&adb_ctx.pool.allocators.granted, "spool_granted", sizeof(DBGrantedPoolInfo), 1024, 0, NULL, NULL, true)) != 0) { return result; } return 0; } static void destroy_allocators() { fast_mblock_destroy(&adb_ctx.user.allocator); fast_mblock_destroy(&adb_ctx.pool.allocators.created); fast_mblock_destroy(&adb_ctx.pool.allocators.granted); } static void free_storage_pool_func(UniqSkiplist *sl, void *ptr, const int delay_seconds) { DBStoragePoolInfo *dbspool; dbspool = (DBStoragePoolInfo *)ptr; uniq_skiplist_delete(adb_ctx.pool.sl_pairs.by_id.skiplist, dbspool); uniq_skiplist_delete(adb_ctx.pool.sl_pairs.by_name.skiplist, dbspool); fast_allocator_free(&adb_ctx.name_acontext, dbspool->pool.name.str); fast_mblock_free_object(&adb_ctx.pool.allocators.created, dbspool); } static void free_granted_pool_func(UniqSkiplist *sl, void *ptr, const int delay_seconds) { DBGrantedPoolInfo *dbgranted; dbgranted = (DBGrantedPoolInfo *)ptr; fast_mblock_free_object(&adb_ctx.pool.allocators.granted, dbgranted); } static int init_skiplists() { const int max_level_count = 12; const int alloc_skiplist_once = 1024; const int min_alloc_elements_once = 8; const int delay_free_seconds = 0; int result; if ((result=uniq_skiplist_init_pair(&adb_ctx.user.sl_pair, SKIPLIST_INIT_LEVEL_COUNT, max_level_count, (skiplist_compare_func)user_compare, NULL, min_alloc_elements_once, delay_free_seconds)) != 0) { return result; } if ((result=uniq_skiplist_init_ex(&adb_ctx.pool.factories.created, max_level_count, (skiplist_compare_func)pool_name_cmp, free_storage_pool_func, alloc_skiplist_once, min_alloc_elements_once, delay_free_seconds, NULL)) != 0) { return result; } if ((result=uniq_skiplist_init_ex(&adb_ctx.pool.factories.granted, max_level_count, (skiplist_compare_func)granted_pool_cmp, free_granted_pool_func, alloc_skiplist_once, min_alloc_elements_once, delay_free_seconds, NULL)) != 0) { return result; } if ((result=uniq_skiplist_init_pair(&adb_ctx.pool.sl_pairs.by_id, SKIPLIST_INIT_LEVEL_COUNT, max_level_count, (skiplist_compare_func)pool_id_cmp, NULL, min_alloc_elements_once, delay_free_seconds)) != 0) { return result; } if ((result=uniq_skiplist_init_pair(&adb_ctx.pool.sl_pairs.by_name, SKIPLIST_INIT_LEVEL_COUNT, max_level_count, (skiplist_compare_func)pool_name_cmp, NULL, min_alloc_elements_once, delay_free_seconds)) != 0) { return result; } return 0; } static void destroy_skiplists() { uniq_skiplist_destroy(&adb_ctx.user.sl_pair.factory); uniq_skiplist_destroy(&adb_ctx.pool.factories.created); uniq_skiplist_destroy(&adb_ctx.pool.factories.granted); uniq_skiplist_destroy(&adb_ctx.pool.sl_pairs.by_id.factory); uniq_skiplist_destroy(&adb_ctx.pool.sl_pairs.by_name.factory); } static int auth_db_init() { int result; if ((result=init_pthread_lock(&adb_ctx.lock)) != 0) { return result; } if ((result=init_name_allocators(&adb_ctx.name_acontext)) != 0) { return result; } if ((result=init_allocators()) != 0) { return result; } if ((result=init_skiplists()) != 0) { return result; } adb_ctx.inited = true; return 0; } void auth_db_destroy() { if (adb_ctx.inited) { destroy_skiplists(); destroy_allocators(); fast_allocator_destroy(&adb_ctx.name_acontext); pthread_mutex_destroy(&adb_ctx.lock); adb_ctx.inited = false; } } static inline DBUserInfo *user_get(AuthServerContext *server_ctx, const string_t *username) { DBUserInfo target; target.user.name = *username; return (DBUserInfo *)uniq_skiplist_find( adb_ctx.user.sl_pair.skiplist, &target); } static inline int user_set_passwd(DBUserInfo *user, const string_t *passwd) { int result; char *old_passwd; old_passwd = user->user.passwd.str; result = fast_allocator_alloc_string(&adb_ctx.name_acontext, &user->user.passwd, passwd); if (old_passwd != NULL) { fast_allocator_free(&adb_ctx.name_acontext, old_passwd); } return result; } static int user_create(AuthServerContext *server_ctx, DBUserInfo **dbuser, const FCFSAuthUserInfo *user, const bool addto_backend) { int result; bool need_insert; if (*dbuser == NULL) { *dbuser = (DBUserInfo *)fast_mblock_alloc_object( &adb_ctx.user.allocator); if (*dbuser == NULL) { return ENOMEM; } if ((result=fast_allocator_alloc_string(&adb_ctx.name_acontext, &(*dbuser)->user.name, &user->name)) != 0) { return result; } need_insert = true; } else { need_insert = false; } if ((result=user_set_passwd(*dbuser, &user->passwd)) != 0) { return result; } (*dbuser)->user.priv = user->priv; if (addto_backend) { if ((result=dao_user_create(server_ctx->dao_ctx, &(*dbuser)->user)) != 0) { return result; } } else { (*dbuser)->user.id = user->id; } PTHREAD_MUTEX_LOCK(&adb_ctx.lock); (*dbuser)->user.status = user->status; if (need_insert) { if ((result=uniq_skiplist_insert(adb_ctx.user.sl_pair. skiplist, *dbuser)) == 0) { adb_ctx.user.count++; } } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return 0; } int adb_user_create(AuthServerContext *server_ctx, const FCFSAuthUserInfo *user) { const bool addto_backend = true; int result; DBUserInfo *dbuser; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); dbuser = user_get(server_ctx, &user->name); if (dbuser != NULL && dbuser->user.status == FCFS_AUTH_USER_STATUS_NORMAL) { result = EEXIST; } else { result = ENOENT; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (result == ENOENT) { result = user_create(server_ctx, &dbuser, user, addto_backend); } return result; } const DBUserInfo *adb_user_get(AuthServerContext *server_ctx, const string_t *username) { DBUserInfo *user; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); user = user_get(server_ctx, username); PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (user != NULL && user->user.status == FCFS_AUTH_USER_STATUS_NORMAL) { return user; } else { return NULL; } } int adb_user_remove(AuthServerContext *server_ctx, const string_t *username) { DBUserInfo *user; int result; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); user = user_get(server_ctx, username); if (user != NULL && user->user.status == FCFS_AUTH_USER_STATUS_NORMAL) { if ((result=dao_user_remove(server_ctx->dao_ctx, user->user.id)) == 0) { user->user.status = FCFS_AUTH_USER_STATUS_DELETED; adb_ctx.user.count--; } } else { result = ENOENT; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return result; } int adb_user_update_priv(AuthServerContext *server_ctx, const string_t *username, const int64_t priv) { DBUserInfo *user; int result; bool changed; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); user = user_get(server_ctx, username); if (user != NULL && user->user.status == FCFS_AUTH_USER_STATUS_NORMAL) { if (user->user.priv == priv) { result = 0; changed = false; } else if ((result=dao_user_update_priv(server_ctx->dao_ctx, user->user.id, priv)) == 0) { user->user.priv = priv; changed = true; } else { changed = false; } } else { result = ENOENT; changed = false; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (changed) { g_db_priv_change_callbacks.user_priv_changed(user->user.id, priv); } return result; } int adb_user_update_passwd(AuthServerContext *server_ctx, const string_t *username, const string_t *passwd) { DBUserInfo *user; int result; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); user = user_get(server_ctx, username); if (user != NULL && user->user.status == FCFS_AUTH_USER_STATUS_NORMAL) { if ((result=dao_user_update_passwd(server_ctx->dao_ctx, user->user.id, passwd)) == 0) { user_set_passwd(user, passwd); } } else { result = ENOENT; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return result; } int adb_user_list(AuthServerContext *server_ctx, const SFListLimitInfo *limit, FCFSAuthUserArray *array) { UniqSkiplistIterator it; DBUserInfo *dbuser; int result; array->count = 0; if ((result=fcfs_auth_user_check_realloc_array(array, limit->count)) != 0) { return result; } PTHREAD_MUTEX_LOCK(&adb_ctx.lock); uniq_skiplist_iterator_at(adb_ctx.user.sl_pair.skiplist, limit->offset, &it); while ((array->count < limit->count) && (dbuser=(DBUserInfo *) uniq_skiplist_next(&it)) != NULL) { if (dbuser->user.status == FCFS_AUTH_USER_STATUS_NORMAL) { array->users[array->count++] = dbuser->user; } } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return 0; } static inline DBStoragePoolInfo *get_spool_by_id(const int64_t pool_id) { DBStoragePoolInfo target; target.pool.id = pool_id; return (DBStoragePoolInfo *)uniq_skiplist_find( adb_ctx.pool.sl_pairs.by_id.skiplist, &target); } static inline DBStoragePoolInfo *get_spool_by_name(const string_t *name) { DBStoragePoolInfo target; target.pool.name = *name; return (DBStoragePoolInfo *)uniq_skiplist_find( adb_ctx.pool.sl_pairs.by_name.skiplist, &target); } static inline DBStoragePoolInfo *user_spool_get(AuthServerContext *server_ctx, DBUserInfo *user, const string_t *poolname) { DBStoragePoolInfo target; target.pool.name = *poolname; return (DBStoragePoolInfo *)uniq_skiplist_find( user->storage_pools.created, &target); } static inline int spool_global_skiplists_insert(DBStoragePoolInfo *dbspool) { int result; if ((result=uniq_skiplist_insert(adb_ctx.pool.sl_pairs. by_id.skiplist, dbspool)) == 0) { result = uniq_skiplist_insert(adb_ctx.pool. sl_pairs.by_name.skiplist, dbspool); } return result; } static int storage_pool_create(AuthServerContext *server_ctx, DBUserInfo *dbuser, DBStoragePoolInfo **dbspool, const FCFSAuthStoragePoolInfo *pool, const bool addto_backend) { int result; bool need_insert; if (*dbspool == NULL) { *dbspool = (DBStoragePoolInfo *)fast_mblock_alloc_object( &adb_ctx.pool.allocators.created); if (*dbspool == NULL) { return ENOMEM; } if ((result=fast_allocator_alloc_string(&adb_ctx.name_acontext, &(*dbspool)->pool.name, &pool->name)) != 0) { return result; } (*dbspool)->user = dbuser; need_insert = true; } else { result = 0; need_insert = false; } (*dbspool)->pool.quota = pool->quota; if (addto_backend) { if ((result=dao_spool_create(server_ctx->dao_ctx, &dbuser-> user.name, &(*dbspool)->pool)) != 0) { fast_allocator_free(&adb_ctx.name_acontext, (*dbspool)->pool.name.str); fast_mblock_free_object(&adb_ctx.pool. allocators.created, *dbspool); return result; } } else { (*dbspool)->pool.id = pool->id; } PTHREAD_MUTEX_LOCK(&adb_ctx.lock); (*dbspool)->pool.status = pool->status; if (need_insert) { if ((result=spool_global_skiplists_insert(*dbspool)) == 0) { result = uniq_skiplist_insert(dbuser->storage_pools. created, *dbspool); } } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (result != 0) { dao_spool_remove(server_ctx->dao_ctx, (*dbspool)->pool.id); //rollback fast_allocator_free(&adb_ctx.name_acontext, (*dbspool)->pool.name.str); fast_mblock_free_object(&adb_ctx.pool. allocators.created, *dbspool); } return result; } int adb_spool_create(AuthServerContext *server_ctx, const string_t *username, const FCFSAuthStoragePoolInfo *pool) { const bool addto_backend = true; int result; DBUserInfo *user; DBStoragePoolInfo *dbspool; result = ENOENT; dbspool = NULL; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); user = user_get(server_ctx, username); if (user != NULL) { if (user->user.status == FCFS_AUTH_USER_STATUS_NORMAL) { dbspool = user_spool_get(server_ctx, user, &pool->name); if (dbspool != NULL) { if (dbspool->pool.status == FCFS_AUTH_POOL_STATUS_NORMAL) { result = EEXIST; } } else { if (get_spool_by_name(&pool->name) != NULL) { result = EEXIST; //occupied by other user } } } else { user = NULL; } } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (user != NULL && result == ENOENT) { return storage_pool_create(server_ctx, user, &dbspool, pool, addto_backend); } else { return result; } } const FCFSAuthStoragePoolInfo *adb_spool_get(AuthServerContext *server_ctx, const string_t *username, const string_t *poolname) { DBUserInfo *user; DBStoragePoolInfo *spool; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); user = user_get(server_ctx, username); if (user != NULL) { spool = user_spool_get(server_ctx, user, poolname); } else { spool = NULL; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (spool != NULL && spool->pool.status == FCFS_AUTH_POOL_STATUS_NORMAL) { return &spool->pool; } else { return NULL; } } int adb_spool_remove(AuthServerContext *server_ctx, const string_t *username, const string_t *poolname) { DBUserInfo *user; DBStoragePoolInfo *spool; int result; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); user = user_get(server_ctx, username); if (user != NULL) { spool = user_spool_get(server_ctx, user, poolname); } else { spool = NULL; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (!(spool != NULL && spool->pool.status == FCFS_AUTH_POOL_STATUS_NORMAL)) { return ENOENT; } if ((result=dao_spool_remove(server_ctx->dao_ctx, spool->pool.id)) != 0) { return result; } PTHREAD_MUTEX_LOCK(&adb_ctx.lock); spool->pool.status = FCFS_AUTH_POOL_STATUS_DELETED; PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return 0; } static void call_pool_quota_avail_func(const DBStoragePoolInfo *dbpool, const bool old_avail) { bool new_avail; new_avail = FCFS_AUTH_POOL_AVAILABLE(dbpool->pool); if (new_avail != old_avail) { g_db_priv_change_callbacks.pool_quota_avail_changed( dbpool->pool.id, new_avail); } } int adb_spool_set_quota(AuthServerContext *server_ctx, const string_t *username, const string_t *poolname, const int64_t quota) { DBUserInfo *user; DBStoragePoolInfo *spool; int64_t old_quota; int result; bool changed; bool old_avail; old_quota = 0; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); user = user_get(server_ctx, username); if (user != NULL) { if ((spool=user_spool_get(server_ctx, user, poolname)) != NULL) { old_quota = spool->pool.quota; } } else { spool = NULL; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (!(spool != NULL && spool->pool.status == FCFS_AUTH_POOL_STATUS_NORMAL)) { return ENOENT; } if (old_quota != quota) { if ((result=dao_spool_set_quota(server_ctx->dao_ctx, spool->pool.id, quota)) != 0) { return result; } old_avail = FCFS_AUTH_POOL_AVAILABLE(spool->pool); changed = true; } else { old_avail = false; changed = false; } PTHREAD_MUTEX_LOCK(&adb_ctx.lock); if (changed) { spool->pool.quota = quota; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (changed) { call_pool_quota_avail_func(spool, old_avail); } return 0; } int adb_spool_get_quota(AuthServerContext *server_ctx, const string_t *poolname, int64_t *quota) { DBStoragePoolInfo *spool; int result; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); if ((spool=get_spool_by_name(poolname)) != NULL) { if (spool->pool.status == FCFS_AUTH_POOL_STATUS_NORMAL) { *quota = spool->pool.quota; result = 0; } else { *quota = 0; result = ENOENT; } } else { *quota = 0; result = ENOENT; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return result; } int adb_spool_set_used_bytes(const string_t *poolname, const int64_t used_bytes) { DBStoragePoolInfo *dbpool; bool old_avail; if ((dbpool=adb_spool_global_get(poolname)) == NULL) { return ENOENT; } if (dbpool->pool.used == used_bytes) { return 0; } old_avail = FCFS_AUTH_POOL_AVAILABLE(dbpool->pool); dbpool->pool.used = used_bytes; call_pool_quota_avail_func(dbpool, old_avail); return 0; } int adb_spool_list(AuthServerContext *server_ctx, const string_t *username, const SFListLimitInfo *limit, FCFSAuthStoragePoolArray *array) { UniqSkiplistIterator it; DBUserInfo *user; DBStoragePoolInfo *spool; int result; array->count = 0; if ((result=fcfs_auth_spool_check_realloc_array(array, limit->count)) != 0) { return result; } PTHREAD_MUTEX_LOCK(&adb_ctx.lock); user = user_get(server_ctx, username); if (user != NULL) { uniq_skiplist_iterator_at(user->storage_pools.created, limit->offset, &it); while ((array->count < limit->count) && (spool=(DBStoragePoolInfo *) uniq_skiplist_next(&it)) != NULL) { if (spool->pool.status == FCFS_AUTH_POOL_STATUS_NORMAL) { array->spools[array->count++] = spool->pool; } } } else { result = ENOENT; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return result; } static int convert_spool_array(AuthServerContext *server_ctx, DBUserInfo *dbuser, const FCFSAuthStoragePoolArray *spool_array) { const bool addto_backend = false; int result; DBStoragePoolInfo *dbspool; const FCFSAuthStoragePoolInfo *spool; const FCFSAuthStoragePoolInfo *end; end = spool_array->spools + spool_array->count; for (spool=spool_array->spools; spooldao_ctx, &dbuser-> user.name, mpool, &spool_array)) != 0) { return result; } if ((result=convert_spool_array(server_ctx, dbuser, &spool_array)) != 0) { return result; } fcfs_auth_spool_free_array(&spool_array); return 0; } static int granted_pool_create(AuthServerContext *server_ctx, DBUserInfo *dbuser, DBGrantedPoolInfo **dbgranted, const FCFSAuthGrantedPoolInfo *granted, const bool addto_backend) { int result; bool need_insert; if (*dbgranted == NULL) { *dbgranted = (DBGrantedPoolInfo *)fast_mblock_alloc_object( &adb_ctx.pool.allocators.granted); if (*dbgranted == NULL) { return ENOMEM; } need_insert = true; } else { need_insert = false; } (*dbgranted)->granted.pool_id = granted->pool_id; (*dbgranted)->granted.privs = granted->privs; if (addto_backend) { if ((result=dao_granted_create(server_ctx->dao_ctx, &dbuser-> user.name, &(*dbgranted)->granted)) != 0) { fast_mblock_free_object(&adb_ctx.pool. allocators.granted, *dbgranted); return result; } } else { (*dbgranted)->granted.id = granted->id; result = 0; } if (need_insert) { PTHREAD_MUTEX_LOCK(&adb_ctx.lock); if (((*dbgranted)->sp=get_spool_by_id(granted->pool_id)) != NULL) { result = uniq_skiplist_insert(dbuser->storage_pools. granted, *dbgranted); } else { logError("file: "__FILE__", line: %d, " "storage pool not exist, pool id: %"PRId64, __LINE__, granted->pool_id); result = ENOENT; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (result != 0) { fast_mblock_free_object(&adb_ctx.pool. allocators.granted, *dbgranted); } } return result; } static int convert_granted_array(AuthServerContext *server_ctx, DBUserInfo *dbuser, const FCFSAuthGrantedPoolArray *granted_array) { const bool addto_backend = false; int result; DBGrantedPoolInfo *dbgranted; const FCFSAuthGrantedPoolFullInfo *gf; const FCFSAuthGrantedPoolFullInfo *end; end = granted_array->gpools + granted_array->count; for (gf=granted_array->gpools; gfgranted, addto_backend)) != 0) { return result; } } return 0; } static int load_granted_pool(AuthServerContext *server_ctx, DBUserInfo *dbuser) { FCFSAuthGrantedPoolArray granted_array; int result; fcfs_auth_granted_init_array(&granted_array); if ((result=dao_granted_list(server_ctx->dao_ctx, &dbuser-> user.name, &granted_array)) != 0) { return result; } if ((result=convert_granted_array(server_ctx, dbuser, &granted_array)) != 0) { return result; } fcfs_auth_granted_free_array(&granted_array); return 0; } static int convert_user_array(AuthServerContext *server_ctx, struct fast_mpool_man *mpool, const FCFSAuthUserArray *user_array) { const bool addto_backend = false; int result; DBUserInfo *dbuser; const FCFSAuthUserInfo *user; const FCFSAuthUserInfo *end; UniqSkiplistIterator it; end = user_array->users + user_array->count; for (user=user_array->users; userdao_ctx)) != 0) { return result; } if ((result=dao_spool_get_auto_id(server_ctx->dao_ctx, &auto_id)) != 0) { return result; } if (AUTO_ID_INITIAL > auto_id) { auto_id = AUTO_ID_INITIAL; } FC_ATOMIC_SET(adb_ctx.pool.auto_id, auto_id); return 0; } int adb_load_data(AuthServerContext *server_ctx) { const int alloc_size_once = 64 * 1024; const int discard_size = 8; int result; struct fast_mpool_man mpool; FCFSAuthUserArray user_array; if ((result=auth_db_init()) != 0) { return result; } if ((result=fast_mpool_init(&mpool, alloc_size_once, discard_size)) != 0) { return result; } fcfs_auth_user_init_array(&user_array); if ((result=dao_user_list(server_ctx->dao_ctx, &mpool, &user_array)) != 0) { return result; } result = convert_user_array(server_ctx, &mpool, &user_array); fcfs_auth_user_free_array(&user_array); fast_mpool_destroy(&mpool); if (result != 0) { return result; } return load_pool_auto_id(server_ctx); } int adb_check_generate_admin_user(AuthServerContext *server_ctx) { int result; bool need_create; unsigned char passwd[FCFS_AUTH_PASSWD_LEN]; FCFSAuthUserInfo user; if (ADMIN_GENERATE_MODE == AUTH_ADMIN_GENERATE_MODE_FIRST) { need_create = (adb_ctx.user.count == 0); } else { need_create = adb_user_get(server_ctx, &ADMIN_GENERATE_USERNAME) == NULL; } if (need_create) { user.name = ADMIN_GENERATE_USERNAME; fcfs_auth_generate_passwd(passwd); FC_SET_STRING_EX(user.passwd, (char *)passwd, FCFS_AUTH_PASSWD_LEN); user.priv = FCFS_AUTH_USER_PRIV_ALL; if ((result=adb_user_create(server_ctx, &user)) == 0) { if ((result=fcfs_auth_save_passwd(ADMIN_GENERATE_KEY_FILENAME. str, passwd)) == 0) { logInfo("file: "__FILE__", line: %d, " "scret key for user \"%s\" store to file: %s", __LINE__, ADMIN_GENERATE_USERNAME.str, ADMIN_GENERATE_KEY_FILENAME.str); } } return result; } else { return 0; } } static inline DBGrantedPoolInfo *granted_pool_get( const DBUserInfo *dbuser, const int64_t pool_id) { DBGrantedPoolInfo target; target.granted.pool_id = pool_id; return (DBGrantedPoolInfo *)uniq_skiplist_find( dbuser->storage_pools.granted, &target); } int adb_granted_create(AuthServerContext *server_ctx, const string_t *username, FCFSAuthGrantedPoolInfo *granted) { const bool addto_backend = true; bool changed; int result; DBUserInfo *dbuser; DBGrantedPoolInfo *dbgranted; dbgranted = NULL; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); dbuser = user_get(server_ctx, username); if (dbuser != NULL) { if (dbuser->user.status == FCFS_AUTH_USER_STATUS_NORMAL) { dbgranted = granted_pool_get(dbuser, granted->pool_id); } else { dbuser = NULL; } } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); /* logInfo("username: %.*s, ptr: %p, dbgranted: %p", username->len, username->str, user, dbgranted); */ if (dbuser == NULL) { return ENOENT; } if (dbgranted != NULL) { changed = !(dbgranted->granted.privs.fdir == granted->privs.fdir && dbgranted->granted.privs.fstore == granted->privs.fstore); if (!changed) { return 0; } } else { changed = false; } if ((result=granted_pool_create(server_ctx, dbuser, &dbgranted, granted, addto_backend)) != 0) { return result; } if (changed) { g_db_priv_change_callbacks.pool_priv_changed(dbuser->user.id, dbgranted->granted.pool_id, &dbgranted->granted.privs); } return 0; } int adb_granted_remove(AuthServerContext *server_ctx, const string_t *username, const int64_t pool_id) { int result; DBUserInfo *dbuser; DBGrantedPoolInfo *dbgranted; dbgranted = NULL; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); dbuser = user_get(server_ctx, username); if (dbuser != NULL) { if (dbuser->user.status == FCFS_AUTH_USER_STATUS_NORMAL) { dbgranted = granted_pool_get(dbuser, pool_id); } } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); if (dbgranted != NULL) { if ((result=dao_granted_remove(server_ctx->dao_ctx, username, pool_id)) == 0) { FCFSAuthSPoolPriviledges privs; privs.fdir = privs.fstore = FCFS_AUTH_POOL_ACCESS_NONE; g_db_priv_change_callbacks.pool_priv_changed(dbuser->user.id, dbgranted->granted.pool_id, &privs); PTHREAD_MUTEX_LOCK(&adb_ctx.lock); uniq_skiplist_delete(dbuser->storage_pools.granted, dbgranted); PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); } return result; } else { return ENOENT; } } static inline void set_gpool_full_info(FCFSAuthGrantedPoolFullInfo *gf, const DBGrantedPoolInfo *dbgpool) { gf->granted = dbgpool->granted; gf->username = dbgpool->sp->user->user.name; gf->pool_name = dbgpool->sp->pool.name; } int adb_granted_full_get(AuthServerContext *server_ctx, const string_t *username, const int64_t pool_id, FCFSAuthGrantedPoolFullInfo *gf) { DBUserInfo *dbuser; DBGrantedPoolInfo *dbgranted; dbgranted = NULL; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); dbuser = user_get(server_ctx, username); if (dbuser != NULL) { if (dbuser->user.status == FCFS_AUTH_USER_STATUS_NORMAL) { if ((dbgranted=granted_pool_get(dbuser, pool_id)) != NULL) { set_gpool_full_info(gf, dbgranted); } } } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return (dbgranted != NULL) ? 0 : ENOENT; } int adb_granted_privs_get(AuthServerContext *server_ctx, const DBUserInfo *dbuser, const DBStoragePoolInfo *dbpool, FCFSAuthSPoolPriviledges *privs) { DBGrantedPoolInfo *dbgranted; int result; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); if (dbpool->user == dbuser) { privs->fdir = privs->fstore = FCFS_AUTH_POOL_ACCESS_ALL; result = 0; } else if ((dbgranted=granted_pool_get(dbuser, dbpool->pool.id)) != NULL) { *privs = dbgranted->granted.privs; result = 0; } else { result = ENOENT; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return result; } int adb_granted_list(AuthServerContext *server_ctx, const string_t *username, const SFListLimitInfo *limit, FCFSAuthGrantedPoolArray *array) { UniqSkiplistIterator it; DBUserInfo *user; DBGrantedPoolInfo *dbgpool; int result; array->count = 0; if ((result=fcfs_auth_gpool_check_realloc_array( array, limit->count)) != 0) { return result; } PTHREAD_MUTEX_LOCK(&adb_ctx.lock); user = user_get(server_ctx, username); if (user != NULL) { uniq_skiplist_iterator_at(user->storage_pools.granted, limit->offset, &it); while ((array->count < limit->count) && (dbgpool=(DBGrantedPoolInfo *) uniq_skiplist_next(&it)) != NULL) { set_gpool_full_info(array->gpools + array->count, dbgpool); array->count++; } } else { result = ENOENT; } PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return result; } int64_t adb_spool_get_auto_id(AuthServerContext *server_ctx) { return __sync_add_and_fetch(&adb_ctx.pool.auto_id, 0); } int adb_spool_inc_auto_id(AuthServerContext *server_ctx) { int64_t auto_id; auto_id = __sync_add_and_fetch(&adb_ctx.pool.auto_id, 1); return dao_spool_set_auto_id(server_ctx->dao_ctx, auto_id); } int adb_spool_next_auto_id(AuthServerContext *server_ctx, int64_t *auto_id) { *auto_id = __sync_add_and_fetch(&adb_ctx.pool.auto_id, 1); return dao_spool_set_auto_id(server_ctx->dao_ctx, *auto_id); } DBStoragePoolInfo *adb_spool_global_get(const string_t *poolname) { DBStoragePoolInfo *dbpool; PTHREAD_MUTEX_LOCK(&adb_ctx.lock); dbpool = get_spool_by_name(poolname); PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock); return dbpool; } ================================================ FILE: src/auth/server/db/auth_db.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_DB_H #define _FCFS_AUTH_DB_H #include "../server_types.h" typedef struct db_user_info { FCFSAuthUserInfo user; struct { struct uniq_skiplist *created; //element: DBStoragePoolInfo struct uniq_skiplist *granted; //element: DBGrantedPoolInfo } storage_pools; } DBUserInfo; typedef struct db_storage_pool_info { FCFSAuthStoragePoolInfo pool; DBUserInfo *user; } DBStoragePoolInfo; typedef struct db_granted_pool_info { FCFSAuthGrantedPoolInfo granted; DBStoragePoolInfo *sp; } DBGrantedPoolInfo; typedef void (*adb_user_priv_change_callback)( const int64_t user_id, const int64_t new_priv); typedef void (*adb_pool_priv_change_callback)(const int64_t user_id, const int64_t pool_id, const FCFSAuthSPoolPriviledges *privs); typedef void (*adb_pool_quota_avail_change_callback)( const int64_t pool_id, const bool available); typedef struct db_priv_change_callbacks { adb_user_priv_change_callback user_priv_changed; adb_pool_priv_change_callback pool_priv_changed; adb_pool_quota_avail_change_callback pool_quota_avail_changed; } DBPrivChangeCallbacks; #ifdef __cplusplus extern "C" { #endif extern DBPrivChangeCallbacks g_db_priv_change_callbacks; int adb_load_data(AuthServerContext *server_ctx); void auth_db_destroy(); int adb_check_generate_admin_user(AuthServerContext *server_ctx); int adb_user_create(AuthServerContext *server_ctx, const FCFSAuthUserInfo *user); const DBUserInfo *adb_user_get(AuthServerContext *server_ctx, const string_t *username); int adb_user_remove(AuthServerContext *server_ctx, const string_t *username); int adb_user_list(AuthServerContext *server_ctx, const SFListLimitInfo *limit, FCFSAuthUserArray *array); int adb_user_update_priv(AuthServerContext *server_ctx, const string_t *username, const int64_t priv); int adb_user_update_passwd(AuthServerContext *server_ctx, const string_t *username, const string_t *passwd); /* storage pool */ int64_t adb_spool_get_auto_id(AuthServerContext *server_ctx); int adb_spool_inc_auto_id(AuthServerContext *server_ctx); int adb_spool_next_auto_id(AuthServerContext *server_ctx, int64_t *next_id); DBStoragePoolInfo *adb_spool_global_get(const string_t *poolname); static inline int adb_spool_access(AuthServerContext *server_ctx, const string_t *poolname) { return adb_spool_global_get(poolname) != NULL ? 0 : ENOENT; } int adb_spool_create(AuthServerContext *server_ctx, const string_t *username, const FCFSAuthStoragePoolInfo *pool); const FCFSAuthStoragePoolInfo *adb_spool_get(AuthServerContext *server_ctx, const string_t *username, const string_t *poolname); int adb_spool_remove(AuthServerContext *server_ctx, const string_t *username, const string_t *poolname); int adb_spool_set_quota(AuthServerContext *server_ctx, const string_t *username, const string_t *poolname, const int64_t quota); int adb_spool_get_quota(AuthServerContext *server_ctx, const string_t *poolname, int64_t *quota); int adb_spool_list(AuthServerContext *server_ctx, const string_t *username, const SFListLimitInfo *limit, FCFSAuthStoragePoolArray *array); int adb_spool_set_used_bytes(const string_t *poolname, const int64_t used_bytes); /* granted pool */ int adb_granted_create(AuthServerContext *server_ctx, const string_t *username, FCFSAuthGrantedPoolInfo *granted); int adb_granted_remove(AuthServerContext *server_ctx, const string_t *username, const int64_t pool_id); int adb_granted_full_get(AuthServerContext *server_ctx, const string_t *username, const int64_t pool_id, FCFSAuthGrantedPoolFullInfo *gf); int adb_granted_privs_get(AuthServerContext *server_ctx, const DBUserInfo *dbuser, const DBStoragePoolInfo *dbpool, FCFSAuthSPoolPriviledges *privs); int adb_granted_list(AuthServerContext *server_ctx, const string_t *username, const SFListLimitInfo *limit, FCFSAuthGrantedPoolArray *array); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/db/dao/dao.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "dao.h" ================================================ FILE: src/auth/server/db/dao/dao.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _AUTH_DAO_H #define _AUTH_DAO_H #include "../../server_global.h" #include "types.h" #include "user.h" #ifdef __cplusplus extern "C" { #endif static inline int dao_get_context_size() { return sizeof(FDIRClientContext); } static inline int dao_init_context(const int thread_index, void *ctx, char *session_id) { int result; FDIRClientContext *client_ctx; client_ctx = (FDIRClientContext *)ctx; if ((result=fdir_client_simple_init_ex(client_ctx, &g_fcfs_auth_client_vars.client_ctx, g_server_global_vars.fdir_client_cfg_filename, NULL)) != 0) { return result; } if (thread_index == 0) { fdir_client_auth_session_set_ex(client_ctx, session_id); } return 0; } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/db/dao/func.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "func.h" FCFSAuthDAOVariables g_auth_dao_vars = { {AUTH_NAMESPACE_STR, AUTH_NAMESPACE_LEN}, { {AUTH_XTTR_NAME_PASSWD_STR, AUTH_XTTR_NAME_PASSWD_LEN}, {AUTH_XTTR_NAME_PRIV_STR, AUTH_XTTR_NAME_PRIV_LEN}, {AUTH_XTTR_NAME_STATUS_STR, AUTH_XTTR_NAME_STATUS_LEN}, {AUTH_XTTR_NAME_QUOTA_STR, AUTH_XTTR_NAME_QUOTA_LEN}, {AUTH_XTTR_NAME_FDIR_STR, AUTH_XTTR_NAME_FDIR_LEN}, {AUTH_XTTR_NAME_FSTORE_STR, AUTH_XTTR_NAME_FSTORE_LEN}, {AUTH_XTTR_NAME_POOL_AUTO_ID_STR, AUTH_XTTR_NAME_POOL_AUTO_ID_LEN} } }; ================================================ FILE: src/auth/server/db/dao/func.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _AUTH_DAO_FUNC_H #define _AUTH_DAO_FUNC_H #include "fastcommon/fast_mpool.h" #include "types.h" #define AUTH_DIR_NAME_CREATED_STR "created" #define AUTH_DIR_NAME_CREATED_LEN (sizeof(AUTH_DIR_NAME_CREATED_STR) - 1) #define AUTH_DIR_NAME_GRANTED_STR "granted" #define AUTH_DIR_NAME_GRANTED_LEN (sizeof(AUTH_DIR_NAME_GRANTED_STR) - 1) #define AUTH_XTTR_NAME_PASSWD_STR "passwd" #define AUTH_XTTR_NAME_PASSWD_LEN (sizeof(AUTH_XTTR_NAME_PASSWD_STR) - 1) #define AUTH_XTTR_NAME_PRIV_STR "priv" #define AUTH_XTTR_NAME_PRIV_LEN (sizeof(AUTH_XTTR_NAME_PRIV_STR) - 1) #define AUTH_XTTR_NAME_STATUS_STR "status" #define AUTH_XTTR_NAME_STATUS_LEN (sizeof(AUTH_XTTR_NAME_STATUS_STR) - 1) #define AUTH_XTTR_NAME_QUOTA_STR "quota" #define AUTH_XTTR_NAME_QUOTA_LEN (sizeof(AUTH_XTTR_NAME_QUOTA_STR) - 1) #define AUTH_XTTR_NAME_FDIR_STR "fdir" #define AUTH_XTTR_NAME_FDIR_LEN (sizeof(AUTH_XTTR_NAME_FDIR_STR) - 1) #define AUTH_XTTR_NAME_FSTORE_STR "fstore" #define AUTH_XTTR_NAME_FSTORE_LEN (sizeof(AUTH_XTTR_NAME_FSTORE_STR) - 1) #define AUTH_XTTR_NAME_POOL_AUTO_ID_STR "auto_pid" #define AUTH_XTTR_NAME_POOL_AUTO_ID_LEN \ (sizeof(AUTH_XTTR_NAME_POOL_AUTO_ID_STR) - 1) typedef struct { string_t ns; struct { string_t passwd; string_t priv; string_t status; string_t quota; string_t fdir; string_t fstore; string_t pool_auto_id; } xttr_names; int64_t base_path_inode; } FCFSAuthDAOVariables; #define DAO_NAMESPACE g_auth_dao_vars.ns #define DAO_BASE_PATH_INODE g_auth_dao_vars.base_path_inode #define DAO_MODE_DIR (0700 | S_IFDIR) #define DAO_MODE_FILE (0700 | S_IFREG) #define AUTH_XTTR_NAME_PASSWD g_auth_dao_vars.xttr_names.passwd #define AUTH_XTTR_NAME_PRIV g_auth_dao_vars.xttr_names.priv #define AUTH_XTTR_NAME_STATUS g_auth_dao_vars.xttr_names.status #define AUTH_XTTR_NAME_QUOTA g_auth_dao_vars.xttr_names.quota #define AUTH_XTTR_NAME_FDIR g_auth_dao_vars.xttr_names.fdir #define AUTH_XTTR_NAME_FSTORE g_auth_dao_vars.xttr_names.fstore #define AUTH_XTTR_NAME_POOL_AUTO_ID g_auth_dao_vars.xttr_names.pool_auto_id #ifdef __cplusplus extern "C" { #endif extern FCFSAuthDAOVariables g_auth_dao_vars; static inline int dao_set_xattr_string(FDIRClientContext *client_ctx, const int64_t inode, const string_t *name, const string_t *value) { FDIRClientOperInodePair oino; key_value_pair_t xattr; AUTH_SET_OPER_INODE_PAIR(oino, inode); xattr.key = *name; xattr.value = *value; return fdir_client_set_xattr_by_inode(client_ctx, &DAO_NAMESPACE, &oino, &xattr, 0); } static inline int dao_set_xattr_integer(FDIRClientContext *client_ctx, const int64_t inode, const string_t *name, const int64_t nv) { char buff[32]; string_t value; value.str = buff; value.len = fc_itoa(nv, buff); return dao_set_xattr_string(client_ctx, inode, name, &value); } static inline int dao_get_xattr_string(FDIRClientContext *client_ctx, const int64_t inode, const string_t *name, string_t *value, const int size) { const int flags = 0; int result; FDIRClientOperInodePair oino; AUTH_SET_OPER_INODE_PAIR(oino, inode); result = fdir_client_get_xattr_by_inode_ex(client_ctx, &DAO_NAMESPACE, &oino, name, LOG_WARNING, value, size, flags); if (result == ENODATA) { value->len = 0; return 0; } else { return result; } } static inline int dao_get_xattr_int64(FDIRClientContext *client_ctx, const int64_t inode, const string_t *name, int64_t *nv) { char buff[32]; string_t value; char *endptr; int result; value.str = buff; if ((result=dao_get_xattr_string(client_ctx, inode, name, &value, sizeof(buff) - 1)) != 0) { return result; } if (value.len == 0) { *nv = 0; return 0; } *(value.str + value.len) = '\0'; *nv = strtoll(value.str, &endptr, 10); if (endptr != NULL && *endptr != '\0') { logError("file: "__FILE__", line: %d, " "field: %.*s, invalid digital string: %s", __LINE__, name->len, name->str, value.str); return EINVAL; } return 0; } static inline int dao_get_xattr_int32(FDIRClientContext *client_ctx, const int64_t inode, const string_t *name, int32_t *nv) { int64_t n; int result; if ((result=dao_get_xattr_int64(client_ctx, inode, name, &n)) == 0) { *nv = n; } return result; } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/db/dao/granted_pool.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "func.h" #include "granted_pool.h" int dao_granted_create(FDIRClientContext *client_ctx, const string_t *username, FCFSAuthGrantedPoolInfo *granted) { int result; int64_t inode; AuthFullPath fp; FDIRClientOperFnamePair path; FDIRDEntryInfo dentry; AUTH_SET_GRANTED_POOL_PATH(fp, username, granted->pool_id); AUTH_SET_PATH_OPER_FNAME(path, fp); if ((result=fdir_client_lookup_inode_by_path_ex(client_ctx, &path, LOG_DEBUG, &inode)) != 0) { if (result != ENOENT) { return result; } if ((result=fdir_client_create_dentry(client_ctx, &path, DAO_MODE_FILE, &dentry)) != 0) { return result; } inode = dentry.inode; } if ((result=dao_set_xattr_integer(client_ctx, inode, &AUTH_XTTR_NAME_FDIR, granted->privs.fdir)) != 0) { return result; } if ((result=dao_set_xattr_integer(client_ctx, inode, &AUTH_XTTR_NAME_FSTORE, granted->privs.fstore)) != 0) { return result; } granted->id = inode; return 0; } int dao_granted_remove(FDIRClientContext *client_ctx, const string_t *username, const int64_t pool_id) { const int flags = 0; AuthFullPath fp; FDIRClientOperFnamePair path; AUTH_SET_GRANTED_POOL_PATH(fp, username, pool_id); AUTH_SET_PATH_OPER_FNAME(path, fp); return fdir_client_remove_dentry(client_ctx, &path, flags); } static int dump_to_granted_array(FDIRClientContext *client_ctx, const FDIRClientDentryArray *darray, FCFSAuthGrantedPoolArray *parray) { const FDIRClientDentry *entry; const FDIRClientDentry *end; char pool_id_buff[32]; char *endptr; FCFSAuthGrantedPoolFullInfo *new_gpools; FCFSAuthGrantedPoolFullInfo *gpool; FCFSAuthGrantedPoolInfo *granted; int len; int result; if (darray->count > parray->alloc) { new_gpools = (FCFSAuthGrantedPoolFullInfo *)fc_malloc( sizeof(FCFSAuthGrantedPoolFullInfo) * darray->count); if (new_gpools == NULL) { return ENOMEM; } if (parray->gpools != parray->fixed) { free(parray->gpools); } parray->gpools = new_gpools; parray->alloc = darray->count; } end = darray->entries + darray->count; for (entry=darray->entries, gpool=parray->gpools; entryname.len < sizeof(pool_id_buff)) { len = entry->name.len; } else { len = sizeof(pool_id_buff) - 1; } memcpy(pool_id_buff, entry->name.str, len); *(pool_id_buff + len) = '\0'; granted = &gpool->granted; granted->id = entry->dentry.inode; granted->pool_id = strtoll(pool_id_buff, &endptr, 10); if ((result=dao_get_xattr_int32(client_ctx, granted->id, &AUTH_XTTR_NAME_FDIR, &granted->privs.fdir)) != 0) { return result; } if ((result=dao_get_xattr_int32(client_ctx, granted->id, &AUTH_XTTR_NAME_FSTORE, &granted->privs.fstore)) != 0) { return result; } } parray->count = darray->count; return 0; } int dao_granted_list(FDIRClientContext *client_ctx, const string_t *username, FCFSAuthGrantedPoolArray *granted_array) { const int flags = 0; int result; AuthFullPath fp; FDIRClientOperFnamePair path; FDIRClientDentryArray dentry_array; if ((result=fdir_client_dentry_array_init(&dentry_array)) != 0) { return result; } AUTH_SET_USER_PATH1(fp, username, AUTH_DIR_NAME_GRANTED_STR, AUTH_DIR_NAME_GRANTED_LEN); AUTH_SET_PATH_OPER_FNAME(path, fp); if ((result=fdir_client_list_dentry_by_path(client_ctx, &path, &dentry_array, flags)) != 0) { fdir_client_dentry_array_free(&dentry_array); return result; } result = dump_to_granted_array(client_ctx, &dentry_array, granted_array); fdir_client_dentry_array_free(&dentry_array); return result; } ================================================ FILE: src/auth/server/db/dao/granted_pool.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _AUTH_DAO_GRANTED_POOL_H #define _AUTH_DAO_GRANTED_POOL_H #include "fastcommon/fast_mpool.h" #include "types.h" #ifdef __cplusplus extern "C" { #endif int dao_granted_create(FDIRClientContext *client_ctx, const string_t *username, FCFSAuthGrantedPoolInfo *granted); int dao_granted_remove(FDIRClientContext *client_ctx, const string_t *username, const int64_t pool_id); int dao_granted_list(FDIRClientContext *client_ctx, const string_t *username, FCFSAuthGrantedPoolArray *granted_array); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/db/dao/storage_pool.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "func.h" #include "storage_pool.h" int dao_spool_create(FDIRClientContext *client_ctx, const string_t *username, FCFSAuthStoragePoolInfo *spool) { int result; int64_t inode; AuthFullPath fp; FDIRClientOperFnamePair path; FDIRDEntryInfo dentry; AUTH_SET_USER_PATH2(fp, username, AUTH_DIR_NAME_CREATED_STR, AUTH_DIR_NAME_CREATED_LEN, spool->name); AUTH_SET_PATH_OPER_FNAME(path, fp); if ((result=fdir_client_create_dentry(client_ctx, &path, DAO_MODE_FILE, &dentry)) == 0) { inode = dentry.inode; } else if (result == EEXIST) { if ((result=fdir_client_lookup_inode_by_path_ex(client_ctx, &path, LOG_ERR, &inode)) != 0) { return result; } } else { return result; } if ((result=dao_set_xattr_integer(client_ctx, inode, &AUTH_XTTR_NAME_QUOTA, spool->quota)) != 0) { return result; } if ((result=dao_set_xattr_integer(client_ctx, inode, &AUTH_XTTR_NAME_STATUS, FCFS_AUTH_POOL_STATUS_NORMAL)) != 0) { return result; } spool->id = inode; return 0; } int dao_spool_remove(FDIRClientContext *client_ctx, const int64_t spool_id) { return dao_set_xattr_integer(client_ctx, spool_id, &AUTH_XTTR_NAME_STATUS, FCFS_AUTH_POOL_STATUS_DELETED); } int dao_spool_set_quota(FDIRClientContext *client_ctx, const int64_t spool_id, const int64_t quota) { return dao_set_xattr_integer(client_ctx, spool_id, &AUTH_XTTR_NAME_QUOTA, quota); } static int dump_to_spool_array(FDIRClientContext *client_ctx, struct fast_mpool_man *mpool, const FDIRClientDentryArray *darray, FCFSAuthStoragePoolArray *parray) { const FDIRClientDentry *entry; const FDIRClientDentry *end; FCFSAuthStoragePoolInfo *new_spools; FCFSAuthStoragePoolInfo *spool; int result; if (darray->count > parray->alloc) { new_spools = (FCFSAuthStoragePoolInfo *)fc_malloc( sizeof(FCFSAuthStoragePoolInfo) * darray->count); if (new_spools == NULL) { return ENOMEM; } if (parray->spools != parray->fixed) { free(parray->spools); } parray->spools = new_spools; parray->alloc = darray->count; } end = darray->entries + darray->count; for (entry=darray->entries, spool=parray->spools; entryid = entry->dentry.inode; spool->name = entry->name; if ((result=dao_get_xattr_int64(client_ctx, spool->id, &AUTH_XTTR_NAME_QUOTA, &spool->quota)) != 0) { return result; } if ((result=dao_get_xattr_int32(client_ctx, spool->id, &AUTH_XTTR_NAME_STATUS, &spool->status)) != 0) { return result; } } parray->count = darray->count; return 0; } int dao_spool_list(FDIRClientContext *client_ctx, const string_t *username, struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *spool_array) { const int flags = 0; int result; AuthFullPath fp; FDIRClientOperFnamePair path; FDIRClientDentryArray dentry_array; if ((result=fdir_client_dentry_array_init_ex( &dentry_array, mpool)) != 0) { return result; } AUTH_SET_USER_PATH1(fp, username, AUTH_DIR_NAME_CREATED_STR, AUTH_DIR_NAME_CREATED_LEN); AUTH_SET_PATH_OPER_FNAME(path, fp); if ((result=fdir_client_list_dentry_by_path(client_ctx, &path, &dentry_array, flags)) != 0) { fdir_client_dentry_array_free(&dentry_array); return result; } result = dump_to_spool_array(client_ctx, mpool, &dentry_array, spool_array); fdir_client_dentry_array_free(&dentry_array); return result; } int dao_spool_set_base_path_inode(FDIRClientContext *client_ctx) { AuthFullPath fp; FDIRClientOperFnamePair path; AUTH_SET_BASE_PATH(fp); AUTH_SET_PATH_OPER_FNAME(path, fp); return fdir_client_lookup_inode_by_path(client_ctx, &path, &DAO_BASE_PATH_INODE); } int dao_spool_get_auto_id(FDIRClientContext *client_ctx, int64_t *auto_id) { return dao_get_xattr_int64(client_ctx, DAO_BASE_PATH_INODE, &AUTH_XTTR_NAME_POOL_AUTO_ID, auto_id); } int dao_spool_set_auto_id(FDIRClientContext *client_ctx, const int64_t auto_id) { return dao_set_xattr_integer(client_ctx, DAO_BASE_PATH_INODE, &AUTH_XTTR_NAME_POOL_AUTO_ID, auto_id); } ================================================ FILE: src/auth/server/db/dao/storage_pool.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _AUTH_DAO_STORAGE_POOL_H #define _AUTH_DAO_STORAGE_POOL_H #include "fastcommon/fast_mpool.h" #include "types.h" #ifdef __cplusplus extern "C" { #endif int dao_spool_set_base_path_inode(FDIRClientContext *client_ctx); int dao_spool_get_auto_id(FDIRClientContext *client_ctx, int64_t *auto_id); int dao_spool_set_auto_id(FDIRClientContext *client_ctx, const int64_t auto_id); int dao_spool_create(FDIRClientContext *client_ctx, const string_t *username, FCFSAuthStoragePoolInfo *spool); int dao_spool_remove(FDIRClientContext *client_ctx, const int64_t spool_id); int dao_spool_set_quota(FDIRClientContext *client_ctx, const int64_t spool_id, const int64_t quota); int dao_spool_list(FDIRClientContext *client_ctx, const string_t *username, struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *spool_array); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/db/dao/types.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _AUTH_DAO_TYPES_H #define _AUTH_DAO_TYPES_H #include #include "fastdir/client/fdir_client.h" #include "common/auth_types.h" #define AUTH_NAMESPACE_STR "sys-auth" #define AUTH_NAMESPACE_LEN (sizeof(AUTH_NAMESPACE_STR) - 1) #define AUTH_BASE_PATH_STR "/home" #define AUTH_BASE_PATH_LEN (sizeof(AUTH_BASE_PATH_STR) - 1) typedef struct auth_full_path { char buff[PATH_MAX]; FDIRDEntryFullName fullname; } AuthFullPath; #define AUTH_SET_GRANTED_POOL_PATH(fp, username, granted_id) \ do { \ char *p; \ FC_SET_STRING_EX(fp.fullname.ns, AUTH_NAMESPACE_STR, \ AUTH_NAMESPACE_LEN); \ fp.fullname.path.str = fp.buff; \ p = fp.buff; \ memcpy(p, AUTH_BASE_PATH_STR, AUTH_BASE_PATH_LEN); \ p += AUTH_BASE_PATH_LEN; \ *p++ = '/'; \ memcpy(p, (username)->str, (username)->len); \ p += (username)->len; \ *p++ = '/'; \ memcpy(p, AUTH_DIR_NAME_GRANTED_STR, AUTH_DIR_NAME_GRANTED_LEN); \ p += AUTH_DIR_NAME_GRANTED_LEN; \ *p++ = '/'; \ p += fc_itoa(granted_id, p); \ *p = '\0'; \ fp.fullname.path.len = p - fp.buff; \ } while (0) #define AUTH_SET_USER_PATH2(fp, username, subdir1_str, subdir1_len, subdir2) \ do { \ char *p; \ FC_SET_STRING_EX(fp.fullname.ns, AUTH_NAMESPACE_STR, \ AUTH_NAMESPACE_LEN); \ fp.fullname.path.str = fp.buff; \ p = fp.buff; \ memcpy(p, AUTH_BASE_PATH_STR, AUTH_BASE_PATH_LEN); \ p += AUTH_BASE_PATH_LEN; \ *p++ = '/'; \ memcpy(p, (username)->str, (username)->len); \ p += (username)->len; \ *p++ = '/'; \ memcpy(p, subdir1_str, subdir1_len); \ p += subdir1_len; \ *p++ = '/'; \ memcpy(p, (subdir2).str, (subdir2).len); \ p += (subdir2).len; \ *p = '\0'; \ fp.fullname.path.len = p - fp.buff; \ } while (0) #define AUTH_SET_USER_PATH1(fp, username, subdir1_str, subdir1_len) \ do { \ char *p; \ FC_SET_STRING_EX(fp.fullname.ns, AUTH_NAMESPACE_STR, \ AUTH_NAMESPACE_LEN); \ fp.fullname.path.str = fp.buff; \ p = fp.buff; \ memcpy(p, AUTH_BASE_PATH_STR, AUTH_BASE_PATH_LEN); \ p += AUTH_BASE_PATH_LEN; \ *p++ = '/'; \ memcpy(p, (username)->str, (username)->len); \ p += (username)->len; \ *p++ = '/'; \ memcpy(p, subdir1_str, subdir1_len); \ p += subdir1_len; \ *p = '\0'; \ fp.fullname.path.len = p - fp.buff; \ } while (0) #define AUTH_SET_USER_HOME(fp, username) \ do { \ char *p; \ FC_SET_STRING_EX(fp.fullname.ns, AUTH_NAMESPACE_STR, \ AUTH_NAMESPACE_LEN); \ fp.fullname.path.str = fp.buff; \ p = fp.buff; \ memcpy(p, AUTH_BASE_PATH_STR, AUTH_BASE_PATH_LEN); \ p += AUTH_BASE_PATH_LEN; \ *p++ = '/'; \ memcpy(p, (username)->str, (username)->len); \ p += (username)->len; \ *p = '\0'; \ fp.fullname.path.len = p - fp.buff; \ } while (0) #define AUTH_SET_BASE_PATH(fp) \ do { \ FC_SET_STRING_EX(fp.fullname.ns, AUTH_NAMESPACE_STR, \ AUTH_NAMESPACE_LEN); \ FC_SET_STRING_EX(fp.fullname.path, AUTH_BASE_PATH_STR, \ AUTH_BASE_PATH_LEN); \ } while (0) #define AUTH_SET_PATH_OPER_FNAME(path, fp) \ FDIR_SET_OPERATOR(path.oper, 0, 0, 0, NULL); \ path.fullname = fp.fullname #define AUTH_SET_OPER_INODE_PAIR(oino, _inode) \ FDIR_SET_OPERATOR(oino.oper, 0, 0, 0, NULL); \ oino.inode = _inode #endif ================================================ FILE: src/auth/server/db/dao/user.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "func.h" #include "user.h" static int user_make_subdir(FDIRClientContext *client_ctx, const string_t *username, const char *subdir_str, const int subdir_len, const bool check_exist) { AuthFullPath fp; FDIRClientOperFnamePair path; FDIRDEntryInfo dentry; int result; AUTH_SET_USER_PATH1(fp, username, subdir_str, subdir_len); AUTH_SET_PATH_OPER_FNAME(path, fp); if (check_exist) { result = fdir_client_lookup_inode_by_path_ex(client_ctx, &path, LOG_DEBUG, &dentry.inode); if (result == 0) { return 0; } else if (result != ENOENT) { return result; } } result = fdir_client_create_dentry(client_ctx, &path, DAO_MODE_DIR, &dentry); return result == EEXIST ? 0 : result; } int dao_user_create(FDIRClientContext *client_ctx, FCFSAuthUserInfo *user) { int64_t inode; int result; bool check_exist; AuthFullPath home; FDIRClientOperFnamePair path; FDIRDEntryInfo dentry; AUTH_SET_USER_HOME(home, &user->name); AUTH_SET_PATH_OPER_FNAME(path, home); result = fdir_client_create_dentry(client_ctx, &path, DAO_MODE_DIR, &dentry); if (result == 0) { inode = dentry.inode; check_exist = false; } else if (result == EEXIST) { if ((result=fdir_client_lookup_inode_by_path_ex(client_ctx, &path, LOG_ERR, &inode)) != 0) { return result; } check_exist = true; } else { return result; } if ((result=user_make_subdir(client_ctx, &user->name, AUTH_DIR_NAME_CREATED_STR, AUTH_DIR_NAME_CREATED_LEN, check_exist)) != 0) { return result; } if ((result=user_make_subdir(client_ctx, &user->name, AUTH_DIR_NAME_GRANTED_STR, AUTH_DIR_NAME_GRANTED_LEN, check_exist)) != 0) { return result; } if ((result=dao_set_xattr_string(client_ctx, inode, &AUTH_XTTR_NAME_PASSWD, &user->passwd)) != 0) { return result; } if ((result=dao_set_xattr_integer(client_ctx, inode, &AUTH_XTTR_NAME_PRIV, user->priv)) != 0) { return result; } if ((result=dao_set_xattr_integer(client_ctx, inode, &AUTH_XTTR_NAME_STATUS, FCFS_AUTH_USER_STATUS_NORMAL)) != 0) { return result; } user->id = inode; return 0; } int dao_user_update_priv(FDIRClientContext *client_ctx, const int64_t user_id, const int64_t priv) { return dao_set_xattr_integer(client_ctx, user_id, &AUTH_XTTR_NAME_PRIV, priv); } int dao_user_update_passwd(FDIRClientContext *client_ctx, const int64_t user_id, const string_t *passwd) { return dao_set_xattr_string(client_ctx, user_id, &AUTH_XTTR_NAME_PASSWD, passwd); } int dao_user_remove(FDIRClientContext *client_ctx, const int64_t user_id) { return dao_set_xattr_integer(client_ctx, user_id, &AUTH_XTTR_NAME_STATUS, FCFS_AUTH_USER_STATUS_DELETED); } static int dump_to_user_array(FDIRClientContext *client_ctx, struct fast_mpool_man *mpool, const FDIRClientDentryArray *darray, FCFSAuthUserArray *uarray) { const FDIRClientDentry *entry; const FDIRClientDentry *end; FCFSAuthUserInfo *new_users; FCFSAuthUserInfo *user; char buff[256]; string_t value; int result; if (darray->count > uarray->alloc) { new_users = (FCFSAuthUserInfo *)fc_malloc( sizeof(FCFSAuthUserInfo) * darray->count); if (new_users == NULL) { return ENOMEM; } if (uarray->users != uarray->fixed) { free(uarray->users); } uarray->users = new_users; uarray->alloc = darray->count; } end = darray->entries + darray->count; for (entry=darray->entries, user=uarray->users; entryid = entry->dentry.inode; user->name = entry->name; value.str = buff; if ((result=dao_get_xattr_string(client_ctx, user->id, &AUTH_XTTR_NAME_PASSWD, &value, sizeof(buff))) != 0) { return result; } if ((result=fast_mpool_alloc_string_ex2(mpool, &user->passwd, &value)) != 0) { return result; } if ((result=dao_get_xattr_int64(client_ctx, user->id, &AUTH_XTTR_NAME_PRIV, &user->priv)) != 0) { return result; } if ((result=dao_get_xattr_int32(client_ctx, user->id, &AUTH_XTTR_NAME_STATUS, &user->status)) != 0) { return result; } } uarray->count = darray->count; return 0; } int dao_user_list(FDIRClientContext *client_ctx, struct fast_mpool_man *mpool, FCFSAuthUserArray *user_array) { const int flags = 0; int result; FDIRDEntryInfo dentry; AuthFullPath fp; FDIRClientOperFnamePair root; FDIRClientOperFnamePair path; FDIRClientOperInodePair oino; FDIRClientDentryArray dentry_array; if ((result=fdir_client_dentry_array_init_ex( &dentry_array, mpool)) != 0) { return result; } AUTH_SET_BASE_PATH(fp); AUTH_SET_PATH_OPER_FNAME(path, fp); if ((result=fdir_client_lookup_inode_by_path_ex(client_ctx, &path, LOG_DEBUG, &dentry.inode)) != 0) { if (result != ENOENT) { return result; } FC_SET_STRING_EX(root.fullname.ns, AUTH_NAMESPACE_STR, AUTH_NAMESPACE_LEN); FC_SET_STRING_EX(root.fullname.path, "/", 1); FDIR_SET_OPERATOR(root.oper, 0, 0, 0, NULL); if ((result=fdir_client_create_dentry(client_ctx, &root, DAO_MODE_DIR, &dentry)) != 0) { return result; } if ((result=fdir_client_create_dentry(client_ctx, &path, DAO_MODE_DIR, &dentry)) != 0) { return result; } } AUTH_SET_OPER_INODE_PAIR(oino, dentry.inode); if ((result=fdir_client_list_dentry_by_inode(client_ctx, &DAO_NAMESPACE, &oino, &dentry_array, flags)) != 0) { fdir_client_dentry_array_free(&dentry_array); return result; } result = dump_to_user_array(client_ctx, mpool, &dentry_array, user_array); fdir_client_dentry_array_free(&dentry_array); return result; } ================================================ FILE: src/auth/server/db/dao/user.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _AUTH_DAO_USER_H #define _AUTH_DAO_USER_H #include "fastcommon/fast_mpool.h" #include "types.h" #ifdef __cplusplus extern "C" { #endif int dao_user_create(FDIRClientContext *client_ctx, FCFSAuthUserInfo *user); int dao_user_remove(FDIRClientContext *client_ctx, const int64_t user_id); int dao_user_update_priv(FDIRClientContext *client_ctx, const int64_t user_id, const int64_t priv); int dao_user_update_passwd(FDIRClientContext *client_ctx, const int64_t user_id, const string_t *passwd); int dao_user_list(FDIRClientContext *client_ctx, struct fast_mpool_man *mpool, FCFSAuthUserArray *user_array); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/db/pool_usage_updater.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fastcommon/fc_atomic.h" #include "fastdir/client/fdir_client.h" #include "../server_global.h" #include "auth_db.h" #include "pool_usage_updater.h" typedef struct { bool inited; volatile char running; struct { volatile uint32_t current; volatile uint32_t next; } generation; FDIRClientNamespaceStatArray nss_array; } PoolUsageUpdaterContext; static PoolUsageUpdaterContext updater_ctx = {false, 0, {0, 0}}; #define NSS_ARRAY updater_ctx.nss_array #define IS_SAME_GENERATION (updater_ctx.generation.current == \ updater_ctx.generation.next) static int nss_fetch(ConnectionInfo *conn) { int result; bool is_last; FDIRClientNamespaceStatEntry *entry; FDIRClientNamespaceStatEntry *end; do { if ((result=fdir_client_proto_nss_fetch(&g_fdir_client_vars. client_ctx, conn, &NSS_ARRAY, &is_last)) != 0) { break; } if (NSS_ARRAY.count == 0) { break; } end = NSS_ARRAY.entries + NSS_ARRAY.count; for (entry=NSS_ARRAY.entries; entryns_name.len, entry->ns_name.str, (double)entry->used_bytes / (1024 * 1024 * 1024)); */ adb_spool_set_used_bytes(&entry->ns_name, entry->used_bytes); } } while (!is_last && IS_SAME_GENERATION); return result; } static int pool_usage_refresh(ConnectionInfo *conn) { int i; int result; if ((result=fdir_client_proto_nss_subscribe(&g_fdir_client_vars. client_ctx, conn)) != 0) { return result; } while (SF_G_CONTINUE_FLAG && IS_SAME_GENERATION) { if ((result=nss_fetch(conn)) != 0) { break; } for (i=0; iops.get_master_connection(cm, 0, shared, &result)) == NULL) { sleep(1); continue; } pool_usage_refresh(conn); cm->ops.close_connection(cm, conn); if (IS_SAME_GENERATION) { sleep(1); } else { break; } } FC_ATOMIC_SET(updater_ctx.running, 0); return NULL; } static int pool_usage_updater_init() { int result; if ((result=fdir_client_namespace_stat_array_init(&NSS_ARRAY)) != 0) { return result; } if ((result=fdir_client_simple_init(g_server_global_vars. fdir_client_cfg_filename)) != 0) { return result; } return 0; } int pool_usage_updater_start() { int result; pthread_t tid; if (!updater_ctx.inited) { if ((result=pool_usage_updater_init()) != 0) { return result; } updater_ctx.inited = true; } if (FC_ATOMIC_GET(updater_ctx.running) != 0) { logWarning("file: "__FILE__", line: %d, " "pool_usage_updater thread already running", __LINE__); return 0; } updater_ctx.generation.current = ++updater_ctx.generation.next; return fc_create_thread(&tid, pool_usage_refresh_thread_func, NULL, SF_G_THREAD_STACK_SIZE); } void pool_usage_updater_terminate() { int count; if (!updater_ctx.inited || FC_ATOMIC_GET(updater_ctx.running) == 0) { return; } count = 0; ++updater_ctx.generation.next; while (FC_ATOMIC_GET(updater_ctx.running) != 0) { sleep(1); count++; } logInfo("file: "__FILE__", line: %d, " "pool_usage_updater thread exit after waiting count: %d", __LINE__, count); } ================================================ FILE: src/auth/server/db/pool_usage_updater.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_POOL_USAGE_UPDATER_H #define _FCFS_POOL_USAGE_UPDATER_H #include "../server_types.h" #ifdef __cplusplus extern "C" { #endif int pool_usage_updater_start(); void pool_usage_updater_terminate(); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/fcfs_authd.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fastcommon/shared_func.h" #include "fastcommon/pthread_func.h" #include "fastcommon/process_ctrl.h" #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/sched_thread.h" #include "sf/sf_global.h" #include "sf/sf_func.h" #include "sf/sf_nio.h" #include "sf/sf_service.h" #include "sf/sf_util.h" #include "common/auth_proto.h" #include "server_global.h" #include "server_func.h" #include "session_subscribe.h" #include "cluster_relationship.h" #include "common_handler.h" #include "cluster_handler.h" #include "service_handler.h" static int setup_server_env(const char *config_filename); static bool daemon_mode = true; static const char *config_filename; static char g_pid_filename[MAX_PATH_SIZE]; static int process_cmdline(int argc, char *argv[], bool *continue_flag) { char *action; bool stop; int result; *continue_flag = false; if (argc < 2) { sf_usage(argv[0]); return 1; } config_filename = sf_parse_daemon_mode_and_action(argc, argv, &g_fcfs_auth_global_vars.version, &daemon_mode, &action); if (config_filename == NULL) { return 0; } log_init2(); //log_set_time_precision(&g_log_context, LOG_TIME_PRECISION_USECOND); result = sf_get_base_path_from_conf_file(config_filename); if (result != 0) { log_destroy(); return result; } snprintf(g_pid_filename, sizeof(g_pid_filename), "%s/authd.pid", SF_G_BASE_PATH_STR); stop = false; result = process_action(g_pid_filename, action, &stop); if (result != 0) { if (result == EINVAL) { sf_usage(argv[0]); } log_destroy(); return result; } if (stop) { log_destroy(); return 0; } *continue_flag = true; return 0; } int main(int argc, char *argv[]) { pthread_t schedule_tid; int wait_count; int result; result = process_cmdline(argc, argv, (bool *)&SF_G_CONTINUE_FLAG); if (!SF_G_CONTINUE_FLAG) { return result; } sf_enable_exit_on_oom(); srand(time(NULL)); fast_mblock_manager_init(); //sched_set_delay_params(300, 1024); do { if ((result=setup_server_env(config_filename)) != 0) { break; } if ((result=sf_startup_schedule(&schedule_tid)) != 0) { break; } if ((result=sf_add_slow_log_schedule(&g_server_global_vars. slow_log)) != 0) { break; } if ((result=sf_socket_server()) != 0) { break; } if ((result=sf_socket_server_ex(&CLUSTER_SF_CTX)) != 0) { break; } if ((result=write_to_pid_file(g_pid_filename)) != 0) { break; } if ((result=service_handler_init()) != 0) { break; } if ((result=session_subscribe_init()) != 0) { break; } common_handler_init(); //sched_print_all_entries(); result = sf_service_init_ex(&CLUSTER_SF_CTX, "cluster", cluster_alloc_thread_extra_data, cluster_thread_loop_callback, NULL, sf_proto_set_body_length, NULL, cluster_deal_task, cluster_task_finish_cleanup, cluster_recv_timeout_callback, 1000, sizeof(FCFSAuthProtoHeader), sizeof(AuthServerTaskArg)); if (result != 0) { break; } sf_enable_thread_notify_ex(&CLUSTER_SF_CTX, true); sf_accept_loop_ex(&CLUSTER_SF_CTX, false); result = sf_service_init_ex(&SERVICE_SF_CTX, "service", service_alloc_thread_extra_data, NULL, NULL, sf_proto_set_body_length, NULL, service_deal_task, service_task_finish_cleanup, NULL, 1000, sizeof(FCFSAuthProtoHeader), sizeof(AuthServerTaskArg)); if (result != 0) { break; } sf_enable_thread_notify(true); if ((result=cluster_relationship_init()) != 0) { break; } } while (0); if (result != 0) { lcrit("program exit abnomally"); log_destroy(); return result; } //sched_print_all_entries(); sf_accept_loop(); if (g_schedule_flag) { pthread_kill(schedule_tid, SIGINT); } wait_count = 0; while ((SF_G_ALIVE_THREAD_COUNT != 0) || g_schedule_flag) { fc_sleep_ms(10); if (++wait_count > 1000) { lwarning("waiting timeout, exit!"); break; } } sf_service_destroy(); delete_pid_file(g_pid_filename); logInfo("file: "__FILE__", line: %d, " "program exit normally.\n", __LINE__); log_destroy(); return 0; } static int setup_server_env(const char *config_filename) { int result; sf_set_current_time(); if ((result=server_load_config(config_filename)) != 0) { return result; } if (daemon_mode) { daemon_init(false); } umask(0); result = sf_setup_signal_handler(); log_set_cache(true); return result; } ================================================ FILE: src/auth/server/server_func.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/ini_file_reader.h" #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/local_ip_func.h" #include "sf/sf_global.h" #include "sf/sf_configs.h" #include "sf/sf_service.h" #include "sf/sf_cluster_cfg.h" #include "fastcfs/vote/fcfs_vote_client.h" #include "common/auth_proto.h" #include "common/auth_func.h" #include "common/server_session.h" #include "server_global.h" #include "session_subscribe.h" #include "cluster_info.h" #include "server_func.h" #define SECTION_NAME_FASTDIR "FastDIR" static int server_load_fdir_client_config(IniContext *ini_context, const char *config_filename) { #define ITEM_NAME_FDIR_CLIENT_CFG_FILENAME "client_config_filename" char full_filename[PATH_MAX]; char *client_cfg_filename; int result; if ((client_cfg_filename=iniGetRequiredStrValue(SECTION_NAME_FASTDIR, ITEM_NAME_FDIR_CLIENT_CFG_FILENAME, ini_context)) == NULL) { return ENOENT; } resolve_path(config_filename, client_cfg_filename, full_filename, sizeof(full_filename)); if (!fileExists(full_filename)) { result = errno != 0 ? errno : ENOENT; logError("file: "__FILE__", line: %d, " "config file: %s, item: %s, value: %s, access file %s fail, " "errno: %d, error info: %s", __LINE__, config_filename, ITEM_NAME_FDIR_CLIENT_CFG_FILENAME, client_cfg_filename, full_filename, result, STRERROR(result)); return result; } if ((g_server_global_vars.fdir_client_cfg_filename= fc_strdup(full_filename)) == NULL) { return ENOMEM; } return 0; } static int server_load_admin_generate_config(IniContext *ini_context, const char *config_filename) { #define SECTION_NAME_ADMIN_GENERATE "admin-generate" #define USERNAME_VARIABLE_STR "${username}" #define USERNAME_VARIABLE_LEN (sizeof(USERNAME_VARIABLE_STR) - 1) char *mode; string_t username; string_t secret_key_filename; FilenameString new_filename; char full_filename[PATH_MAX]; int result; mode = iniGetStrValue(SECTION_NAME_ADMIN_GENERATE, "mode", ini_context); if (mode != NULL && strcmp(mode, "always") == 0) { ADMIN_GENERATE_MODE = AUTH_ADMIN_GENERATE_MODE_ALWAYS; } else { ADMIN_GENERATE_MODE = AUTH_ADMIN_GENERATE_MODE_FIRST; } username.str = iniGetStrValue(SECTION_NAME_ADMIN_GENERATE, "username", ini_context); if (username.str == NULL || *username.str == '\0') { username.str = "admin"; } username.len = strlen(username.str); if ((result=fc_check_filename(&username, "username")) != 0) { return result; } secret_key_filename.str = iniGetStrValue(SECTION_NAME_ADMIN_GENERATE, "secret_key_filename", ini_context); if (secret_key_filename.str == NULL || *secret_key_filename.str == '\0') { secret_key_filename.str = "keys/"USERNAME_VARIABLE_STR".key"; } secret_key_filename.len = strlen(secret_key_filename.str); fcfs_auth_replace_filename_with_username(&secret_key_filename, &username, &new_filename); resolve_path(config_filename, FC_FILENAME_STRING_PTR(new_filename), full_filename, sizeof(full_filename)); FC_SET_STRING(secret_key_filename, full_filename); if (!fileExists(full_filename)) { char abs_path[PATH_MAX]; int create_count; getAbsolutePath(full_filename, abs_path, sizeof(abs_path)); if ((result=fc_mkdirs_ex(abs_path, 0755, &create_count)) != 0) { return result; } SF_CHOWN_TO_RUNBY_RETURN_ON_ERROR(abs_path); } ADMIN_GENERATE_BUFF = (char *)fc_malloc(username.len + secret_key_filename.len + 2); if (ADMIN_GENERATE_BUFF == NULL) { return ENOMEM; } ADMIN_GENERATE_USERNAME.str = ADMIN_GENERATE_BUFF; memcpy(ADMIN_GENERATE_USERNAME.str, username.str, username.len + 1); ADMIN_GENERATE_USERNAME.len = username.len; ADMIN_GENERATE_KEY_FILENAME.str = ADMIN_GENERATE_BUFF + username.len + 1; memcpy(ADMIN_GENERATE_KEY_FILENAME.str, secret_key_filename.str, secret_key_filename.len + 1); ADMIN_GENERATE_KEY_FILENAME.len = secret_key_filename.len; return 0; } static int server_load_pool_generate_config(IniContext *ini_context, const char *config_filename) { #define SECTION_NAME_POOL_GENERATE "pool-generate" char *name_template; int result; AUTO_ID_INITIAL = iniGetInt64Value(SECTION_NAME_POOL_GENERATE, "auto_id_initial", ini_context, 1); name_template = iniGetStrValue(SECTION_NAME_POOL_GENERATE, "pool_name_template", ini_context); if (name_template == NULL || *name_template == '\0') { name_template = FCFS_AUTH_AUTO_ID_TAG_STR; } else if (strstr(name_template, FCFS_AUTH_AUTO_ID_TAG_STR) == NULL) { logError("file: "__FILE__", line: %d, " "config file: %s, pool_name_template: %s is invalid, " "must contains %s", __LINE__, config_filename, name_template, FCFS_AUTH_AUTO_ID_TAG_STR); return EINVAL; } POOL_NAME_TEMPLATE.len = strlen(name_template); POOL_NAME_TEMPLATE.str = (char *)fc_malloc(POOL_NAME_TEMPLATE.len + 1); if (POOL_NAME_TEMPLATE.str == NULL) { return ENOMEM; } memcpy(POOL_NAME_TEMPLATE.str, name_template, POOL_NAME_TEMPLATE.len + 1); if ((result=fc_check_filename(&POOL_NAME_TEMPLATE, "pool_name_template")) != 0) { return result; } return 0; } static void log_cluster_server_config() { FastBuffer buffer; if (fast_buffer_init1(&buffer, 1024) != 0) { return; } fc_server_to_config_string(&CLUSTER_SERVER_CONFIG, &buffer); log_it1(LOG_INFO, buffer.data, buffer.length); fast_buffer_destroy(&buffer); fc_server_to_log(&CLUSTER_SERVER_CONFIG); } static void server_log_configs() { char sz_server_config[512]; char sz_global_config[512]; char sz_slowlog_config[256]; char sz_service_config[256]; char sz_session_config[512]; sf_global_config_to_string(sz_global_config, sizeof(sz_global_config)); sf_slow_log_config_to_string(&SLOW_LOG_CFG, "slow-log", sz_slowlog_config, sizeof(sz_slowlog_config)); sf_context_config_to_string(&SERVICE_SF_CTX, sz_service_config, sizeof(sz_service_config)); server_session_cfg_to_string(sz_session_config, sizeof(sz_session_config)); snprintf(sz_server_config, sizeof(sz_server_config), "admin-generate {mode: %s, username: %s, " "secret_key_filename: %s}, pool-generate: " "{auto_id_initial: %"PRId64", pool_name_template: %s}, " "master-election {quorum: %s, vote_node_enabled: %d, " "master_lost_timeout: %ds, max_wait_time: %ds}", (ADMIN_GENERATE_MODE == AUTH_ADMIN_GENERATE_MODE_FIRST ? "first" : "always"), ADMIN_GENERATE_USERNAME.str, ADMIN_GENERATE_KEY_FILENAME.str, AUTO_ID_INITIAL, POOL_NAME_TEMPLATE.str, sf_get_election_quorum_caption( MASTER_ELECTION_QUORUM), VOTE_NODE_ENABLED, ELECTION_MASTER_LOST_TIMEOUT, ELECTION_MAX_WAIT_TIME); logInfo("FCFSAuth V%d.%d.%d, %s, %s, service: {%s}, %s", g_fcfs_auth_global_vars.version.major, g_fcfs_auth_global_vars.version.minor, g_fcfs_auth_global_vars.version.patch, sz_global_config, sz_slowlog_config, sz_service_config, sz_server_config); logInfo("FastDIR {client_config_filename: %s, " "pool_usage_refresh_interval: %d}, %s", g_server_global_vars.fdir_client_cfg_filename, POOL_USAGE_REFRESH_INTERVAL, sz_session_config); log_local_host_ip_addrs(); log_cluster_server_config(); } static int load_master_election_config(const char *cluster_filename) { IniContext ini_context; IniFullContext ini_ctx; int result; if ((result=iniLoadFromFile(cluster_filename, &ini_context)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, cluster_filename, result); return result; } FAST_INI_SET_FULL_CTX_EX(ini_ctx, cluster_filename, "master-election", &ini_context); ELECTION_MASTER_LOST_TIMEOUT = iniGetIntCorrectValue( &ini_ctx, "master_lost_timeout", 3, 1, 30); ELECTION_MAX_WAIT_TIME = iniGetIntCorrectValue( &ini_ctx, "max_wait_time", 5, 1, 300); if ((result=sf_load_election_quorum_config(&MASTER_ELECTION_QUORUM, &ini_ctx)) == 0) { result = fcfs_vote_client_init_for_server( &ini_ctx, &VOTE_NODE_ENABLED); } iniFreeContext(&ini_context); return result; } static int load_cluster_config(IniFullContext *ini_ctx, char *full_cluster_filename) { int result; if ((result=sf_load_cluster_config_ex(&CLUSTER_CONFIG, ini_ctx, FCFS_AUTH_DEFAULT_CLUSTER_PORT, full_cluster_filename, PATH_MAX)) != 0) { return result; } if ((result=load_master_election_config(full_cluster_filename)) != 0) { return result; } if ((result=cluster_info_init(full_cluster_filename)) != 0) { return result; } sf_set_address_family_by_ip(&SERVICE_SF_CTX, &SERVICE_GROUP_ADDRESS_ARRAY( CLUSTER_MYSELF_PTR->server)); sf_set_address_family_by_ip(&CLUSTER_SF_CTX, &CLUSTER_GROUP_ADDRESS_ARRAY( CLUSTER_MYSELF_PTR->server)); return 0; } int server_load_config(const char *filename) { const int fixed_buffer_size = 0; const int task_buffer_extra_size = 0; IniContext ini_context; IniFullContext ini_ctx; char full_cluster_filename[PATH_MAX]; int result; if ((result=iniLoadFromFile(filename, &ini_context)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, filename, result); return result; } if ((result=sf_load_config("fcfs_authd", fc_comm_type_sock, filename, &ini_context, "service", FCFS_AUTH_DEFAULT_SERVICE_PORT, FCFS_AUTH_DEFAULT_SERVICE_PORT, fixed_buffer_size, task_buffer_extra_size)) != 0) { return result; } if ((result=sf_load_context_from_config(&CLUSTER_SF_CTX, fc_comm_type_sock, filename, &ini_context, "cluster", FCFS_AUTH_DEFAULT_CLUSTER_PORT, FCFS_AUTH_DEFAULT_CLUSTER_PORT, fixed_buffer_size, task_buffer_extra_size)) != 0) { return result; } FAST_INI_SET_FULL_CTX_EX(ini_ctx, filename, NULL, &ini_context); if ((result=load_cluster_config(&ini_ctx, full_cluster_filename)) != 0) { return result; } ini_ctx.section_name = "session"; if ((result=server_session_init_ex(&ini_ctx, sizeof(ServerSessionFields), &g_server_session_callbacks)) != 0) { return result; } if ((result=server_load_admin_generate_config( &ini_context, filename)) != 0) { return result; } if ((result=server_load_pool_generate_config( &ini_context, filename)) != 0) { return result; } if ((result=server_load_fdir_client_config( &ini_context, filename)) != 0) { return result; } POOL_USAGE_REFRESH_INTERVAL = iniGetIntValue(SECTION_NAME_FASTDIR, "pool_usage_refresh_interval", &ini_context, 3); if (POOL_USAGE_REFRESH_INTERVAL <= 0) { POOL_USAGE_REFRESH_INTERVAL = 1; } if ((result=sf_load_slow_log_config(filename, &ini_context, &SLOW_LOG_CTX, &SLOW_LOG_CFG)) != 0) { return result; } iniFreeContext(&ini_context); load_local_host_ip_addrs(); server_log_configs(); return 0; } ================================================ FILE: src/auth/server/server_func.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_AUTH_SERVER_FUNC_H #define _FCFS_AUTH_SERVER_FUNC_H #include "server_types.h" #ifdef __cplusplus extern "C" { #endif int server_load_config(const char *filename); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/server_global.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "server_global.h" AuthServerGlobalVars g_server_global_vars; ================================================ FILE: src/auth/server/server_global.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _SERVER_GLOBAL_H #define _SERVER_GLOBAL_H #include "fastcommon/common_define.h" #include "sf/sf_global.h" #include "../common/auth_global.h" #include "server_types.h" #define AUTH_ADMIN_GENERATE_MODE_FIRST 'F' #define AUTH_ADMIN_GENERATE_MODE_ALWAYS 'A' typedef struct server_global_vars { struct { FCFSAuthClusterServerInfo *master; FCFSAuthClusterServerInfo *myself; FCFSAuthClusterServerInfo *next_master; SFClusterConfig config; FCFSAuthClusterServerArray server_array; struct { SFElectionQuorum quorum; bool vote_node_enabled; int master_lost_timeout; int max_wait_time; } master_election; SFContext sf_context; //for cluster communication } cluster; struct { int mode; char *buff; //space for username and secret_key_filename string_t username; string_t secret_key_filename; } admin_generate; struct { int64_t auto_id_initial; string_t pool_name_template; } pool_generate; char *fdir_client_cfg_filename; int pool_usage_refresh_interval; SFSlowLogContext slow_log; } AuthServerGlobalVars; #define MASTER_ELECTION_QUORUM g_server_global_vars.cluster. \ master_election.quorum #define VOTE_NODE_ENABLED g_server_global_vars.cluster. \ master_election.vote_node_enabled #define ELECTION_MASTER_LOST_TIMEOUT g_server_global_vars.cluster. \ master_election.master_lost_timeout #define ELECTION_MAX_WAIT_TIME g_server_global_vars.cluster. \ master_election.max_wait_time #define CLUSTER_CONFIG g_server_global_vars.cluster.config #define CLUSTER_SERVER_CONFIG CLUSTER_CONFIG.server_cfg #define CLUSTER_NEXT_MASTER g_server_global_vars.cluster.next_master #define CLUSTER_MYSELF_PTR g_server_global_vars.cluster.myself #define CLUSTER_MASTER_PTR g_server_global_vars.cluster.master #define CLUSTER_MASTER_ATOM_PTR ((FCFSAuthClusterServerInfo *) \ __sync_add_and_fetch(&CLUSTER_MASTER_PTR, 0)) #define MYSELF_IS_MASTER (CLUSTER_MASTER_ATOM_PTR == CLUSTER_MYSELF_PTR) #define CLUSTER_SERVER_ARRAY g_server_global_vars.cluster.server_array #define CLUSTER_MY_SERVER_ID CLUSTER_MYSELF_PTR->server->id #define SERVICE_SF_CTX g_sf_context #define CLUSTER_SF_CTX g_server_global_vars.cluster.sf_context #define CLUSTER_CONNECT_TIMEOUT CLUSTER_SF_CTX.net_buffer_cfg.connect_timeout #define CLUSTER_NETWORK_TIMEOUT CLUSTER_SF_CTX.net_buffer_cfg.network_timeout #define ADMIN_GENERATE g_server_global_vars.admin_generate #define ADMIN_GENERATE_MODE ADMIN_GENERATE.mode #define ADMIN_GENERATE_BUFF ADMIN_GENERATE.buff #define ADMIN_GENERATE_USERNAME ADMIN_GENERATE.username #define ADMIN_GENERATE_KEY_FILENAME ADMIN_GENERATE.secret_key_filename #define POOL_GENERATE g_server_global_vars.pool_generate #define AUTO_ID_INITIAL POOL_GENERATE.auto_id_initial #define POOL_NAME_TEMPLATE POOL_GENERATE.pool_name_template #define POOL_USAGE_REFRESH_INTERVAL g_server_global_vars. \ pool_usage_refresh_interval #define SLOW_LOG g_server_global_vars.slow_log #define SLOW_LOG_CFG SLOW_LOG.cfg #define SLOW_LOG_CTX SLOW_LOG.ctx #define CLUSTER_GROUP_INDEX g_server_global_vars.cluster.config.cluster_group_index #define SERVICE_GROUP_INDEX g_server_global_vars.cluster.config.service_group_index #define CLUSTER_GROUP_ADDRESS_ARRAY(server) \ (server)->group_addrs[CLUSTER_GROUP_INDEX].address_array #define SERVICE_GROUP_ADDRESS_ARRAY(server) \ (server)->group_addrs[SERVICE_GROUP_INDEX].address_array #define CLUSTER_GROUP_ADDRESS_FIRST_PTR(server) \ (*(server)->group_addrs[CLUSTER_GROUP_INDEX].address_array.addrs) #define SERVICE_GROUP_ADDRESS_FIRST_PTR(server) \ (*(server)->group_addrs[SERVICE_GROUP_INDEX].address_array.addrs) #define CLUSTER_GROUP_ADDRESS_FIRST_IP(server) \ CLUSTER_GROUP_ADDRESS_FIRST_PTR(server)->conn.ip_addr #define CLUSTER_GROUP_ADDRESS_FIRST_PORT(server) \ CLUSTER_GROUP_ADDRESS_FIRST_PTR(server)->conn.port #define SERVICE_GROUP_ADDRESS_FIRST_IP(server) \ SERVICE_GROUP_ADDRESS_FIRST_PTR(server)->conn.ip_addr #define SERVICE_GROUP_ADDRESS_FIRST_PORT(server) \ SERVICE_GROUP_ADDRESS_FIRST_PTR(server)->conn.port #define CLUSTER_CONFIG_SIGN_BUF g_server_global_vars.cluster.config.md5_digest #ifdef __cplusplus extern "C" { #endif extern AuthServerGlobalVars g_server_global_vars; #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/server_types.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _SERVER_TYPES_H #define _SERVER_TYPES_H #include "fastcommon/uniq_skiplist.h" #include "fastcommon/fc_queue.h" #include "fastcommon/fast_mblock.h" #include "fastcommon/locked_list.h" #include "sf/sf_types.h" #include "common/auth_types.h" #include "common/server_session.h" #define TASK_STATUS_CONTINUE 12345 #define TASK_ARG ((AuthServerTaskArg *)task->arg) #define TASK_CTX TASK_ARG->context #define SESSION_ENTRY TASK_CTX.shared.service.session #define SESSION_FIELDS ((ServerSessionFields *)SESSION_ENTRY->fields) #define SESSION_DBUSER SESSION_FIELDS->dbuser #define SESSION_DBPOOL SESSION_FIELDS->dbpool #define SESSION_USER SESSION_FIELDS->dbuser->user #define SESSION_SUBSCRIBER TASK_CTX.shared.cluster.subscriber #define REQUEST TASK_CTX.common.request #define RESPONSE TASK_CTX.common.response #define RESPONSE_STATUS RESPONSE.header.status #define REQUEST_STATUS REQUEST.header.status #define SERVER_TASK_TYPE TASK_CTX.task_type #define CLUSTER_PEER TASK_CTX.shared.cluster.peer #define AUTH_SERVER_TASK_TYPE_SESSION 1 #define AUTH_SERVER_TASK_TYPE_SUBSCRIBE 2 #define AUTH_SERVER_TASK_TYPE_RELATIONSHIP 3 #define SERVER_CTX ((AuthServerContext *)task->thread_data->arg) #define SESSION_HOLDER SERVER_CTX->service.session_holder typedef struct fcfs_auth_cluster_server_info { FCServerInfo *server; volatile bool is_online; } FCFSAuthClusterServerInfo; typedef struct fcfs_auth_cluster_server_array { FCFSAuthClusterServerInfo *servers; int count; } FCFSAuthClusterServerArray; struct db_user_info; struct db_storage_pool_info; typedef struct server_session_fields { bool publish; const struct db_user_info *dbuser; const struct db_storage_pool_info *dbpool; FCFSAuthSPoolPriviledges pool_privs; struct fc_list_head dlink; //for publish list } ServerSessionFields; typedef struct server_session_subscriber { struct fc_queue queue; //element: ServerSessionSubscribeEntry struct fc_list_head dlink; //for global subscriber's chain struct { volatile int in_queue; //if in the subscriber queue of NIO thread struct fast_task_info *task; struct fc_list_head dlink; //for nio thread subscriber's chain } nio; } ServerSessionSubscriber; typedef struct server_task_arg { struct { SFCommonTaskContext common; int task_type; union { struct { ServerSessionEntry *session; } service; union { ServerSessionSubscriber *subscriber; FCFSAuthClusterServerInfo *peer; //the peer server in the cluster } cluster; } shared; } context; } AuthServerTaskArg; typedef struct auth_server_context { void *dao_ctx; union { struct { FCLockedList subscribers; //element: ServerSessionSubscriber } cluster; struct { ServerSessionEntry *session_holder; } service; }; } AuthServerContext; #endif ================================================ FILE: src/auth/server/service_handler.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //service_handler.c #include #include #include #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/shared_func.h" #include "fastcommon/pthread_func.h" #include "fastcommon/sched_thread.h" #include "fastcommon/ioevent_loop.h" #include "sf/sf_util.h" #include "sf/sf_func.h" #include "sf/sf_nio.h" #include "sf/sf_service.h" #include "sf/sf_global.h" #include "common/auth_proto.h" #include "common/auth_func.h" #include "db/dao/dao.h" #include "db/auth_db.h" #include "server_global.h" #include "server_func.h" #include "common_handler.h" #include "service_handler.h" int service_handler_init() { g_fcfs_auth_client_vars.need_load_passwd = false; return 0; } int service_handler_destroy() { return 0; } void service_task_finish_cleanup(struct fast_task_info *task) { switch (SERVER_TASK_TYPE) { case AUTH_SERVER_TASK_TYPE_SESSION: if (SESSION_ENTRY != NULL) { server_session_delete(SESSION_ENTRY->id_info.id); SESSION_ENTRY = NULL; } SERVER_TASK_TYPE = SF_SERVER_TASK_TYPE_NONE; break; default: break; } sf_task_finish_clean_up(task); } static int check_user_priv(struct fast_task_info *task) { int64_t the_priv; switch (REQUEST.header.cmd) { case SF_PROTO_ACTIVE_TEST_REQ: case FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ: case FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_REQ: case FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_REQ: case FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_REQ: return 0; case FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ: case FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ: case FCFS_AUTH_SERVICE_PROTO_USER_LIST_REQ: case FCFS_AUTH_SERVICE_PROTO_USER_GRANT_REQ: case FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_REQ: the_priv = FCFS_AUTH_USER_PRIV_USER_MANAGE; break; case FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ: case FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ: case FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ: case FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ: case FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ: case FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ: the_priv = FCFS_AUTH_USER_PRIV_CREATE_POOL; break; case FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ: the_priv = 0; break; default: RESPONSE.error.length = sprintf(RESPONSE.error.message, "unkown cmd: %d", REQUEST.header.cmd); return -EINVAL; } if (SESSION_ENTRY == NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "please login first!"); return EPERM; } if ((the_priv != 0) && ((SESSION_USER.priv & the_priv) == 0)) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "permission denied"); return EPERM; } return 0; } static int service_deal_user_login(struct fast_task_info *task) { FCFSAuthProtoUserLoginReq *req; FCFSAuthProtoUserLoginResp *resp; ServerSessionFields *fields; FCFSAuthProtoNameInfo *proto_poolname; string_t username; string_t passwd; string_t poolname; int flags; int result; if ((result=server_check_min_body_length(sizeof( FCFSAuthProtoUserLoginReq) + 1)) != 0) { return result; } req = (FCFSAuthProtoUserLoginReq *)REQUEST.body; FC_SET_STRING_EX(username, req->up_pair.username.str, req->up_pair.username.len); FC_SET_STRING_EX(passwd, req->up_pair.passwd, FCFS_AUTH_PASSWD_LEN); proto_poolname = (FCFSAuthProtoNameInfo *)(req-> up_pair.username.str + username.len); FC_SET_STRING_EX(poolname, proto_poolname->str, proto_poolname->len); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoUserLoginReq) + username.len + poolname.len)) != 0) { return result; } if (SESSION_ENTRY != NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "user already logined"); TASK_CTX.common.log_level = LOG_NOTHING; return -EEXIST; } if (SERVER_TASK_TYPE != SF_SERVER_TASK_TYPE_NONE) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "task type: %d != %d", SERVER_TASK_TYPE, SF_SERVER_TASK_TYPE_NONE); return -EEXIST; } fields = (ServerSessionFields *)(SESSION_HOLDER->fields); if (!((fields->dbuser=adb_user_get(SERVER_CTX, &username)) != NULL && fc_string_equal(&fields->dbuser->user.passwd, &passwd))) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "user login fail, username or password not correct"); return EPERM; } if (poolname.len > 0) { if ((fields->dbpool=adb_spool_global_get(&poolname)) == NULL) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "pool %.*s not exist", poolname.len, poolname.str); return ENOENT; } if ((result=adb_granted_privs_get(SERVER_CTX, fields->dbuser, fields->dbpool, &fields->pool_privs)) != 0) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "pool %.*s, not priviledge", poolname.len, poolname.str); return EPERM; } } else { fields->dbpool = NULL; fields->pool_privs.fdir = 0; fields->pool_privs.fstore = 0; } flags = req->flags; fields->publish = (flags & FCFS_AUTH_SESSION_FLAGS_PUBLISH) != 0; SESSION_HOLDER->id_info.id = 0; if ((SESSION_ENTRY=server_session_add(SESSION_HOLDER, fields->publish)) == NULL) { return ENOMEM; } SERVER_TASK_TYPE = AUTH_SERVER_TASK_TYPE_SESSION; /* logInfo("session id: %"PRId64", pool: %.*s, pool_privs " "{fdir: %d, fstore: %d}", SESSION_ENTRY->id_info.id, poolname.len, poolname.str, fields->pool_privs.fdir, fields->pool_privs.fstore); */ resp = (FCFSAuthProtoUserLoginResp *)SF_PROTO_SEND_BODY(task); long2buff(SESSION_ENTRY->id_info.id, resp->session_id); RESPONSE.header.body_len = sizeof(FCFSAuthProtoUserLoginResp); RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_RESP; TASK_ARG->context.common.response_done = true; return 0; } static int service_deal_user_create(struct fast_task_info *task) { FCFSAuthProtoUserCreateReq *req; FCFSAuthUserInfo user; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoUserCreateReq) + 1, sizeof(FCFSAuthProtoUserCreateReq) + NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoUserCreateReq *)REQUEST.body; FC_SET_STRING_EX(user.name, req->up_pair.username.str, req->up_pair.username.len); FC_SET_STRING_EX(user.passwd, req->up_pair.passwd, FCFS_AUTH_PASSWD_LEN); user.priv = buff2long(req->priv); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoUserCreateReq) + user.name.len)) != 0) { return result; } if ((result=fc_check_filename_ex(&user.name, "username", RESPONSE.error.message, &RESPONSE.error.length, sizeof(RESPONSE.error.message))) != 0) { return result; } user.status = FCFS_AUTH_USER_STATUS_NORMAL; return adb_user_create(SERVER_CTX, &user); } static int service_deal_user_passwd(struct fast_task_info *task) { FCFSAuthProtoUserPasswdReq *req; string_t username; string_t passwd; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoUserPasswdReq) + 1, sizeof(FCFSAuthProtoUserPasswdReq) + NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoUserPasswdReq *)REQUEST.body; FC_SET_STRING_EX(username, req->up_pair.username.str, req->up_pair.username.len); FC_SET_STRING_EX(passwd, req->up_pair.passwd, FCFS_AUTH_PASSWD_LEN); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoUserPasswdReq) + username.len)) != 0) { return result; } return adb_user_update_passwd(SERVER_CTX, &username, &passwd); } static int service_parse_limit(struct fast_task_info *task, const SFProtoLimitInfo *limit_proto, SFListLimitInfo *limit_info) { sf_proto_extract_limit(limit_proto, limit_info); if (limit_info->count <= 0) { limit_info->count = 1024; } return 0; } static int service_deal_user_list(struct fast_task_info *task) { FCFSAuthUserArray array; const DBUserInfo *dbuser; const FCFSAuthUserInfo *user; const FCFSAuthUserInfo *end; string_t username; SFListLimitInfo limit; FCFSAuthProtoUserListReq *req; FCFSAuthProtoListRespHeader *resp_header; FCFSAuthProtoUserListRespBodyPart *body_part; char *p; char *buff_end; bool truncated; int len; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoUserListReq), sizeof(FCFSAuthProtoUserListReq) + NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoUserListReq *)REQUEST.body; FC_SET_STRING_EX(username, req->username.str, req->username.len); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoUserListReq) + username.len)) != 0) { return result; } if (username.len > 0) { if ((dbuser=adb_user_get(SERVER_CTX, &username)) == NULL) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "username:%.*s not exist", username.len, username.str); return ENOENT; } fcfs_auth_user_init_array(&array); array.users[0] = dbuser->user; array.count = 1; limit.count = 100; } else { if ((result=service_parse_limit(task, &req->limit, &limit)) != 0) { return result; } fcfs_auth_user_init_array(&array); if ((result=adb_user_list(SERVER_CTX, &limit, &array)) != 0) { fcfs_auth_user_free_array(&array); return result; } } resp_header = (FCFSAuthProtoListRespHeader *)SF_PROTO_SEND_BODY(task); p = (char *)(resp_header + 1); buff_end = SF_SEND_BUFF_END(task); end = array.users + array.count; truncated = false; for (user=array.users; username.len; if (p + len > buff_end) { truncated = true; break; } body_part = (FCFSAuthProtoUserListRespBodyPart *)p; long2buff(user->priv, body_part->priv); body_part->username.len = user->name.len; memcpy(body_part->username.str, user->name.str, user->name.len); p += len; } resp_header->is_last = (array.count < limit.count) && !truncated; int2buff(user - array.users, resp_header->count); RESPONSE.header.body_len = p - SF_PROTO_SEND_BODY(task); RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_LIST_RESP; TASK_ARG->context.common.response_done = true; fcfs_auth_user_free_array(&array); return 0; } static int service_deal_user_grant(struct fast_task_info *task) { FCFSAuthProtoUserGrantReq *req; string_t username; int64_t priv; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoUserGrantReq) + 1, sizeof(FCFSAuthProtoUserGrantReq) + NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoUserGrantReq *)REQUEST.body; FC_SET_STRING_EX(username, req->username.str, req->username.len); priv = buff2long(req->priv); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoUserGrantReq) + username.len)) != 0) { return result; } return adb_user_update_priv(SERVER_CTX, &username, priv); } static int service_deal_user_remove(struct fast_task_info *task) { FCFSAuthProtoUserRemoveReq *req; string_t username; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoUserRemoveReq) + 1, sizeof(FCFSAuthProtoUserRemoveReq) + NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoUserRemoveReq *)REQUEST.body; FC_SET_STRING_EX(username, req->username.str, req->username.len); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoUserRemoveReq) + username.len)) != 0) { return result; } return adb_user_remove(SERVER_CTX, &username); } static int spool_create_by_template(struct fast_task_info *task, const string_t *username, const string_t *pool_name, FCFSAuthStoragePoolInfo *spool, const bool dryrun) { int result; int name_size; struct { char buff[32]; string_t tag; struct { int64_t n; string_t s; } value; } auto_id; name_size = spool->name.len; FC_SET_STRING_EX(auto_id.tag, FCFS_AUTH_AUTO_ID_TAG_STR, FCFS_AUTH_AUTO_ID_TAG_LEN); auto_id.value.n = adb_spool_get_auto_id(SERVER_CTX); auto_id.value.s.str = auto_id.buff; do { auto_id.value.s.len = fc_itoa(auto_id.value.n, auto_id.value.s.str); if ((result=str_replace(pool_name, &auto_id.tag, &auto_id.value.s, &spool->name, name_size)) != 0) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "invalid pool name: %.*s\n", pool_name->len, pool_name->str); return result; } if (dryrun) { result = adb_spool_access(SERVER_CTX, &spool->name); if (result == 0) { //pool exist result = adb_spool_next_auto_id(SERVER_CTX, &auto_id.value.n); continue; } else if (result == ENOENT) { result = 0; } break; } result = adb_spool_create(SERVER_CTX, username, spool); if (result == 0) { result = adb_spool_inc_auto_id(SERVER_CTX); break; } else if (result == EEXIST) { result = adb_spool_next_auto_id(SERVER_CTX, &auto_id.value.n); } else { break; } } while (result == 0); return result; } static int service_deal_spool_create(struct fast_task_info *task) { FCFSAuthProtoSPoolCreateReq *req; FCFSAuthProtoSPoolCreateResp *resp; char name_buff[NAME_MAX + 1]; string_t username; string_t pool_name; FCFSAuthStoragePoolInfo spool; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolCreateReq), sizeof(FCFSAuthProtoSPoolCreateReq) + NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoSPoolCreateReq *)REQUEST.body; FC_SET_STRING_EX(spool.name, req->poolname.str, req->poolname.len); spool.quota = buff2long(req->quota); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoSPoolCreateReq) + spool.name.len)) != 0) { return result; } if (spool.name.len == 0) { spool.name = POOL_NAME_TEMPLATE; } else if ((result=fc_check_filename_ex(&spool.name, "pool name", RESPONSE.error.message, &RESPONSE.error.length, sizeof(RESPONSE.error.message))) != 0) { return result; } spool.status = FCFS_AUTH_POOL_STATUS_NORMAL; username = SESSION_USER.name; if (spool.name.len >= FCFS_AUTH_AUTO_ID_TAG_LEN && strstr(spool.name.str, FCFS_AUTH_AUTO_ID_TAG_STR) != NULL) { pool_name = spool.name; spool.name.str = name_buff; spool.name.len = sizeof(name_buff); result = spool_create_by_template(task, &username, &pool_name, &spool, req->dryrun); } else { if (req->dryrun) { result = adb_spool_access(SERVER_CTX, &spool.name); if (result == 0) { result = EEXIST; } else if (result == ENOENT) { result = 0; } } else { result = adb_spool_create(SERVER_CTX, &username, &spool); } } if (result == 0) { resp = (FCFSAuthProtoSPoolCreateResp *)SF_PROTO_SEND_BODY(task); resp->poolname.len = spool.name.len; memcpy(resp->poolname.str, spool.name.str, spool.name.len); RESPONSE.header.body_len = sizeof(*resp) + spool.name.len; RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_RESP; TASK_ARG->context.common.response_done = true; } else if (result == EEXIST) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "pool %.*s already exist", spool.name.len, spool.name.str); } return result; } #define SPOOL_GET(username, poolname) \ do { \ if ((spool=adb_spool_get(SERVER_CTX, &username, &poolname)) == NULL) { \ RESPONSE.error.length = sprintf(RESPONSE.error.message, \ "user: %.*s, pool: %.*s not exist", username.len, \ username.str, poolname.len, poolname.str); \ return ENOENT; \ } \ } while (0) static int service_deal_spool_list(struct fast_task_info *task) { FCFSAuthStoragePoolArray array; const FCFSAuthStoragePoolInfo *spool; const FCFSAuthStoragePoolInfo *end; string_t username; string_t poolname; SFListLimitInfo limit; FCFSAuthProtoSPoolListReq *req; FCFSAuthProtoListRespHeader *resp_header; FCFSAuthProtoSPoolListRespBodyPart *body_part; char *p; char *buff_end; bool truncated; int len; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolListReq), sizeof(FCFSAuthProtoSPoolListReq) + NAME_MAX * 2)) != 0) { return result; } req = (FCFSAuthProtoSPoolListReq *)REQUEST.body; fcfs_auth_parse_user_pool_pair(&req->up_pair, &username, &poolname); if ((result=server_expect_body_length(sizeof(FCFSAuthProtoSPoolListReq) + username.len + poolname.len)) != 0) { return result; } if (username.len == 0) { username = SESSION_USER.name; } else if (fc_string_equal(&username, &SESSION_USER.name)) { //do nothing } else { if ((SESSION_USER.priv & FCFS_AUTH_USER_PRIV_USER_MANAGE) == 0) { if (poolname.len > 0) { FCFSAuthGrantedPoolFullInfo gf; SPOOL_GET(username, poolname); if ((result=adb_granted_full_get(SERVER_CTX, &SESSION_USER. name, spool->id, &gf)) != 0) { result = EPERM; } } else { result = EPERM; } if (result == EPERM) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "permission denied"); return result; } } } if (poolname.len > 0) { SPOOL_GET(username, poolname); fcfs_auth_spool_init_array(&array); array.spools[0] = *spool; array.count = 1; limit.count = 100; } else { if ((result=service_parse_limit(task, &req->limit, &limit)) != 0) { return result; } fcfs_auth_spool_init_array(&array); if ((result=adb_spool_list(SERVER_CTX, &username, &limit, &array)) != 0) { fcfs_auth_spool_free_array(&array); return result; } } resp_header = (FCFSAuthProtoListRespHeader *)SF_PROTO_SEND_BODY(task); p = (char *)(resp_header + 1); end = array.spools + array.count; buff_end = SF_SEND_BUFF_END(task); truncated = false; for (spool=array.spools; spoolname.len; if (p + len > buff_end) { truncated = true; break; } body_part = (FCFSAuthProtoSPoolListRespBodyPart *)p; long2buff(spool->quota, body_part->quota); body_part->poolname.len = spool->name.len; memcpy(body_part->poolname.str, spool->name.str, spool->name.len); p += len; } resp_header->is_last = (array.count < limit.count) && !truncated; int2buff(spool - array.spools, resp_header->count); RESPONSE.header.body_len = p - SF_PROTO_SEND_BODY(task); RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_RESP; TASK_ARG->context.common.response_done = true; fcfs_auth_spool_free_array(&array); return 0; } static int service_deal_spool_remove(struct fast_task_info *task) { FCFSAuthProtoSPoolRemoveReq *req; string_t username; string_t poolname; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolRemoveReq) + 1, sizeof(FCFSAuthProtoSPoolRemoveReq) + NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoSPoolRemoveReq *)REQUEST.body; FC_SET_STRING_EX(poolname, req->poolname.str, req->poolname.len); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoSPoolRemoveReq) + poolname.len)) != 0) { return result; } username = SESSION_USER.name; return adb_spool_remove(SERVER_CTX, &username, &poolname); } static int service_deal_spool_set_quota(struct fast_task_info *task) { FCFSAuthProtoSPoolSetQuotaReq *req; string_t username; string_t poolname; int64_t quota; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolSetQuotaReq) + 1, sizeof(FCFSAuthProtoSPoolSetQuotaReq) + NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoSPoolSetQuotaReq *)REQUEST.body; FC_SET_STRING_EX(poolname, req->poolname.str, req->poolname.len); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoSPoolSetQuotaReq) + poolname.len)) != 0) { return result; } quota = buff2long(req->quota); username = SESSION_USER.name; return adb_spool_set_quota(SERVER_CTX, &username, &poolname, quota); } static int service_deal_spool_get_quota(struct fast_task_info *task) { FCFSAuthProtoSPoolGetQuotaReq *req; FCFSAuthProtoSPoolGetQuotaResp *resp; string_t poolname; int64_t quota; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolGetQuotaReq) + 1, sizeof(FCFSAuthProtoSPoolGetQuotaReq) + NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoSPoolGetQuotaReq *)REQUEST.body; FC_SET_STRING_EX(poolname, req->poolname.str, req->poolname.len); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoSPoolGetQuotaReq) + poolname.len)) != 0) { return result; } if ((result=adb_spool_get_quota(SERVER_CTX, &poolname, "a)) == 0) { resp = (FCFSAuthProtoSPoolGetQuotaResp *)SF_PROTO_SEND_BODY(task); long2buff(quota, resp->quota); RESPONSE.header.body_len = sizeof(*resp); TASK_ARG->context.common.response_done = true; } return result; } static int service_deal_spool_grant(struct fast_task_info *task) { FCFSAuthProtoSPoolGrantReq *req; struct { string_t my; string_t dest; } usernames; string_t poolname; const FCFSAuthStoragePoolInfo *spool; FCFSAuthGrantedPoolInfo granted; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolGrantReq) + 2, sizeof(FCFSAuthProtoSPoolGrantReq) + 2 * NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoSPoolGrantReq *)REQUEST.body; fcfs_auth_parse_user_pool_pair(&req->up_pair, &usernames.dest, &poolname); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoSPoolGrantReq) + usernames.dest.len + poolname.len)) != 0) { return result; } usernames.my = SESSION_USER.name; if ((spool=adb_spool_get(SERVER_CTX, &usernames.my, &poolname)) == NULL) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "user: %.*s, pool: %.*s not exist", usernames.my.len, usernames.my.str, poolname.len, poolname.str); return ENOENT; } granted.pool_id = spool->id; granted.privs.fdir = buff2int(req->privs.fdir); granted.privs.fstore = buff2int(req->privs.fstore); if ((granted.privs.fdir == FCFS_AUTH_POOL_ACCESS_NONE) && (granted.privs.fstore == FCFS_AUTH_POOL_ACCESS_NONE)) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "no access priviledges to grant"); return EINVAL; } /* logInfo("sizeof(FCFSAuthProtoSPoolGrantReq): %d, dest: %.*s(%d), " "poolname: %.*s, pool_id: %"PRId64, (int)sizeof(FCFSAuthProtoSPoolGrantReq), usernames.dest.len, usernames.dest.str, usernames.dest.len, poolname.len, poolname.str, granted.pool_id); */ return adb_granted_create(SERVER_CTX, &usernames.dest, &granted); } static int service_deal_spool_withdraw(struct fast_task_info *task) { FCFSAuthProtoSPoolWithdrawReq *req; struct { string_t my; string_t dest; } usernames; string_t poolname; const FCFSAuthStoragePoolInfo *spool; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolWithdrawReq) + 2, sizeof(FCFSAuthProtoSPoolWithdrawReq) + 2 * NAME_MAX)) != 0) { return result; } req = (FCFSAuthProtoSPoolWithdrawReq *)REQUEST.body; fcfs_auth_parse_user_pool_pair(&req->up_pair, &usernames.dest, &poolname); if ((result=server_expect_body_length( sizeof(FCFSAuthProtoSPoolWithdrawReq) + usernames.dest.len + poolname.len)) != 0) { return result; } usernames.my = SESSION_USER.name; if ((spool=adb_spool_get(SERVER_CTX, &usernames.my, &poolname)) == NULL) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "user: %.*s, pool: %.*s not exist", usernames.my.len, usernames.my.str, poolname.len, poolname.str); return ENOENT; } return adb_granted_remove(SERVER_CTX, &usernames.dest, spool->id); } static int service_deal_gpool_list(struct fast_task_info *task) { FCFSAuthGrantedPoolArray array; const FCFSAuthGrantedPoolFullInfo *gpool; const FCFSAuthGrantedPoolFullInfo *end; char pname_buff[NAME_MAX]; string_t username; string_t poolname; SFListLimitInfo limit; FCFSAuthProtoGPoolListReq *req; FCFSAuthProtoListRespHeader *resp_header; FCFSAuthProtoGPoolListRespBodyPart *body_part; char *p; char *buff_end; bool truncated; int count; int len; int result; if ((result=server_check_body_length(sizeof(FCFSAuthProtoGPoolListReq), sizeof(FCFSAuthProtoGPoolListReq) + NAME_MAX * 2)) != 0) { return result; } req = (FCFSAuthProtoGPoolListReq *)REQUEST.body; fcfs_auth_parse_user_pool_pair(&req->up_pair, &username, &poolname); if ((result=server_expect_body_length(sizeof(FCFSAuthProtoGPoolListReq) + username.len + poolname.len)) != 0) { return result; } if (username.len == 0) { username = SESSION_USER.name; } else { if ((SESSION_USER.priv & FCFS_AUTH_USER_PRIV_USER_MANAGE) == 0) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "permission denied"); return EPERM; } } if (poolname.len > 0) { memcpy(pname_buff, poolname.str, poolname.len); poolname.str = pname_buff; limit.offset = 0; limit.count = 1000 * 1000; //to list all } else { if ((result=service_parse_limit(task, &req->limit, &limit)) != 0) { return result; } } fcfs_auth_granted_init_array(&array); if ((result=adb_granted_list(SERVER_CTX, &username, &limit, &array)) != 0) { fcfs_auth_granted_free_array(&array); return result; } count = 0; resp_header = (FCFSAuthProtoListRespHeader *)SF_PROTO_SEND_BODY(task); p = (char *)(resp_header + 1); buff_end = SF_SEND_BUFF_END(task); end = array.gpools + array.count; truncated = false; for (gpool=array.gpools; gpoolpool_name, &poolname)) { len = sizeof(FCFSAuthProtoGPoolListRespBodyPart) + gpool->username.len + gpool->pool_name.len; if (p + len > buff_end) { truncated = true; break; } body_part = (FCFSAuthProtoGPoolListRespBodyPart *)p; int2buff(gpool->granted.privs.fdir, body_part->privs.fdir); int2buff(gpool->granted.privs.fstore, body_part->privs.fstore); fcfs_auth_pack_user_pool_pair(&gpool->username, &gpool->pool_name, &body_part->up_pair); p += len; count++; } } resp_header->is_last = (array.count < limit.count) && !truncated; int2buff(count, resp_header->count); RESPONSE.header.body_len = p - SF_PROTO_SEND_BODY(task); RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_RESP; TASK_ARG->context.common.response_done = true; fcfs_auth_granted_free_array(&array); return 0; } static int service_deal_cluster_stat(struct fast_task_info *task) { int result; FCFSAuthProtoClusterStatRespBodyHeader *body_header; FCFSAuthProtoClusterStatRespBodyPart *body_part; FCFSAuthClusterServerInfo *cs; FCFSAuthClusterServerInfo *send; if ((result=server_expect_body_length(0)) != 0) { return result; } body_header = (FCFSAuthProtoClusterStatRespBodyHeader *) SF_PROTO_SEND_BODY(task); body_part = (FCFSAuthProtoClusterStatRespBodyPart *)(body_header + 1); int2buff(CLUSTER_SERVER_ARRAY.count, body_header->count); send = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count; for (cs=CLUSTER_SERVER_ARRAY.servers; csserver->id, body_part->server_id); body_part->is_online = ((cs == CLUSTER_MASTER_ATOM_PTR || cs->is_online) ? 1 : 0); body_part->is_master = (cs == CLUSTER_MASTER_ATOM_PTR ? 1 : 0); fc_safe_strcpy(body_part->ip_addr, SERVICE_GROUP_ADDRESS_FIRST_IP( cs->server)); short2buff(SERVICE_GROUP_ADDRESS_FIRST_PORT(cs->server), body_part->port); } RESPONSE.header.body_len = (char *)body_part - SF_PROTO_SEND_BODY(task); RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_RESP; TASK_CTX.common.response_done = true; return 0; } static int service_process(struct fast_task_info *task) { int result; if (!MYSELF_IS_MASTER) { if (!(REQUEST.header.cmd == FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ || REQUEST.header.cmd == SF_SERVICE_PROTO_GET_LEADER_REQ || REQUEST.header.cmd == SF_PROTO_ACTIVE_TEST_REQ)) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am not master"); return SF_RETRIABLE_ERROR_NOT_MASTER; } } switch (REQUEST.header.cmd) { case SF_PROTO_ACTIVE_TEST_REQ: RESPONSE.header.cmd = SF_PROTO_ACTIVE_TEST_RESP; return sf_proto_deal_active_test(task, &REQUEST, &RESPONSE); case FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_REQ: return service_deal_user_login(task); case FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ: RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_CREATE_RESP; return service_deal_user_create(task); case FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ: RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_RESP; return service_deal_user_passwd(task); case FCFS_AUTH_SERVICE_PROTO_USER_LIST_REQ: return service_deal_user_list(task); case FCFS_AUTH_SERVICE_PROTO_USER_GRANT_REQ: RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_GRANT_RESP; return service_deal_user_grant(task); case FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_REQ: RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_RESP; return service_deal_user_remove(task); case FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ: return service_deal_spool_create(task); case FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ: return service_deal_spool_list(task); case FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ: RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_RESP; return service_deal_spool_remove(task); case FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ: RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_RESP; return service_deal_spool_set_quota(task); case FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_REQ: RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_RESP; return service_deal_spool_get_quota(task); case FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ: RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_RESP; return service_deal_spool_grant(task); case FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ: RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_RESP; return service_deal_spool_withdraw(task); case FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ: return service_deal_gpool_list(task); case FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_REQ: return service_deal_cluster_stat(task); case FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ: if ((result=fcfs_auth_deal_get_master(task, SERVICE_GROUP_INDEX)) == 0) { RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_GET_MASTER_RESP; } return result; case SF_SERVICE_PROTO_GET_LEADER_REQ: if ((result=fcfs_auth_deal_get_master(task, SERVICE_GROUP_INDEX)) == 0) { RESPONSE.header.cmd = SF_SERVICE_PROTO_GET_LEADER_RESP; } return result; default: RESPONSE.error.length = sprintf( RESPONSE.error.message, "unkown cmd: %d", REQUEST.header.cmd); return -EINVAL; } } int service_deal_task(struct fast_task_info *task, const int stage) { int result; /* logInfo("file: "__FILE__", line: %d, " "task: %p, sock: %d, nio stage: %d, continue: %d, " "cmd: %d (%s)", __LINE__, task, task->event.fd, stage, stage == SF_NIO_STAGE_CONTINUE, ((FCFSAuthProtoHeader *)task->recv.ptr->data)->cmd, fdir_get_cmd_caption(((FCFSAuthProtoHeader *) task->recv.ptr->data)->cmd)); */ if (stage == SF_NIO_STAGE_CONTINUE) { if (task->continue_callback != NULL) { result = task->continue_callback(task); } else { result = RESPONSE_STATUS; if (result == TASK_STATUS_CONTINUE) { logError("file: "__FILE__", line: %d, " "unexpect status: %d", __LINE__, result); result = EBUSY; } } } else { sf_proto_init_task_context(task, &TASK_CTX.common); if ((result=check_user_priv(task)) == 0) { result = service_process(task); } } if (result == TASK_STATUS_CONTINUE) { return 0; } else { RESPONSE_STATUS = result; return sf_proto_deal_task_done(task, "service", &TASK_CTX.common); } } static int create_session_for_access_fdir(ServerSessionEntry *session_holder, char *session_id) { const bool persistent = true; ServerSessionFields *fields; ServerSessionEntry *session; fields = (ServerSessionFields *)(session_holder->fields); fields->publish = false; fields->pool_privs.fdir = FCFS_AUTH_POOL_ACCESS_ALL; fields->pool_privs.fstore = FCFS_AUTH_POOL_ACCESS_ALL; session_holder->id_info.id = 0; if ((session=server_session_add_ex(session_holder, fields->publish, persistent)) == NULL) { return ENOMEM; } long2buff(session->id_info.id, session_id); return 0; } void *service_alloc_thread_extra_data(const int thread_index) { short alloc_size; short dao_context_size; static bool dao_session_inited = false; static char dao_session_id[FCFS_AUTH_SESSION_ID_LEN]; AuthServerContext *server_context; dao_context_size = dao_get_context_size(); alloc_size = sizeof(AuthServerContext) + dao_context_size + sizeof(ServerSessionEntry) + sizeof(ServerSessionFields); server_context = (AuthServerContext *)fc_malloc(alloc_size); if (server_context == NULL) { return NULL; } memset(server_context, 0, alloc_size); server_context->dao_ctx = (void *)(server_context + 1); server_context->service.session_holder = (ServerSessionEntry *)(((char *) server_context->dao_ctx) + dao_context_size); server_context->service.session_holder->fields = server_context->service.session_holder + 1; if (!dao_session_inited) { dao_session_inited = true; if (create_session_for_access_fdir(server_context-> service.session_holder, dao_session_id) != 0) { return NULL; } } if (dao_init_context(thread_index, server_context-> dao_ctx, dao_session_id) != 0) { sf_terminate_myself(); return NULL; } return server_context; } ================================================ FILE: src/auth/server/service_handler.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //service_handler.h #ifndef FCFS_AUTH_SERVER_HANDLER_H #define FCFS_AUTH_SERVER_HANDLER_H #include #include #include #include "fastcommon/fast_task_queue.h" #include "server_types.h" #ifdef __cplusplus extern "C" { #endif int service_handler_init(); int service_handler_destroy(); int service_deal_task(struct fast_task_info *task, const int stage); void service_task_finish_cleanup(struct fast_task_info *task); void *service_alloc_thread_extra_data(const int thread_index); //int service_thread_loop(struct nio_thread_data *thread_data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/auth/server/session_subscribe.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include "fastcommon/shared_func.h" #include "fastcommon/sched_thread.h" #include "fastcommon/logger.h" #include "fastcommon/locked_list.h" #include "sf/sf_global.h" #include "sf/sf_service.h" #include "db/auth_db.h" #include "server_global.h" #include "cluster_handler.h" #include "server_session.h" #include "session_subscribe.h" typedef struct server_session_subscribe_context { struct fast_mblock_man entry_allocator; //element: ServerSessionSubscribeEntry struct fast_mblock_man subs_allocator; //element: ServerSessionSubscriber /* queue element: ServerSessionSubscriber */ FCLockedList subscribers; /* queue element: ServerSessionFields */ FCLockedList sessions; //publish session only } ServerSessionSubscribeContext; static ServerSessionSubscribeContext subscribe_ctx; static void set_session_subscribe_entry(const ServerSessionEntry *session, ServerSessionSubscribeEntry *subs_entry) { ServerSessionFields *fields; subs_entry->operation = FCFS_AUTH_SESSION_OP_TYPE_CREATE; subs_entry->session_id = session->id_info.id; fields = (ServerSessionFields *)(session->fields); subs_entry->fields.user.id = fields->dbuser->user.id; subs_entry->fields.user.priv = fields->dbuser->user.priv; if (fields->dbpool != NULL) { subs_entry->fields.pool.id = fields->dbpool->pool.id; subs_entry->fields.pool.available = FCFS_AUTH_POOL_AVAILABLE( fields->dbpool->pool); subs_entry->fields.pool.privs = fields->pool_privs; } else { subs_entry->fields.pool.id = 0; subs_entry->fields.pool.available = 0; subs_entry->fields.pool.privs.fdir = 0; subs_entry->fields.pool.privs.fstore = 0; } } static int publish_entry_to_all_subscribers( const ServerSessionSubscribeEntry *src_entry) { int result; bool notify; ServerSessionSubscriber *subscriber; ServerSessionSubscribeEntry *subs_entry; result = 0; PTHREAD_MUTEX_LOCK(&subscribe_ctx.subscribers.lock); fc_list_for_each_entry(subscriber, &subscribe_ctx. subscribers.head, dlink) { subs_entry = (ServerSessionSubscribeEntry *) fast_mblock_alloc_object(&subscribe_ctx.entry_allocator); if (subs_entry == NULL) { result = ENOMEM; break; } *subs_entry = *src_entry; fc_queue_push_ex(&subscriber->queue, subs_entry, ¬ify); if (notify) { cluster_subscriber_queue_push(subscriber); } } PTHREAD_MUTEX_UNLOCK(&subscribe_ctx.subscribers.lock); return result; } static inline int publish_session_to_all_subscribers( const ServerSessionEntry *session) { ServerSessionSubscribeEntry subs_entry; set_session_subscribe_entry(session, &subs_entry); return publish_entry_to_all_subscribers(&subs_entry); } static void server_session_add_callback(ServerSessionEntry *session) { ServerSessionFields *fields; fields = (ServerSessionFields *)(session->fields); if (fields->publish) { locked_list_add_tail(&fields->dlink, &subscribe_ctx.sessions); publish_session_to_all_subscribers(session); } } static void server_session_del_callback(ServerSessionEntry *session) { ServerSessionFields *fields; ServerSessionSubscribeEntry subs_entry; fields = (ServerSessionFields *)(session->fields); if (fields->publish && MYSELF_IS_MASTER) { locked_list_del(&fields->dlink, &subscribe_ctx.sessions); memset(&subs_entry, 0, sizeof(subs_entry)); subs_entry.operation = FCFS_AUTH_SESSION_OP_TYPE_REMOVE; subs_entry.session_id = session->id_info.id; publish_entry_to_all_subscribers(&subs_entry); } } ServerSessionCallbacks g_server_session_callbacks = { server_session_add_callback, server_session_del_callback }; typedef struct { int64_t user_id; int64_t pool_id; const FCFSAuthSPoolPriviledges *pool_privs; } ServerSessionMatchParam; static void publish_matched_server_sessions( const ServerSessionMatchParam *mparam) { ServerSessionFields *fields; ServerSessionEntry *session; int matched_count; if (fc_list_empty(&subscribe_ctx.subscribers.head)) { return; } matched_count = 0; PTHREAD_MUTEX_LOCK(&subscribe_ctx.sessions.lock); fc_list_for_each_entry(fields, &subscribe_ctx.sessions.head, dlink) { session = FCFS_AUTH_SERVER_SESSION_BY_FIELDS(fields); if (mparam->user_id != 0) { if (fields->dbuser->user.id != mparam->user_id) { continue; } } if (mparam->pool_id != 0) { if (!(fields->dbpool != NULL && fields->dbpool-> pool.id == mparam->pool_id)) { continue; } } if (mparam->pool_privs != NULL) { fields->pool_privs = *mparam->pool_privs; } ++matched_count; publish_session_to_all_subscribers(session); } PTHREAD_MUTEX_UNLOCK(&subscribe_ctx.sessions.lock); if (matched_count > 0) { } } static void user_priv_change_callback(const int64_t user_id, const int64_t new_priv) { ServerSessionMatchParam mparam; mparam.user_id = user_id; mparam.pool_id = 0; mparam.pool_privs = NULL; publish_matched_server_sessions(&mparam); } static void pool_priv_change_callback(const int64_t user_id, const int64_t pool_id, const FCFSAuthSPoolPriviledges *pool_privs) { ServerSessionMatchParam mparam; mparam.user_id = user_id; mparam.pool_id = pool_id; mparam.pool_privs = pool_privs; publish_matched_server_sessions(&mparam); } static void pool_quota_avail_change_callback( const int64_t pool_id, const bool available) { ServerSessionMatchParam mparam; mparam.user_id = 0; mparam.pool_id = pool_id; mparam.pool_privs = NULL; publish_matched_server_sessions(&mparam); } DBPrivChangeCallbacks g_db_priv_change_callbacks = { user_priv_change_callback, pool_priv_change_callback, pool_quota_avail_change_callback }; static int push_all_sessions_to_queue(ServerSessionSubscriber *subscriber) { int result; ServerSessionFields *fields; ServerSessionEntry *session; ServerSessionSubscribeEntry *head; ServerSessionSubscribeEntry *tail; ServerSessionSubscribeEntry *subs_entry; result = 0; head = tail = NULL; PTHREAD_MUTEX_LOCK(&subscribe_ctx.sessions.lock); fc_list_for_each_entry(fields, &subscribe_ctx.sessions.head, dlink) { session = FCFS_AUTH_SERVER_SESSION_BY_FIELDS(fields); if (!session->id_info.fields.publish) { continue; } subs_entry = (ServerSessionSubscribeEntry *) fast_mblock_alloc_object(&subscribe_ctx.entry_allocator); if (subs_entry == NULL) { result = ENOMEM; break; } set_session_subscribe_entry(session, subs_entry); if (head == NULL) { head = subs_entry; } else { tail->next = subs_entry; } tail = subs_entry; } PTHREAD_MUTEX_UNLOCK(&subscribe_ctx.sessions.lock); if (result == 0 && head != NULL) { struct fc_queue_info qinfo; tail->next = NULL; qinfo.head = head; qinfo.tail = tail; fc_queue_push_queue_to_tail_silence(&subscriber->queue, &qinfo); } return result; } int subscriber_alloc_init_func(ServerSessionSubscriber *subscriber, void *args) { FC_INIT_LIST_HEAD(&subscriber->dlink); return fc_queue_init(&subscriber->queue, (long) (&((ServerSessionSubscribeEntry *)NULL)->next)); } int session_subscribe_init() { int result; if ((result=fast_mblock_init_ex1(&subscribe_ctx.entry_allocator, "subscribe_entry", sizeof(ServerSessionSubscribeEntry), 8 * 1024, 0, NULL, NULL, true)) != 0) { return result; } if ((result=fast_mblock_init_ex1(&subscribe_ctx.subs_allocator, "session_subscriber", sizeof(ServerSessionSubscriber), 1024, 0, (fast_mblock_object_init_func) subscriber_alloc_init_func, NULL, true)) != 0) { return result; } if ((result=locked_list_init(&subscribe_ctx.subscribers)) != 0) { return result; } if ((result=locked_list_init(&subscribe_ctx.sessions)) != 0) { return result; } return 0; } void session_subscribe_destroy() { } ServerSessionSubscriber *session_subscribe_alloc() { return (ServerSessionSubscriber *)fast_mblock_alloc_object( &subscribe_ctx.subs_allocator); } void session_subscribe_register(ServerSessionSubscriber *subscriber) { push_all_sessions_to_queue(subscriber); locked_list_add_tail(&subscriber->dlink, &subscribe_ctx.subscribers); } void session_subscribe_unregister(ServerSessionSubscriber *subscriber) { locked_list_del(&subscriber->dlink, &subscribe_ctx.subscribers); } void session_subscribe_free_entries(ServerSessionSubscribeEntry *entry) { ServerSessionSubscribeEntry *current; while (entry != NULL) { current = entry; entry = entry->next; fast_mblock_free_object(&subscribe_ctx.entry_allocator, current); } } void session_subscribe_release(ServerSessionSubscriber *subscriber) { ServerSessionSubscribeEntry *entry; entry = (ServerSessionSubscribeEntry *)fc_queue_try_pop_all( &subscriber->queue); if (entry != NULL) { session_subscribe_free_entries(entry); } fast_mblock_free_object(&subscribe_ctx.subs_allocator, subscriber); } void session_subscribe_clear_session() { ServerSessionFields *fields; ServerSessionFields *tmp; PTHREAD_MUTEX_LOCK(&subscribe_ctx.sessions.lock); fc_list_for_each_entry_safe(fields, tmp, &subscribe_ctx.sessions.head, dlink) { fc_list_del_init(&fields->dlink); } PTHREAD_MUTEX_UNLOCK(&subscribe_ctx.sessions.lock); } ================================================ FILE: src/auth/server/session_subscribe.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _AUTH_SESSION_SUBSCRIBE_H #define _AUTH_SESSION_SUBSCRIBE_H #include "fastcommon/fast_mblock.h" #include "server_types.h" typedef struct auth_session_subscribe_entry { char operation; uint64_t session_id; SessionSyncedFields fields; struct auth_session_subscribe_entry *next; //for fc_queue } ServerSessionSubscribeEntry; #ifdef __cplusplus extern "C" { #endif extern ServerSessionCallbacks g_server_session_callbacks; int session_subscribe_init(); void session_subscribe_destroy(); void session_subscribe_free_entries(ServerSessionSubscribeEntry *entry); ServerSessionSubscriber *session_subscribe_alloc(); void session_subscribe_register(ServerSessionSubscriber *subscriber); void session_subscribe_unregister(ServerSessionSubscriber *subscriber); void session_subscribe_release(ServerSessionSubscriber *subscriber); void session_subscribe_clear_session(); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/common/fcfs_global.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fcfs_global.h" FCFSGlobalVars g_fcfs_global_vars = { {5, 5, 0} }; ================================================ FILE: src/common/fcfs_global.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_GLOBAL_H #define _FCFS_GLOBAL_H #include "fastcommon/common_define.h" typedef struct fcfs_global_vars { Version version; } FCFSGlobalVars; #ifdef __cplusplus extern "C" { #endif extern FCFSGlobalVars g_fcfs_global_vars; #ifdef __cplusplus } #endif #endif ================================================ FILE: src/fuse/Makefile.in ================================================ .SUFFIXES: .c .o .lo COMPILE = $(CC) $(CFLAGS) INC_PATH = -I.. -I../common -I../include LIB_PATH = -L../api $(LIBS) -lfuse3 -lfcfsapi -lfdirclient -lfsapi \ -lfsclient -lfcfsauthclient -lfastcommon -lserverframe TARGET_PATH = $(TARGET_PREFIX)/bin STATIC_OBJS = global.o getgroups.o groups_htable.o fuse_wrapper.o ALL_PRGS = fcfs_fused all: $(STATIC_OBJS) $(ALL_PRGS) $(ALL_PRGS): $(STATIC_OBJS) .o: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c.o: $(COMPILE) -c -o $@ $< $(INC_PATH) install: mkdir -p $(TARGET_PATH) cp -f $(ALL_PRGS) $(TARGET_PATH) clean: rm -f $(STATIC_OBJS) $(ALL_PRGS) ================================================ FILE: src/fuse/fcfs_fused.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/process_ctrl.h" #include "fastcommon/sched_thread.h" #include "sf/sf_global.h" #include "sf/sf_service.h" #include "sf/sf_func.h" #include "sf/sf_util.h" #include "sf/idempotency/client/client_channel.h" #include "sf/idempotency/client/receipt_handler.h" #include "global.h" #include "fuse_wrapper.h" //#define FDIR_MBLOCK_CHECK 1 #ifdef FDIR_MBLOCK_CHECK static int setup_mblock_stat_task(); #endif static struct fuse_session *fuse_instance; static int setup_server_env(const char *config_filename); static struct fuse_session *create_fuse_session(char *argv0, struct fuse_lowlevel_ops *ops); static void fuse_exit_handler(int sig); static bool daemon_mode = true; static bool need_delete_pid_file = false; static const char *config_filename; static char g_pid_filename[MAX_PATH_SIZE]; static int parse_cmd_options(int argc, char *argv[]) { int ch; const struct option longopts[] = { {"user", required_argument, NULL, 'u'}, {"key", required_argument, NULL, 'k'}, {"namespace", required_argument, NULL, 'n'}, {"mountpoint", required_argument, NULL, 'm'}, {"base-path", required_argument, NULL, 'b'}, SF_COMMON_LONG_OPTIONS, {NULL, 0, NULL, 0} }; while ((ch = getopt_long(argc, argv, SF_COMMON_OPT_STRING"u:k:n:m:b:", longopts, NULL)) != -1) { switch (ch) { case 'u': FC_SET_STRING(g_fcfs_auth_client_vars.client_ctx. auth_cfg.username, optarg); break; case 'k': FC_SET_STRING(g_fcfs_auth_client_vars.client_ctx. auth_cfg.secret_key_filename, optarg); break; case 'n': g_fuse_global_vars.nsmp.ns = optarg; break; case 'm': g_fuse_global_vars.nsmp.mountpoint = optarg; break; case 'b': sf_set_global_base_path(optarg); break; case '?': return EINVAL; default: break; } } return 0; } #define OPTION_NAME_USER_STR "user" #define OPTION_NAME_USER_LEN (sizeof(OPTION_NAME_USER_STR) - 1) #define OPTION_NAME_KEY_STR "key" #define OPTION_NAME_KEY_LEN (sizeof(OPTION_NAME_KEY_STR) - 1) #define OPTION_NAME_NAMESPACE_STR "namespace" #define OPTION_NAME_NAMESPACE_LEN (sizeof(OPTION_NAME_NAMESPACE_STR) - 1) #define OPTION_NAME_MOUNTPOINT_STR "mountpoint" #define OPTION_NAME_MOUNTPOINT_LEN (sizeof(OPTION_NAME_MOUNTPOINT_STR) - 1) #define OPTION_NAME_BASE_PATH_STR "base-path" #define OPTION_NAME_BASE_PATH_LEN (sizeof(OPTION_NAME_BASE_PATH_STR) - 1) static int process_cmdline(int argc, char *argv[], bool *continue_flag) { const SFCMDOption other_options[] = { {{OPTION_NAME_USER_STR, OPTION_NAME_USER_LEN}, 'u', true, "-u | --user: the username"}, {{OPTION_NAME_KEY_STR, OPTION_NAME_KEY_LEN}, 'k', true, "-k | --key: the secret key filename"}, {{OPTION_NAME_NAMESPACE_STR, OPTION_NAME_NAMESPACE_LEN}, 'n', true, "-n | --namespace: the FastDIR namespace"}, {{OPTION_NAME_MOUNTPOINT_STR, OPTION_NAME_MOUNTPOINT_LEN}, 'm', true, "-m | --mountpoint: the mountpoint"}, {{OPTION_NAME_BASE_PATH_STR, OPTION_NAME_BASE_PATH_LEN}, 'b', true, "-b | --base-path: the base path"}, {{NULL, 0}, 0, false, NULL} }; char *action; bool stop; int result; *continue_flag = false; if (argc < 2) { sf_usage_ex(argv[0], other_options); return 1; } config_filename = sf_parse_daemon_mode_and_action_ex( argc, argv, &g_fcfs_global_vars.version, &daemon_mode, &action, "start", other_options); if (config_filename == NULL) { return 0; } log_init2(); //log_set_time_precision(&g_log_context, LOG_TIME_PRECISION_MSECOND); if ((result=parse_cmd_options(argc, argv)) != 0) { return result; } if (!SF_G_BASE_PATH_INITED) { result = sf_get_base_path_from_conf_file(config_filename); if (result != 0) { log_destroy(); return result; } SF_G_BASE_PATH_INITED = true; } fc_get_full_filename(SF_G_BASE_PATH_STR, SF_G_BASE_PATH_LEN, "fused.pid", sizeof("fused.pid") - 1, g_pid_filename); stop = false; result = process_action(g_pid_filename, action, &stop); if (result != 0) { if (result == EINVAL) { sf_usage_ex(argv[0], other_options); } log_destroy(); return result; } if (stop) { log_destroy(); return 0; } *continue_flag = true; return result; } static void sig_usr1_handler(int sig) { ConnectionPoolStat fdir_stat; ConnectionPoolStat fs_stat; double fdir_avg_servers; double fs_avg_servers; conn_pool_stat(&g_fdir_client_vars.client_ctx.cm.cpool, &fdir_stat); if (fdir_stat.bucket_used > 0) { fdir_avg_servers = (double)fdir_stat.server_count / (double)fdir_stat.bucket_used; } else { fdir_avg_servers = 1.00; } logInfo("fdir connection pool stat {htable capacity: %d, " "server count: %d, used-buckets: %d, " "servers / used-buckets: %.2f, " "connections {total: %d, used: %d, free: %d}", fdir_stat.htable_capacity, fdir_stat.server_count, fdir_stat.bucket_used, fdir_avg_servers, fdir_stat.connection.total_count, (fdir_stat.connection.total_count - fdir_stat.connection.free_count), fdir_stat.connection.free_count); conn_pool_stat(&g_fs_client_vars.client_ctx.cm.cpool, &fs_stat); if (fs_stat.bucket_used > 0) { fs_avg_servers = (double)fs_stat.server_count / (double)fs_stat.bucket_used; } else { fs_avg_servers = 1.00; } logInfo("fstore connection pool stat {htable capacity: %d, " "server count: %d, used-buckets: %d, " "servers / used-buckets: %.2f, " "connections {total: %d, used: %d, free: %d}", fs_stat.htable_capacity, fs_stat.server_count, fs_stat.bucket_used, fs_avg_servers, fs_stat.connection.total_count, (fs_stat.connection.total_count - fs_stat.connection.free_count), fs_stat.connection.free_count); } static int setup_user_signal_handler() { struct sigaction act; memset(&act, 0, sizeof(act)); sigemptyset(&act.sa_mask); act.sa_handler = sig_usr1_handler; if (sigaction(SIGUSR1, &act, NULL) < 0 || sigaction(SIGUSR2, &act, NULL) < 0) { logCrit("file: "__FILE__", line: %d, " "call sigaction fail, errno: %d, error info: %s", __LINE__, errno, strerror(errno)); return errno; } return 0; } int main(int argc, char *argv[]) { int result; int wait_count; pthread_t schedule_tid; struct fuse_lowlevel_ops fuse_operations; struct fuse_session *se; result = process_cmdline(argc, argv, (bool *)&SF_G_CONTINUE_FLAG); if (!SF_G_CONTINUE_FLAG) { return result; } #ifdef FDIR_MBLOCK_CHECK fast_mblock_manager_init(); #endif sf_enable_exit_on_oom(); do { if ((result=setup_server_env(config_filename)) != 0) { break; } if ((result=fs_fuse_wrapper_init(&fuse_operations)) != 0) { break; } if ((result=sf_startup_schedule(&schedule_tid)) != 0) { break; } if ((se=create_fuse_session(argv[0], &fuse_operations)) == NULL) { result = ENOMEM; break; } fuse_instance = se; sf_set_sig_quit_handler(fuse_exit_handler); setup_user_signal_handler(); #ifdef FDIR_MBLOCK_CHECK setup_mblock_stat_task(); #endif if ((result=fuse_session_mount(se, g_fuse_global_vars. nsmp.mountpoint)) != 0) { break; } /* Block until ctrl+c or fusermount -u */ if (g_fuse_global_vars.singlethread) { result = fuse_session_loop(se); } else { struct { #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 12) struct fuse_loop_config holder; #endif struct fuse_loop_config *ptr; } fuse_config; #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 12) fuse_config.ptr = &fuse_config.holder; fuse_config.holder.clone_fd = g_fuse_global_vars.clone_fd; fuse_config.holder.max_idle_threads = g_fuse_global_vars.max_idle_threads; #else fuse_config.ptr = fuse_loop_cfg_create(); fuse_loop_cfg_set_clone_fd(fuse_config.ptr, g_fuse_global_vars.clone_fd); if (g_fuse_global_vars.max_idle_threads < g_fuse_global_vars.max_threads) { fuse_loop_cfg_set_idle_threads(fuse_config.ptr, g_fuse_global_vars.max_idle_threads); } fuse_loop_cfg_set_max_threads(fuse_config.ptr, g_fuse_global_vars.max_threads); #endif result = fuse_session_loop_mt(se, fuse_config.ptr); } fuse_session_unmount(se); fcfs_api_terminate(); } while (0); if (g_schedule_flag) { pthread_kill(schedule_tid, SIGINT); } wait_count = 0; while (g_schedule_flag) { fc_sleep_ms(100); if (++wait_count > 100) { lwarning("waiting timeout, exit!"); break; } } if (result == 0) { logInfo("file: "__FILE__", line: %d, " "program exit normally.\n", __LINE__); } else { logCrit("file: "__FILE__", line: %d, " "program exit abnormally with errno: %d!\n", __LINE__, result); } if (need_delete_pid_file) { delete_pid_file(g_pid_filename); } log_destroy(); return result < 0 ? 1 : result; } static int setup_server_env(const char *config_filename) { int result; if ((result=sf_global_init("fcfs_fused")) != 0) { return result; } if (daemon_mode) { daemon_init(false); } if ((result=fcfs_fuse_global_init(config_filename)) != 0) { return result; } umask(0); log_set_use_file_write_lock(true); if ((result=log_reopen()) != 0) { if (result == EAGAIN || result == EACCES) { logCrit("file: "__FILE__", line: %d, " "the process already running, please kill " "the old process then try again!", __LINE__); } return result; } if ((result=sf_setup_signal_handler()) != 0) { return result; } if ((result=write_to_pid_file(g_pid_filename)) != 0) { return result; } log_set_cache(true); need_delete_pid_file = true; return fcfs_api_start_ex(&g_fcfs_api_ctx); } static struct fuse_session *create_fuse_session(char *argv0, struct fuse_lowlevel_ops *ops) { struct fuse_args args; char *argv[16]; int argc; argc = 0; argv[argc++] = argv0; if (g_log_context.log_level == LOG_DEBUG) { argv[argc++] = "-d"; } if (g_fuse_global_vars.auto_unmount) { argv[argc++] = "-o"; argv[argc++] = "auto_unmount"; } if (g_fuse_global_vars.allow_others == allow_root) { argv[argc++] = "-o"; argv[argc++] = "allow_root"; } else if (g_fuse_global_vars.allow_others == allow_all) { argv[argc++] = "-o"; argv[argc++] = "allow_other"; } if (g_fuse_global_vars.writeback_cache) { argv[argc++] = "-o"; argv[argc++] = "writeback_cache"; argv[argc++] = "-o"; argv[argc++] = "time_gran=1000000000"; } if (g_fuse_global_vars.read_only) { argv[argc++] = "-o"; argv[argc++] = "ro"; } args.argc = argc; args.argv = argv; args.allocated = 0; if ((g_fuse_cinfo_opts=fuse_parse_conn_info_opts(&args)) == NULL) { logError("file: "__FILE__", line: %d, " "fuse_parse_conn_info_opts fail!", __LINE__); return NULL; } return fuse_session_new(&args, ops, sizeof(*ops), NULL); } static void fuse_exit_handler(int sig) { if (fuse_instance != NULL) { fuse_session_exit(fuse_instance); fuse_instance = NULL; } } #ifdef FDIR_MBLOCK_CHECK static int mblock_stat_task_func(void *args) { fast_mblock_manager_stat_print_ex(false, FAST_MBLOCK_ORDER_BY_ELEMENT_SIZE); return 0; } static int setup_mblock_stat_task() { ScheduleEntry schedule_entry; ScheduleArray schedule_array; INIT_SCHEDULE_ENTRY(schedule_entry, sched_generate_next_id(), 0, 0, 0, 60, mblock_stat_task_func, NULL); schedule_entry.new_thread = true; schedule_array.count = 1; schedule_array.entries = &schedule_entry; return sched_add_entries(&schedule_array); } #endif ================================================ FILE: src/fuse/fuse_wrapper.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "global.h" #include "groups_htable.h" #include "getgroups.h" #include "fuse_wrapper.h" #define FS_READDIR_BUFFER_INIT_NONE 0 #define FS_READDIR_BUFFER_INIT_NORMAL 1 #define FS_READDIR_BUFFER_INIT_PLUS 2 struct fuse_conn_info_opts *g_fuse_cinfo_opts; static struct fast_mblock_man fh_allocator; static inline void set_operator_by_req(const struct fuse_ctx *fctx, FDIRDentryOperator *oper, char *buff) { int count; if (g_fcfs_api_ctx.owner.type == fcfs_api_owner_type_fixed) { *oper = g_fcfs_api_ctx.owner.oper; return; } if (fctx->uid == 0 || !ADDITIONAL_GROUPS_ENABLED) { FDIR_SET_OPERATOR(*oper, fctx->uid, fctx->gid, 0, buff); return; } if (GROUPS_CACHE_ENABLED) { if (fcfs_groups_htable_find(fctx->pid, fctx->uid, fctx->gid, &count, buff) != 0) { count = fcfs_get_groups(fctx->pid, fctx->uid, fctx->gid, buff); fcfs_groups_htable_insert(fctx->pid, fctx->uid, fctx->gid, count, buff); /* logInfo("SET pid: %d, uid: %d, gid: %d, groups count: %d, " "first group: %d", fctx->pid, fctx->uid, fctx->gid, count, count > 0 ? buff2int(buff) : -1); */ } else { /* logInfo("get pid: %d, uid: %d, gid: %d, groups count: %d, " "first group: %d", fctx->pid, fctx->uid, fctx->gid, count, count > 0 ? buff2int(buff) : -1); */ } } else { count = fcfs_get_groups(fctx->pid, fctx->uid, fctx->gid, buff); } FDIR_SET_OPERATOR(*oper, fctx->uid, fctx->gid, count, buff); } static inline void fill_entry_param(const FDIRDEntryInfo *dentry, struct fuse_entry_param *param) { memset(param, 0, sizeof(*param)); param->ino = dentry->inode; param->attr_timeout = g_fuse_global_vars.attribute_timeout; param->entry_timeout = g_fuse_global_vars.entry_timeout; fcfs_api_fill_stat(dentry, ¶m->attr); } static inline int fs_convert_inode(fuse_req_t req, const fuse_ino_t ino, int64_t *new_inode) { int result; FDIRDentryOperator oper; static int64_t root_inode = 0; if (ino == FUSE_ROOT_ID) { if (root_inode == 0) { char groups_buff[FDIR_MAX_USER_GROUP_BYTES]; set_operator_by_req(fuse_req_ctx(req), &oper, groups_buff); if ((result=fcfs_api_lookup_inode_by_path("/", &oper, new_inode)) != 0) { return result; } root_inode = *new_inode; } else { *new_inode = root_inode; } } else { *new_inode = ino; } return 0; } #define SET_OPER_INODE_PAIR(req, oino, _inode) \ char groups_buff[FDIR_MAX_USER_GROUP_BYTES]; \ do { \ const struct fuse_ctx *fctx; \ fctx = fuse_req_ctx(req); \ set_operator_by_req(fctx, &oino.oper, groups_buff); \ oino.inode = _inode; \ } while (0) #define SET_OPER_PNAME_PAIR(req, opname, parent_inode, name) \ char groups_buff[FDIR_MAX_USER_GROUP_BYTES]; \ do { \ set_operator_by_req(fuse_req_ctx(req), &opname.oper, groups_buff); \ FDIR_SET_DENTRY_PNAME_STR(&opname.pname, parent_inode, name); \ } while (0) static inline void do_reply_attr(fuse_req_t req, FDIRDEntryInfo *dentry) { struct stat stat; memset(&stat, 0, sizeof(stat)); fcfs_api_fill_stat(dentry, &stat); fuse_reply_attr(req, &stat, g_fuse_global_vars.attribute_timeout); } static void fs_do_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { const int flags = 0; int result; int64_t new_inode; FDIRClientOperInodePair oino; FDIRDEntryInfo dentry; if (fs_convert_inode(req, ino, &new_inode) != 0) { fuse_reply_err(req, ENOENT); return; } SET_OPER_INODE_PAIR(req, oino, new_inode); if ((result=fcfs_api_stat_dentry_by_inode(&oino, flags, &dentry)) == 0) { do_reply_attr(req, &dentry); } else { fuse_reply_err(req, result); } } void fs_do_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi) { const int flags = 0; int result; int64_t new_inode; FDIRStatModifyFlags options; FDIRClientOperInodePair oino; const struct fuse_ctx *fctx; FDIRDEntryInfo *pe; FDIRDEntryInfo dentry; /* logInfo("=====file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", to_set: %d, fi: %p, size bit: %d====", __LINE__, __FUNCTION__, ino, to_set, fi, (to_set & FUSE_SET_ATTR_SIZE)); */ if (fs_convert_inode(req, ino, &new_inode) != 0) { fuse_reply_err(req, ENOENT); return; } fctx = fuse_req_ctx(req); SET_OPER_INODE_PAIR(req, oino, new_inode); options.flags = 0; if ((to_set & FUSE_SET_ATTR_MODE)) { options.mode = 1; } if ((to_set & FUSE_SET_ATTR_UID)) { options.uid = 1; } if ((to_set & FUSE_SET_ATTR_GID)) { options.gid = 1; } if ((to_set & FUSE_SET_ATTR_SIZE)) { FCFSAPIFileInfo *fh; if (fi == NULL) { if ((result=fcfs_api_file_truncate(&oino, attr->st_size, fctx->pid, &dentry)) != 0) { fuse_reply_err(req, result); return; } dentry.stat.size = attr->st_size; pe = &dentry; } else { fh = (FCFSAPIFileInfo *)fi->fh; if (fh == NULL) { fuse_reply_err(req, EBADF); return; } /* logInfo("file: "__FILE__", line: %d, func: %s, " "SET file size from %"PRId64" to: %"PRId64, __LINE__, __FUNCTION__, fh->dentry.stat.size, (int64_t)attr->st_size); */ if ((result=fcfs_api_ftruncate_ex(fh, attr->st_size, fctx->pid)) != 0) { fuse_reply_err(req, result); return; } fh->dentry.stat.size = attr->st_size; pe = &fh->dentry; } } else { pe = NULL; } if ((to_set & FUSE_SET_ATTR_CTIME)) { options.ctime = 1; } if ((to_set & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_ATIME_NOW))) { options.atime = 1; if ((to_set & FUSE_SET_ATTR_ATIME_NOW)) { options.atime_now = 1; } } if ((to_set & (FUSE_SET_ATTR_MTIME | FUSE_SET_ATTR_MTIME_NOW))) { options.mtime = 1; if ((to_set & FUSE_SET_ATTR_MTIME_NOW)) { options.mtime_now = 1; } else if (options.atime_now && attr->st_atime == attr->st_mtime) { options.mtime_now = 1; } } /* logInfo("file: "__FILE__", line: %d, func: %s, new_inode: %"PRId64", " "flags: %"PRId64", to_set: %d, atime bit: %d, mtime bit: %d, ctime bit: %d, " "uid bit: %d, uid_to_set: %d, gid bit: %d, gid_to_set: %d, " "oper {uid: %d, gid: %d}, atime_now: %d, atime: %ld, " "mtime_now: %d, mtime: %ld", __LINE__, __FUNCTION__, new_inode, options.flags, to_set, (to_set & FUSE_SET_ATTR_ATIME), (to_set & FUSE_SET_ATTR_MTIME), options.ctime, options.uid, attr->st_uid, options.gid, attr->st_gid, oino.oper.uid, oino.oper.gid, (to_set & FUSE_SET_ATTR_ATIME_NOW), attr->st_atim.tv_sec, (to_set & FUSE_SET_ATTR_MTIME_NOW), attr->st_mtim.tv_sec); */ if (options.flags == 0) { if (pe == NULL) { pe = &dentry; result = fcfs_api_stat_dentry_by_inode(&oino, flags, &dentry); } else { result = 0; } } else { pe = &dentry; result = fcfs_api_modify_stat_by_inode(&oino, attr, options.flags, flags, &dentry); } if (result != 0) { fuse_reply_err(req, result); } else { do_reply_attr(req, pe); } } static void fs_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { const int flags = 0; int result; int64_t parent_inode; FDIRDEntryInfo dentry; FDIRClientOperPnamePair opname; struct fuse_entry_param param; if (fs_convert_inode(req, parent, &parent_inode) != 0) { fuse_reply_err(req, ENOENT); return; } SET_OPER_PNAME_PAIR(req, opname, parent_inode, name); if ((result=fcfs_api_stat_dentry_by_pname(&opname, flags, &dentry)) != 0) { /* logError("file: "__FILE__", line: %d, func: %s, " "parent: %"PRId64", name: %s(%d), result: %d", __LINE__, __FUNCTION__, parent, name, (int)strlen(name), result); */ fuse_reply_err(req, result); return; } fill_entry_param(&dentry, ¶m); fuse_reply_entry(req, ¶m); } static int dentry_list_to_buff(fuse_req_t req, FCFSAPIOpendirSession *session) { FDIRClientDentry *cd; FDIRClientDentry *end; struct stat stat; struct fuse_entry_param param; int result; int len; int next_offset; char name[NAME_MAX]; fast_buffer_reset(&session->buffer); if (session->array.count == 0) { return 0; } end = session->array.entries + session->array.count; for (cd=session->array.entries; cdname.len >= sizeof(name)) { snprintf(name, sizeof(name), "%.*s", cd->name.len, cd->name.str); } else { memcpy(name, cd->name.str, cd->name.len); *(name + cd->name.len) = '\0'; } if (session->btype == FS_READDIR_BUFFER_INIT_NORMAL) { len = fuse_add_direntry(req, NULL, 0, name, NULL, 0); } else { len = fuse_add_direntry_plus(req, NULL, 0, name, NULL, 0); } next_offset = session->buffer.length + len; if (next_offset > session->buffer.alloc_size) { if ((result=fast_buffer_set_capacity(&session->buffer, next_offset)) != 0) { return result; } } if (session->btype == FS_READDIR_BUFFER_INIT_NORMAL) { memset(&stat, 0, sizeof(stat)); fcfs_api_fill_stat(&cd->dentry, &stat); fuse_add_direntry(req, session->buffer.data + session->buffer.length, session->buffer. alloc_size - session->buffer.length, name, &stat, next_offset); } else { fill_entry_param(&cd->dentry, ¶m); fuse_add_direntry_plus(req, session->buffer.data + session->buffer.length, session->buffer. alloc_size - session->buffer.length, name, ¶m, next_offset); } session->buffer.length = next_offset; } return 0; } static void fs_do_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { int64_t new_inode; FCFSAPIOpendirSession *session; FDIRClientOperInodePair oino; int result; if (fs_convert_inode(req, ino, &new_inode) != 0) { fuse_reply_err(req, ENOENT); return; } if ((session=fcfs_api_alloc_opendir_session()) == NULL) { fuse_reply_err(req, ENOMEM); return; } SET_OPER_INODE_PAIR(req, oino, new_inode); session->btype = FS_READDIR_BUFFER_INIT_NONE; if ((result=fcfs_api_list_dentry_by_inode(&oino, &session->array)) != 0) { fcfs_api_free_opendir_session(session); fuse_reply_err(req, result); return; } fi->fh = (long)session; fuse_reply_open(req, fi); } static void do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi, const int buffer_type) { FCFSAPIOpendirSession *session; int result; /* logInfo("file: "__FILE__", line: %d, func: %s, buffer_type: %d, " "ino: %"PRId64", fh: %"PRId64", offset: %"PRId64", size: %"PRId64, __LINE__, __FUNCTION__, buffer_type, ino, fi->fh, (int64_t)offset, (int64_t)size); */ session = (FCFSAPIOpendirSession *)fi->fh; if (session == NULL) { fuse_reply_err(req, EBUSY); return; } if (session->btype == FS_READDIR_BUFFER_INIT_NONE) { session->btype = buffer_type; if ((result=dentry_list_to_buff(req, session)) != 0) { fuse_reply_err(req, result); return; } } else if (session->btype != buffer_type) { logWarning("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", unexpect buffer type: %d != %d", __LINE__, __FUNCTION__, ino, session->btype, buffer_type); fuse_reply_buf(req, NULL, 0); return; } if (offset < session->buffer.length) { fuse_reply_buf(req, session->buffer.data + offset, FC_MIN(session->buffer.length - offset, size)); } else { fuse_reply_buf(req, NULL, 0); } } /* static void fs_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi) { do_readdir(req, ino, size, offset, fi, FS_READDIR_BUFFER_INIT_NORMAL); } */ static void fs_do_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi) { do_readdir(req, ino, size, offset, fi, FS_READDIR_BUFFER_INIT_PLUS); } static void fs_do_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { FCFSAPIOpendirSession *session; session = (FCFSAPIOpendirSession *)fi->fh; if (session != NULL) { fcfs_api_free_opendir_session(session); fi->fh = 0; } fuse_reply_err(req, 0); } static int do_open(fuse_req_t req, FDIRDEntryInfo *dentry, struct fuse_file_info *fi, const FCFSAPIFileContext *fctx) { int result; FCFSAPIFileInfo *fh; fh = (FCFSAPIFileInfo *)fast_mblock_alloc_object(&fh_allocator); if (fh == NULL) { return ENOMEM; } if (fi->flags & O_DIRECT) { if (OS_KERNEL_VERSION.major > 4 || (OS_KERNEL_VERSION.major == 4 && OS_KERNEL_VERSION.minor >= 18)) { fi->direct_io = 1; } } else { if (g_fuse_global_vars.kernel_cache) { fi->keep_cache = 1; } } if ((result=fcfs_api_open_by_dentry(fh, dentry, fi->flags, fctx)) != 0) { fast_mblock_free_object(&fh_allocator, fh); if (!(result == EISDIR || result == ENOENT)) { result = EACCES; } return result; } fi->fh = (long)fh; return 0; } static void fs_do_access(fuse_req_t req, fuse_ino_t ino, int mask) { const int flags = 0; int result; int64_t new_inode; FDIRClientOperInodePair oino; FDIRDEntryInfo dentry; if (fs_convert_inode(req, ino, &new_inode) != 0) { fuse_reply_err(req, ENOENT); return; } SET_OPER_INODE_PAIR(req, oino, new_inode); result = fcfs_api_access_dentry_by_inode(&oino, mask, flags, &dentry); fuse_reply_err(req, result); } static void fs_do_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) { const dev_t rdev = 0; FCFSAPIFileContext fctx; int result; int64_t parent_inode; FDIRClientOperPnamePair opname; FDIRDEntryInfo dentry; struct fuse_entry_param param; if (fs_convert_inode(req, parent, &parent_inode) != 0) { fuse_reply_err(req, ENOENT); return; } /* logInfo("file: "__FILE__", line: %d, func: %s, " "parent ino: %"PRId64", name: %s, mode: %03o", __LINE__, __FUNCTION__, parent_inode, name, mode); */ SET_OPER_PNAME_PAIR(req, opname, parent_inode, name); if ((result=fdir_client_create_dentry_by_pname_ex(g_fcfs_api_ctx. contexts.fdir, &g_fcfs_api_ctx.ns, &opname, mode, rdev, &dentry)) != 0) { if (result != EEXIST) { fuse_reply_err(req, result); return; } if ((fi->flags & O_EXCL)) { fuse_reply_err(req, EEXIST); return; } if ((result=fcfs_api_access_dentry_by_pname(&opname, FCFS_API_GET_ACCESS_MASK(fi->flags), FCFS_API_GET_ACCESS_FLAGS(fi->flags), &dentry)) != 0) { fuse_reply_err(req, result); return; } } FCFS_API_SET_FCTX(fctx, opname.oper, mode, fuse_req_ctx(req)->pid); fi->flags &= ~(O_CREAT | O_EXCL); if ((result=do_open(req, &dentry, fi, &fctx)) != 0) { fuse_reply_err(req, result); return; } fill_entry_param(&dentry, ¶m); fuse_reply_create(req, ¶m, fi); } static void do_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, const dev_t rdev) { int result; int64_t parent_inode; FDIRClientOperPnamePair opname; FDIRDEntryInfo dentry; struct fuse_entry_param param; if (fs_convert_inode(req, parent, &parent_inode) != 0) { fuse_reply_err(req, ENOENT); return; } /* logInfo("file: "__FILE__", line: %d, func: %s, " "parent ino: %"PRId64", name: %s, mode: %03o, " "rdev: %lx, isdir: %d", __LINE__, __FUNCTION__, parent_inode, name, mode, (long)rdev, S_ISDIR(mode)); */ SET_OPER_PNAME_PAIR(req, opname, parent_inode, name); if ((result=fdir_client_create_dentry_by_pname_ex(g_fcfs_api_ctx. contexts.fdir, &g_fcfs_api_ctx.ns, &opname, mode, rdev, &dentry)) != 0) { fuse_reply_err(req, result); return; } fill_entry_param(&dentry, ¶m); fuse_reply_entry(req, ¶m); } static void fs_do_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) { do_mknod(req, parent, name, mode, rdev); } static void fs_do_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) { mode |= S_IFDIR; do_mknod(req, parent, name, mode, 0); } static int remove_dentry(fuse_req_t req, fuse_ino_t parent, const char *name, const int flags) { const struct fuse_ctx *fctx; int64_t parent_inode; FDIRClientOperPnamePair opname; if (fs_convert_inode(req, parent, &parent_inode) != 0) { return ENOENT; } fctx = fuse_req_ctx(req); /* logInfo("file: "__FILE__", line: %d, func: %s, " "parent ino: %"PRId64", name: %s, pid: %d, uid: %d, gid: %d", __LINE__, __FUNCTION__, parent_inode, name, fctx->pid, fctx->uid, fctx->gid); */ SET_OPER_PNAME_PAIR(req, opname, parent_inode, name); return fcfs_api_remove_dentry_by_pname(&opname, flags, fctx->pid); } static void fs_do_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) { int result; result = remove_dentry(req, parent, name, FDIR_UNLINK_FLAGS_MATCH_DIR); fuse_reply_err(req, result); } static void fs_do_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) { int result; result = remove_dentry(req, parent, name, FDIR_UNLINK_FLAGS_MATCH_FILE); fuse_reply_err(req, result); } void fs_do_rename(fuse_req_t req, fuse_ino_t oldparent, const char *oldname, fuse_ino_t newparent, const char *newname, unsigned int flags) { int64_t old_parent_inode; int64_t new_parent_inode; const struct fuse_ctx *fctx; string_t old_nm; string_t new_nm; FDIRDentryOperator oper; char groups_buff[FDIR_MAX_USER_GROUP_BYTES]; int result; if (fs_convert_inode(req, oldparent, &old_parent_inode) != 0) { fuse_reply_err(req, ENOENT); return; } if (fs_convert_inode(req, newparent, &new_parent_inode) != 0) { fuse_reply_err(req, ENOENT); return; } /* logInfo("file: "__FILE__", line: %d, func: %s, " "parent ino: %"PRId64", name: %s, " "newparent ino: %"PRId64", new name: %s", __LINE__, __FUNCTION__, old_parent_inode, oldname, new_parent_inode, newname); */ fctx = fuse_req_ctx(req); FC_SET_STRING(old_nm, (char *)oldname); FC_SET_STRING(new_nm, (char *)newname); set_operator_by_req(fctx, &oper, groups_buff); result = fcfs_api_rename_dentry_by_pname(old_parent_inode, &old_nm, new_parent_inode, &new_nm, &oper, flags, fctx->pid); fuse_reply_err(req, result); } static void fs_do_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, const char *name) { const int flags = FDIR_FLAGS_FOLLOW_SYMLINK; const struct fuse_ctx *fctx; FDIRClientOperPnamePair opname; FDIRDEntryInfo dentry; struct fuse_entry_param param; int64_t parent_inode; int result; if (fs_convert_inode(req, parent, &parent_inode) != 0) { fuse_reply_err(req, ENOENT); return; } fctx = fuse_req_ctx(req); SET_OPER_PNAME_PAIR(req, opname, parent_inode, name); if ((result=fdir_client_link_dentry_by_pname(g_fcfs_api_ctx.contexts.fdir, ino, &g_fcfs_api_ctx.ns, &opname, (0777 & (~fctx->umask)), flags, &dentry)) == 0) { fill_entry_param(&dentry, ¶m); fuse_reply_entry(req, ¶m); } else { fuse_reply_err(req, result); } } static void fs_do_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name) { const struct fuse_ctx *fctx; int64_t parent_inode; FDIRClientOperPnamePair opname; string_t lk; FDIRDEntryInfo dentry; struct fuse_entry_param param; int result; if (fs_convert_inode(req, parent, &parent_inode) != 0) { fuse_reply_err(req, ENOENT); return; } /* logInfo("file: "__FILE__", line: %d, func: %s, " "link length: %d, parent ino: %"PRId64", " "name length: %d", __LINE__, __FUNCTION__, (int)strlen(link), parent_inode, (int)strlen(name)); */ fctx = fuse_req_ctx(req); FC_SET_STRING(lk, (char *)link); SET_OPER_PNAME_PAIR(req, opname, parent_inode, name); if ((result=fdir_client_symlink_dentry_by_pname(g_fcfs_api_ctx. contexts.fdir, &lk, &g_fcfs_api_ctx.ns, &opname, (0777 & (~fctx->umask)), &dentry)) == 0) { fill_entry_param(&dentry, ¶m); fuse_reply_entry(req, ¶m); } else { fuse_reply_err(req, result); } } static void fs_do_readlink(fuse_req_t req, fuse_ino_t ino) { int result; char buff[PATH_MAX]; FDIRClientOperInodePair oino; string_t link; SET_OPER_INODE_PAIR(req, oino, ino); link.str = buff; if ((result=fcfs_api_readlink_by_inode(&oino, &link, PATH_MAX)) == 0) { fuse_reply_readlink(req, link.str); } else { fuse_reply_err(req, result); } } static void fs_do_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { /* logInfo("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", nlookup: %"PRId64, __LINE__, __FUNCTION__, ino, nlookup); */ fuse_reply_none(req); } static void fs_do_forget_multi(fuse_req_t req, size_t count, struct fuse_forget_data *forgets) { /* logInfo("file: "__FILE__", line: %d, func: %s, " "count: %d", __LINE__, __FUNCTION__, (int)count); */ fuse_reply_none(req); } static void fs_do_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { int result; int64_t new_inode; FCFSAPIFileContext fctx; FDIRClientOperInodePair oino; const struct fuse_ctx *fuse_ctx; FDIRDEntryInfo dentry; /* logInfo("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", fh: %"PRId64", O_APPEND flag: %d, " "O_WRONLY: %d, O_RDWR: %d, O_NONBLOCK flag: %d", __LINE__, __FUNCTION__, ino, fi->fh, (fi->flags & O_APPEND), (fi->flags & O_WRONLY), (fi->flags & O_RDWR), (fi->flags & O_NONBLOCK)); */ if (fs_convert_inode(req, ino, &new_inode) != 0) { fuse_reply_err(req, ENOENT); return; } fuse_ctx = fuse_req_ctx(req); SET_OPER_INODE_PAIR(req, oino, new_inode); if ((result=fcfs_api_access_dentry_by_inode(&oino, FCFS_API_GET_ACCESS_MASK(fi->flags), FCFS_API_GET_ACCESS_FLAGS(fi->flags), &dentry)) != 0) { fuse_reply_err(req, result); return; } FCFS_API_SET_FCTX(fctx, oino.oper, dentry.stat.mode, fuse_ctx->pid); if ((result=do_open(req, &dentry, fi, &fctx)) != 0) { fuse_reply_err(req, result); return; } fuse_reply_open(req, fi); } static void fs_do_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { /* logInfo("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", fh: %"PRId64"\n", __LINE__, __FUNCTION__, ino, fi->fh); */ fuse_reply_err(req, 0); } static void fs_do_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) { FCFSAPIFileInfo *fh; const struct fuse_ctx *fctx; int result; /* logInfo("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", fh: %"PRId64", datasync: %d", __LINE__, __FUNCTION__, ino, fi->fh, datasync); */ fh = (FCFSAPIFileInfo *)fi->fh; if (fh != NULL) { fctx = fuse_req_ctx(req); if (datasync) { result = fcfs_api_fdatasync(fh, fctx->pid); } else { result = fcfs_api_fsync(fh, fctx->pid); } } else { result = EBADF; } fuse_reply_err(req, result); } static void fs_do_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { int result; FCFSAPIFileInfo *fh; /* logInfo("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", fh: %"PRId64"\n", __LINE__, __FUNCTION__, ino, fi->fh); */ fh = (FCFSAPIFileInfo *)fi->fh; if (fh != NULL) { result = fcfs_api_close(fh); fast_mblock_free_object(&fh_allocator, fh); } else { result = EBADF; } fuse_reply_err(req, result); } static void fs_do_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi) { FCFSAPIFileInfo *fh; const struct fuse_ctx *fctx; int result; int read_bytes; char fixed_buff[256 * 1024]; char *buff; if (size <= sizeof(fixed_buff)) { buff = fixed_buff; } else if ((buff=(char *)fc_malloc(size)) == NULL) { fuse_reply_err(req, ENOMEM); return; } fh = (FCFSAPIFileInfo *)fi->fh; if (fh == NULL) { fuse_reply_err(req, EBADF); return; } fctx = fuse_req_ctx(req); if ((result=fcfs_api_pread_ex(fh, buff, size, offset, &read_bytes, fctx->pid)) != 0) { fuse_reply_err(req, result); return; } /* logInfo("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", fh: %p, size: %"PRId64", offset: %"PRId64", " "read_bytes: %d, flags: %d, O_DIRECT: %d, O_SYNC: %d, " "O_DSYNC: %d", __LINE__, __FUNCTION__, ino, fh, size, offset, read_bytes, fh->flags, fh->flags & O_DIRECT, fh->flags & O_SYNC, fh->flags & O_DSYNC); */ fuse_reply_buf(req, buff, read_bytes); if (buff != fixed_buff) { free(buff); } } void fs_do_write(fuse_req_t req, fuse_ino_t ino, const char *buff, size_t size, off_t offset, struct fuse_file_info *fi) { FCFSAPIFileInfo *fh; const struct fuse_ctx *fctx; int result; int written_bytes; fh = (FCFSAPIFileInfo *)fi->fh; if (fh == NULL) { fuse_reply_err(req, EBADF); return; } fctx = fuse_req_ctx(req); if ((result=fcfs_api_pwrite_ex(fh, buff, size, offset, &written_bytes, fctx->pid)) != 0) { fuse_reply_err(req, result); return; } /* logInfo("=======file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", size: %"PRId64", offset: %"PRId64", " "written_bytes: %d, flags: %d, O_DIRECT: %d, O_SYNC: %d, " "O_DSYNC: %d", __LINE__, __FUNCTION__, ino, size, offset, written_bytes, fh->flags, fh->flags & O_DIRECT, fh->flags & O_SYNC, fh->flags & O_DSYNC); */ fuse_reply_write(req, written_bytes); } void fs_do_lseek(fuse_req_t req, fuse_ino_t ino, off_t offset, int whence, struct fuse_file_info *fi) { FCFSAPIFileInfo *fh; int result; fh = (FCFSAPIFileInfo *)fi->fh; if (fh == NULL) { fuse_reply_err(req, EBADF); return; } if ((result=fcfs_api_lseek(fh, offset, whence)) != 0) { fuse_reply_err(req, result); return; } fuse_reply_lseek(req, fh->offset); } static void fs_do_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock) { int result; FCFSAPIFileInfo *fh; int64_t owner_id; fh = (FCFSAPIFileInfo *)fi->fh; if (fh == NULL) { result = EBADF; owner_id = 0; } else { owner_id = fi->lock_owner; result = fcfs_api_getlk_ex(fh, lock, &owner_id); } /* logInfo("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", fh: %"PRId64", type: %d, " "whence: %d, start: %"PRId64", len: %"PRId64", pid: %d, " "owner_id: %"PRId64", result: %d", __LINE__, __FUNCTION__, ino, fi->fh, lock->l_type, lock->l_whence, lock->l_start, lock->l_len, lock->l_pid, owner_id, result); */ if (result == 0) { fuse_reply_lock(req, lock); } else { fuse_reply_err(req, result); } } static void fs_do_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep) { const bool blocked = false; int result; FCFSAPIFileInfo *fh; fh = (FCFSAPIFileInfo *)fi->fh; if (fh == NULL) { result = EBADF; } else { result = fcfs_api_setlk_ex(fh, lock, fi->lock_owner, blocked); } /* logInfo("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", fh: %"PRId64", lock_owner: %"PRId64", " "type: %d, whence: %d, start: %"PRId64", len: %"PRId64", " "pid: %d, sleep: %d, result: %d", __LINE__, __FUNCTION__, ino, fi->fh, fi->lock_owner, lock->l_type, lock->l_whence, lock->l_start, lock->l_len, lock->l_pid, sleep, result); */ fuse_reply_err(req, result); } static void fs_do_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op) { int result; FCFSAPIFileInfo *fh; /* logInfo("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64", fh: %"PRId64", lock_owner: %"PRId64", " "op: %d, operation: %d", __LINE__, __FUNCTION__, ino, fi->fh, fi->lock_owner, op, (op & (LOCK_SH | LOCK_EX | LOCK_UN))); */ fh = (FCFSAPIFileInfo *)fi->fh; if (fh == NULL) { result = EBADF; } else { result = fcfs_api_flock_ex(fh, op, fi->lock_owner); } fuse_reply_err(req, result); } static void fs_do_statfs(fuse_req_t req, fuse_ino_t ino) { int result; struct statvfs stbuf; if ((result=fcfs_api_statvfs("/", &stbuf)) == 0) { fuse_reply_statfs(req, &stbuf); } else { fuse_reply_err(req, result); } /* logInfo("file: "__FILE__", line: %d, func: %s, " "ino: %"PRId64, __LINE__, __FUNCTION__, ino); */ } static void fs_do_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi) { int result; FCFSAPIFileInfo *fh; const struct fuse_ctx *fctx; fh = (FCFSAPIFileInfo *)fi->fh; if (fh == NULL) { result = EBADF; } else { fctx = fuse_req_ctx(req); result = fcfs_api_fallocate_ex(fh, mode, offset, length, fctx->pid); } fuse_reply_err(req, result); } static void fs_do_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags) { int64_t new_inode; int result; FDIRClientOperInodePair oino; key_value_pair_t xattr; if (!g_fuse_global_vars.xattr_enabled) { fuse_reply_err(req, ENOSYS); return; } if (fs_convert_inode(req, ino, &new_inode) != 0) { fuse_reply_err(req, ENOENT); return; } SET_OPER_INODE_PAIR(req, oino, new_inode); FC_SET_STRING(xattr.key, (char *)name); FC_SET_STRING_EX(xattr.value, (char *)value, size); result = fcfs_api_set_xattr_by_inode(&oino, &xattr, flags); fuse_reply_err(req, result); } static void fs_do_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) { const int flags = 0; int result; int64_t new_inode; FDIRClientOperInodePair oino; string_t nm; if (!g_fuse_global_vars.xattr_enabled) { fuse_reply_err(req, ENOSYS); return; } if (fs_convert_inode(req, ino, &new_inode) != 0) { fuse_reply_err(req, ENOENT); return; } SET_OPER_INODE_PAIR(req, oino, new_inode); FC_SET_STRING(nm, (char *)name); result = fcfs_api_remove_xattr_by_inode(&oino, &nm, flags); fuse_reply_err(req, result); } static void fs_do_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) { int result; int flags; int value_size; int64_t new_inode; char v[FDIR_XATTR_MAX_VALUE_SIZE]; FDIRClientOperInodePair oino; string_t nm; string_t value; if (!g_fuse_global_vars.xattr_enabled) { fuse_reply_err(req, ENOSYS); return; } if (fs_convert_inode(req, ino, &new_inode) != 0) { fuse_reply_err(req, ENOENT); return; } /* logInfo("getxattr ino: %"PRId64", name: %s, size: %d, req: %p", ino, name, (int)size, req); */ if (size == 0) { value_size = 0; flags = FDIR_FLAGS_XATTR_GET_SIZE; } else if (size <= FDIR_XATTR_MAX_VALUE_SIZE) { value_size = size; flags = 0; } else { value_size = FDIR_XATTR_MAX_VALUE_SIZE; flags = 0; } SET_OPER_INODE_PAIR(req, oino, new_inode); value.str = v; FC_SET_STRING(nm, (char *)name); if ((result=fcfs_api_get_xattr_by_inode(&oino, &nm, &value, value_size, flags)) != 0) { fuse_reply_err(req, result == EOVERFLOW ? ERANGE : result); return; } if (size == 0) { fuse_reply_xattr(req, value.len); } else { fuse_reply_buf(req, value.str, value.len); } } static void fs_do_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { #define MAX_LIST_SIZE (8 * 1024) int result; int flags; int list_size; int64_t new_inode; FDIRClientOperInodePair oino; char v[MAX_LIST_SIZE]; string_t list; if (!g_fuse_global_vars.xattr_enabled) { fuse_reply_err(req, ENOSYS); return; } if (fs_convert_inode(req, ino, &new_inode) != 0) { fuse_reply_err(req, ENOENT); return; } if (size == 0) { list_size = 0; flags = FDIR_FLAGS_XATTR_GET_SIZE; } else if (size <= MAX_LIST_SIZE) { list_size = size; flags = 0; } else { list_size = MAX_LIST_SIZE; flags = 0; } SET_OPER_INODE_PAIR(req, oino, new_inode); list.str = v; if ((result=fcfs_api_list_xattr_by_inode(&oino, &list, list_size, flags)) != 0) { fuse_reply_err(req, result == EOVERFLOW ? ERANGE : result); return; } /* logInfo("listxattr ino: %"PRId64", size: %d, list: %.*s, list len: %d", ino, (int)size, list.len, list.str, list.len); */ if (size == 0) { fuse_reply_xattr(req, list.len); } else { fuse_reply_buf(req, list.str, list.len); } } static void fs_do_init(void *userdata, struct fuse_conn_info *conn) { fuse_apply_conn_info_opts(g_fuse_cinfo_opts, conn); conn->want |= FUSE_CAP_EXPORT_SUPPORT; } int fs_fuse_wrapper_init(struct fuse_lowlevel_ops *ops) { int result; if ((result=fast_mblock_init_ex1(&fh_allocator, "fuse_fh", sizeof(FCFSAPIFileInfo), 4096, 0, NULL, NULL, true)) != 0) { return result; } if (GROUPS_CACHE_ENABLED && g_fcfs_api_ctx.owner.type != fcfs_api_owner_type_fixed) { if ((result=fcfs_groups_htable_init()) != 0) { return result; } } memset(ops, 0, sizeof(*ops)); ops->init = fs_do_init; ops->lookup = fs_do_lookup; ops->getattr = fs_do_getattr; ops->setattr = fs_do_setattr; ops->opendir = fs_do_opendir; //ops->readdir = fs_do_readdir; ops->readdirplus = fs_do_readdirplus; ops->releasedir = fs_do_releasedir; ops->create = fs_do_create; ops->access = fs_do_access; ops->open = fs_do_open; ops->fsync = fs_do_fsync; ops->flush = fs_do_flush; ops->release = fs_do_release; ops->read = fs_do_read; ops->write = fs_do_write; ops->mknod = fs_do_mknod; ops->mkdir = fs_do_mkdir; ops->rmdir = fs_do_rmdir; ops->unlink = fs_do_unlink; ops->link = fs_do_link; ops->symlink = fs_do_symlink; ops->readlink = fs_do_readlink; ops->rename = fs_do_rename; ops->forget = fs_do_forget; ops->forget_multi = fs_do_forget_multi; ops->lseek = fs_do_lseek; ops->getlk = fs_do_getlk; ops->setlk = fs_do_setlk; ops->flock = fs_do_flock; ops->statfs = fs_do_statfs; ops->fallocate = fs_do_fallocate; ops->setxattr = fs_do_setxattr; ops->getxattr = fs_do_getxattr; ops->listxattr = fs_do_listxattr; ops->removexattr = fs_do_removexattr; return 0; } ================================================ FILE: src/fuse/fuse_wrapper.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_FUSE_WRAPPER_H #define _FCFS_FUSE_WRAPPER_H #ifndef FUSE_USE_VERSION #define FUSE_USE_VERSION 312 #endif #include "fastcommon/common_define.h" #include "fastcfs/api/fcfs_api.h" #include "fastcfs/api/fcfs_api_util.h" #include "fuse3/fuse_lowlevel.h" #ifdef __cplusplus extern "C" { #endif extern struct fuse_conn_info_opts *g_fuse_cinfo_opts; int fs_fuse_wrapper_init(struct fuse_lowlevel_ops *ops); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/fuse/getgroups.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "global.h" #include "getgroups.h" static inline int get_last_id(const char *buff, const char *tag_str, const int tag_len, const char **next) { const char *start; const char *last; if ((start=strstr(buff, tag_str)) == NULL) { *next = buff; return -1; } start += tag_len; if ((last=strchr(start, '\n')) == NULL) { *next = buff; return -1; } *next = last; do { --last; } while (last >= start && (*last >= '0' && *last <= '9')); return strtol(last + 1, NULL, 10); } int fcfs_getgroups(const pid_t pid, const uid_t fsuid, const gid_t fsgid, const int size, gid_t *list) { #define UID_TAG_STR "\nUid:" #define UID_TAG_LEN (sizeof(UID_TAG_STR) - 1) #define GID_TAG_STR "\nGid:" #define GID_TAG_LEN (sizeof(GID_TAG_STR) - 1) #define GROUPS_TAG_STR "\nGroups:" #define GROUPS_TAG_LEN (sizeof(GROUPS_TAG_STR) - 1) char filename[64]; char buff[1024]; const char *next; struct passwd *user; char *p; char *end; int fd; int len; int euid; int egid; gid_t val; int count; p = filename; memcpy(p, "/proc/", 6); p += 6; p += fc_itoa(pid, p); *p++ = '/'; memcpy(p, "status", 6); p += 6; *p = '\0'; fd = open(filename, O_RDONLY); if (fd < 0) { return 0; } len = read(fd, buff, sizeof(buff)); close(fd); if (len <= 0) { return 0; } buff[len - 1] = '\0'; euid = get_last_id(buff, UID_TAG_STR, UID_TAG_LEN, &next); if ((egid=get_last_id(next, GID_TAG_STR, GID_TAG_LEN, &next)) < 0) { egid = get_last_id(buff, GID_TAG_STR, GID_TAG_LEN, &next); } if (fsuid == euid && fsgid == egid) { if ((p=strstr(next, GROUPS_TAG_STR)) == NULL) { if ((p=strstr(buff, GROUPS_TAG_STR)) == NULL) { return 0; } } p += GROUPS_TAG_LEN; count = 0; while (count < size) { while (*p == ' ' || *p == '\t') { ++p; } val = strtoul(p, &end, 10); if (end == p) { break; } if (val != fsgid) { list[count++] = val; } p = end; } } else { /* logInfo("line: %d, fsuid: %d, fsgid: %d, euid: %d, egid: %d", __LINE__, fsuid, fsgid, euid, egid); */ if ((user=getpwuid(fsuid)) == NULL) { return 0; } count = size; if (getgrouplist(user->pw_name, fsgid, list, &count) < 0) { return 0; } } return count; } int fcfs_get_groups(const pid_t pid, const uid_t fsuid, const gid_t fsgid, char *buff) { int count; gid_t list[FDIR_MAX_USER_GROUP_COUNT]; const gid_t *group; const gid_t *end; char *p; count = fcfs_getgroups(pid, fsuid, fsgid, FDIR_MAX_USER_GROUP_COUNT, list); if (count <= 0) { count = 0; } else if (count == 1 && list[0] == fsgid) { count = 0; } else { end = list + count; for (group=list, p=buff; group 0 ? buff2int(buff) : -1); */ return count; } ================================================ FILE: src/fuse/getgroups.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_GETGROUPS_H #define _FCFS_GETGROUPS_H #include "fastcommon/common_define.h" #ifdef __cplusplus extern "C" { #endif int fcfs_getgroups(const pid_t pid, const uid_t fsuid, const gid_t fsgid, const int size, gid_t *list); int fcfs_get_groups(const pid_t pid, const uid_t fsuid, const gid_t fsgid, char *buff); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/fuse/global.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/sched_thread.h" #include "fastcommon/system_info.h" #include "sf/sf_global.h" #include "sf/idempotency/client/client_channel.h" #include "fuse_wrapper.h" #include "global.h" #define INI_FUSE_SECTION_NAME "FUSE" #define INI_GROUPS_CACHE_SECTION_NAME "groups-cache" #define FUSE_ALLOW_ALL_STR "all" #define FUSE_ALLOW_ROOT_STR "root" #define FUSE_MIN_SHARED_ALLOCATOR_COUNT 1 #define FUSE_MAX_SHARED_ALLOCATOR_COUNT 100 #define FUSE_DEFAULT_SHARED_ALLOCATOR_COUNT 7 #define FUSE_MIN_HASHTABLE_SHARDING_COUNT 1 #define FUSE_MAX_HASHTABLE_SHARDING_COUNT 1000 #define FUSE_DEFAULT_HASHTABLE_SHARDING_COUNT 163 #define FUSE_MIN_HASHTABLE_TOTAL_CAPACITY 10949 #define FUSE_MAX_HASHTABLE_TOTAL_CAPACITY 1000000 #define FUSE_DEFAULT_HASHTABLE_TOTAL_CAPACITY 175447 FUSEGlobalVars g_fuse_global_vars = {{NULL, NULL}}; static int load_fuse_config(IniFullContext *ini_ctx) { string_t mountpoint; char *writeback_cache; char *allow_others; int result; get_kernel_version(&OS_KERNEL_VERSION); ini_ctx->section_name = INI_FUSE_SECTION_NAME; if (g_fuse_global_vars.nsmp.mountpoint == NULL) { FC_SET_STRING_NULL(mountpoint); } else { FC_SET_STRING(mountpoint, g_fuse_global_vars.nsmp.mountpoint); } if ((result=fcfs_api_load_ns_mountpoint(ini_ctx, FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, &g_fuse_global_vars.nsmp, &mountpoint, true)) != 0) { return result; } g_fuse_global_vars.max_threads = iniGetIntCorrectValue(ini_ctx, "max_threads", 10, 1, 1024); g_fuse_global_vars.max_idle_threads = iniGetIntCorrectValue(ini_ctx, "max_idle_threads", g_fuse_global_vars.max_threads, 1, 1024); g_fuse_global_vars.singlethread = iniGetBoolValue(ini_ctx-> section_name, "singlethread", ini_ctx->context, false); g_fuse_global_vars.clone_fd = iniGetBoolValue(ini_ctx-> section_name, "clone_fd", ini_ctx->context, false); if (g_fuse_global_vars.clone_fd) { if (OS_KERNEL_VERSION.major < 4 || (OS_KERNEL_VERSION.major == 4 && OS_KERNEL_VERSION.minor < 2)) { logWarning("file: "__FILE__", line: %d, " "kernel version %d.%d < 4.2, do NOT support " "FUSE feature clone_fd", __LINE__, OS_KERNEL_VERSION.major, OS_KERNEL_VERSION.minor); g_fuse_global_vars.clone_fd = false; } } g_fuse_global_vars.auto_unmount = iniGetBoolValue(ini_ctx-> section_name, "auto_unmount", ini_ctx->context, false); g_fuse_global_vars.read_only = iniGetBoolValue(ini_ctx-> section_name, "read_only", ini_ctx->context, false); allow_others = iniGetStrValue(ini_ctx->section_name, "allow_others", ini_ctx->context); if (allow_others == NULL || *allow_others == '\0') { g_fuse_global_vars.allow_others = allow_none; } else if (strcasecmp(allow_others, FUSE_ALLOW_ALL_STR) == 0) { g_fuse_global_vars.allow_others = allow_all; } else if (strcasecmp(allow_others, FUSE_ALLOW_ROOT_STR) == 0) { g_fuse_global_vars.allow_others = allow_root; } else { g_fuse_global_vars.allow_others = allow_none; } g_fuse_global_vars.attribute_timeout = iniGetDoubleValue(ini_ctx-> section_name, "attribute_timeout", ini_ctx->context, FCFS_FUSE_DEFAULT_ATTRIBUTE_TIMEOUT); g_fuse_global_vars.entry_timeout = iniGetDoubleValue(ini_ctx-> section_name, "entry_timeout", ini_ctx->context, FCFS_FUSE_DEFAULT_ENTRY_TIMEOUT); g_fuse_global_vars.xattr_enabled = iniGetBoolValue(ini_ctx-> section_name, "xattr_enabled", ini_ctx->context, false); writeback_cache = iniGetStrValue(ini_ctx->section_name, "writeback_cache", ini_ctx->context); if (writeback_cache == NULL) { g_fuse_global_vars.writeback_cache = (OS_KERNEL_VERSION.major > 3 || (OS_KERNEL_VERSION.major == 3 && OS_KERNEL_VERSION.minor >= 15)); } else { g_fuse_global_vars.writeback_cache = FAST_INI_STRING_IS_TRUE( writeback_cache); if (g_fuse_global_vars.writeback_cache) { if (OS_KERNEL_VERSION.major < 3 || (OS_KERNEL_VERSION.major == 3 && OS_KERNEL_VERSION.minor < 15)) { logWarning("file: "__FILE__", line: %d, " "kernel version %d.%d < 3.15, do NOT support " "FUSE feature writeback_cache", __LINE__, OS_KERNEL_VERSION.major, OS_KERNEL_VERSION.minor); g_fuse_global_vars.writeback_cache = false; } } } g_fuse_global_vars.kernel_cache = iniGetBoolValue(ini_ctx-> section_name, "kernel_cache", ini_ctx->context, true); ADDITIONAL_GROUPS_ENABLED = iniGetBoolValue(ini_ctx->section_name, "groups_enabled", ini_ctx->context, true); return 0; } static void load_additional_groups_config(IniFullContext *ini_ctx) { ini_ctx->section_name = INI_GROUPS_CACHE_SECTION_NAME; GROUPS_CACHE_ENABLED = iniGetBoolValue(ini_ctx-> section_name, "enabled", ini_ctx->context, true); GROUPS_CACHE_TIMEOUT = iniGetIntCorrectValue(ini_ctx, "timeout", 300, 1, 1e8); GROUPS_CACHE_ALLOCATOR_COUNT = iniGetIntCorrectValueEx( ini_ctx, "shared_allocator_count", FUSE_DEFAULT_SHARED_ALLOCATOR_COUNT, FUSE_MIN_SHARED_ALLOCATOR_COUNT, FUSE_MAX_SHARED_ALLOCATOR_COUNT, true); GROUPS_CACHE_SHARDING_COUNT = iniGetIntCorrectValue( ini_ctx, "hashtable_sharding_count", FUSE_DEFAULT_HASHTABLE_SHARDING_COUNT, FUSE_MIN_HASHTABLE_SHARDING_COUNT, FUSE_MAX_HASHTABLE_SHARDING_COUNT); GROUPS_CACHE_HTABLE_CAPACITY = iniGetIntCorrectValue( ini_ctx, "hashtable_total_capacity", FUSE_DEFAULT_HASHTABLE_TOTAL_CAPACITY, FUSE_MIN_HASHTABLE_TOTAL_CAPACITY, FUSE_MAX_HASHTABLE_TOTAL_CAPACITY); GROUPS_CACHE_ELEMENT_LIMIT = iniGetIntCorrectValue(ini_ctx, "element_limit", 64 * 1024, 16 * 1024, 1024 * 1024); } static void additional_groups_config_to_string(char *buff, const int size) { if (!ADDITIONAL_GROUPS_ENABLED) { snprintf(buff, size, "groups_enabled: 0"); return; } if (GROUPS_CACHE_ENABLED) { snprintf(buff, size, "groups_enabled: 1, " "groups-cache {enabled: 1, timeout: %d, " "shared_allocator_count: %d, " "hashtable_sharding_count: %d, " "hashtable_total_capacity: %d, " "element_limit: %d}", GROUPS_CACHE_TIMEOUT, GROUPS_CACHE_ALLOCATOR_COUNT, GROUPS_CACHE_SHARDING_COUNT, GROUPS_CACHE_HTABLE_CAPACITY, GROUPS_CACHE_ELEMENT_LIMIT); } else { snprintf(buff, size, "groups_enabled: 1, " "groups-cache {enabled: 0}"); } } static const char *get_allow_others_caption( const FUSEAllowOthersMode allow_others) { switch (allow_others) { case allow_all: return FUSE_ALLOW_ALL_STR; case allow_root: return FUSE_ALLOW_ROOT_STR; default: return ""; } } int fcfs_fuse_global_init(const char *config_filename) { const bool publish = true; int result; BufferInfo sf_idempotency_config; char buff[256]; char rdma_busy_polling[128]; char owner_config[2 * NAME_MAX + 64]; char additional_groups_config[256]; char max_threads_buff[64]; IniContext iniContext; IniFullContext ini_ctx; if ((result=iniLoadFromFile(config_filename, &iniContext)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, config_filename, result); return result; } FAST_INI_SET_FULL_CTX_EX(ini_ctx, config_filename, FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, &iniContext); do { if ((result=load_fuse_config(&ini_ctx)) != 0) { break; } if (ADDITIONAL_GROUPS_ENABLED) { load_additional_groups_config(&ini_ctx); } if ((result=fcfs_api_pooled_init1_with_auth( g_fuse_global_vars.nsmp.ns, &ini_ctx, publish)) != 0) { break; } if ((result=fcfs_api_check_mountpoint1(config_filename, g_fuse_global_vars.nsmp.mountpoint)) != 0) { break; } if ((result=fcfs_api_load_idempotency_config( NULL, &ini_ctx)) != 0) { break; } } while (0); iniFreeContext(&iniContext); if (result != 0) { return result; } sf_idempotency_config.buff = buff; sf_idempotency_config.alloc_size = sizeof(buff); fcfs_api_log_client_common_configs(&g_fcfs_api_ctx, FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME, &sf_idempotency_config, owner_config); #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 12) sprintf(max_threads_buff, "max_idle_threads: %d", g_fuse_global_vars.max_idle_threads); #else sprintf(max_threads_buff, "max_threads: %d, max_idle_threads: %d", g_fuse_global_vars.max_threads, g_fuse_global_vars. max_idle_threads); #endif additional_groups_config_to_string(additional_groups_config, sizeof(additional_groups_config)); if (g_fcfs_api_ctx.rdma.enabled) { sprintf(rdma_busy_polling, "rdma busy polling: %s, ", g_fcfs_api_ctx.rdma.busy_polling ? "true" : "false"); } else { *rdma_busy_polling = '\0'; } logInfo("FastCFS V%d.%d.%d, FUSE library version " "{compile: %d.%d, runtime: %s}, %s" "FastDIR namespace: %s, %sFUSE mountpoint: %s, " "%s, singlethread: %d, clone_fd: %d, " "%s, allow_others: %s, auto_unmount: %d, read_only: %d, " "attribute_timeout: %.1fs, entry_timeout: %.1fs, " "xattr_enabled: %d, writeback_cache: %d, kernel_cache: %d, %s", g_fcfs_global_vars.version.major, g_fcfs_global_vars.version.minor, g_fcfs_global_vars.version.patch, FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION, fuse_pkgversion(), rdma_busy_polling, g_fuse_global_vars.nsmp.ns, sf_idempotency_config.buff, g_fuse_global_vars.nsmp.mountpoint, owner_config, g_fuse_global_vars.singlethread, g_fuse_global_vars.clone_fd, max_threads_buff, get_allow_others_caption(g_fuse_global_vars.allow_others), g_fuse_global_vars.auto_unmount, g_fuse_global_vars.read_only, g_fuse_global_vars.attribute_timeout, g_fuse_global_vars.entry_timeout, g_fuse_global_vars.xattr_enabled, g_fuse_global_vars.writeback_cache, g_fuse_global_vars.kernel_cache, additional_groups_config); return 0; } ================================================ FILE: src/fuse/global.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_FUSE_GLOBAL_H #define _FCFS_FUSE_GLOBAL_H #include "fastcfs/api/fcfs_api.h" #include "common/fcfs_global.h" #define FCFS_FUSE_DEFAULT_ATTRIBUTE_TIMEOUT 1.0 #define FCFS_FUSE_DEFAULT_ENTRY_TIMEOUT 1.0 typedef enum { allow_none, allow_all, allow_root } FUSEAllowOthersMode; typedef struct { FCFSAPINSMountpointHolder nsmp; bool singlethread; bool clone_fd; bool auto_unmount; bool read_only; bool xattr_enabled; bool writeback_cache; bool kernel_cache; bool groups_enabled; struct { bool enabled; int timeout; int sharding_count; int htable_capacity; int allocator_count; int element_limit; } groups_cache; int max_idle_threads; int max_threads; //libfuse >= 3.12 double attribute_timeout; double entry_timeout; FUSEAllowOthersMode allow_others; Version kernel_version; } FUSEGlobalVars; #define OS_KERNEL_VERSION g_fuse_global_vars.kernel_version #define ADDITIONAL_GROUPS_ENABLED g_fuse_global_vars.groups_enabled #define GROUPS_CACHE_ENABLED g_fuse_global_vars.groups_cache.enabled #define GROUPS_CACHE_TIMEOUT g_fuse_global_vars.groups_cache.timeout #define GROUPS_CACHE_SHARDING_COUNT g_fuse_global_vars.groups_cache.sharding_count #define GROUPS_CACHE_HTABLE_CAPACITY g_fuse_global_vars.groups_cache.htable_capacity #define GROUPS_CACHE_ALLOCATOR_COUNT g_fuse_global_vars.groups_cache.allocator_count #define GROUPS_CACHE_ELEMENT_LIMIT g_fuse_global_vars.groups_cache.element_limit #ifdef __cplusplus extern "C" { #endif extern FUSEGlobalVars g_fuse_global_vars; int fcfs_fuse_global_init(const char *config_filename); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/fuse/groups_htable.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "groups_htable.h" #include "global.h" #define FCFS_GROUP_FIXED_COUNT 16 typedef struct { SFShardingHashEntry hentry; //must be the first time_t expires; struct { short alloc_size; short count; char holder[FCFS_GROUP_FIXED_COUNT * 4]; char *list; //4 bytes network order integer array } groups; } FCFSGroupHashEntry; typedef struct { int *count; char *list; } FCFSGroupOpContext; static SFHtableShardingContext fcfs_group_ctx; static int groups_htable_insert_callback(SFShardingHashEntry *he, void *arg, const bool new_create) { FCFSGroupHashEntry *entry; FCFSGroupOpContext *op_ctx; entry = (FCFSGroupHashEntry *)he; op_ctx = (FCFSGroupOpContext *)arg; if (entry->groups.alloc_size == 0) { entry->groups.alloc_size = FCFS_GROUP_FIXED_COUNT; entry->groups.list = entry->groups.holder; } if (*(op_ctx->count) > entry->groups.alloc_size) { int new_size; char *new_list; new_size = 2 * entry->groups.alloc_size; while (new_size < *(op_ctx->count)) { new_size *= 2; } if ((new_list=fc_malloc(new_size * 4)) == NULL) { return ENOMEM; } if (entry->groups.list != entry->groups.holder) { free(entry->groups.list); } entry->groups.alloc_size = new_size; entry->groups.list = new_list; } entry->groups.count = *(op_ctx->count); if ((*op_ctx->count) > 0) { memcpy(entry->groups.list, op_ctx->list, entry->groups.count * 4); } entry->expires = g_current_time + GROUPS_CACHE_TIMEOUT; return 0; } static void *groups_htable_find_callback(SFShardingHashEntry *he, void *arg) { FCFSGroupHashEntry *entry; FCFSGroupOpContext *op_ctx; entry = (FCFSGroupHashEntry *)he; op_ctx = (FCFSGroupOpContext *)arg; if (entry->expires >= g_current_time) { *(op_ctx->count) = entry->groups.count; if (entry->groups.count > 0) { memcpy(op_ctx->list, entry->groups.list, entry->groups.count * 4); } return entry; } else { *(op_ctx->count) = 0; return NULL; } } int fcfs_groups_htable_init() { int64_t min_ttl_ms; int64_t max_ttl_ms; const double low_water_mark_ratio = 0.10; if (GROUPS_CACHE_TIMEOUT <= 30) { min_ttl_ms = 1 * 1000; max_ttl_ms = 30 * 1000; } else if (GROUPS_CACHE_TIMEOUT <= 300) { min_ttl_ms = 3 * 1000; max_ttl_ms = 300 * 1000; } else { min_ttl_ms = 5 * 1000; max_ttl_ms = GROUPS_CACHE_TIMEOUT * 1000LL; } return sf_sharding_htable_init_ex(&fcfs_group_ctx, sf_sharding_htable_key_ids_two, groups_htable_insert_callback, groups_htable_find_callback, NULL, NULL, GROUPS_CACHE_SHARDING_COUNT, GROUPS_CACHE_HTABLE_CAPACITY, GROUPS_CACHE_ALLOCATOR_COUNT, sizeof(FCFSGroupHashEntry), GROUPS_CACHE_ELEMENT_LIMIT, min_ttl_ms, max_ttl_ms, low_water_mark_ratio); } #define FCFS_GROUP_HTABLE_SET_KEY(key, pid, uid, gid) \ key.id1 = ((int64_t)uid << 32) | gid; \ key.id2 = pid int fcfs_groups_htable_insert(const pid_t pid, const uid_t uid, const gid_t gid, const int count, const char *list) { SFTwoIdsHashKey key; FCFSGroupOpContext op_ctx; FCFS_GROUP_HTABLE_SET_KEY(key, pid, uid, gid); op_ctx.count = (int *)&count; op_ctx.list = (char *)list; return sf_sharding_htable_insert(&fcfs_group_ctx, &key, &op_ctx); } int fcfs_groups_htable_find(const pid_t pid, const uid_t uid, const gid_t gid, int *count, char *list) { SFTwoIdsHashKey key; FCFSGroupOpContext op_ctx; FCFS_GROUP_HTABLE_SET_KEY(key, pid, uid, gid); op_ctx.count = count; op_ctx.list = list; if (sf_sharding_htable_find(&fcfs_group_ctx, &key, &op_ctx) != NULL) { return 0; } else { return ENOENT; } } ================================================ FILE: src/fuse/groups_htable.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_GROUPS_HTABLE_H #define _FCFS_GROUPS_HTABLE_H #include "sf/sf_sharding_htable.h" #ifdef __cplusplus extern "C" { #endif int fcfs_groups_htable_init(); int fcfs_groups_htable_insert(const pid_t pid, const uid_t uid, const gid_t gid, const int count, const char *list); int fcfs_groups_htable_find(const pid_t pid, const uid_t uid, const gid_t gid, int *count, char *list); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/java/jni/Makefile.in ================================================ .SUFFIXES: .c .o .lo COMPILE = $(CC) $(CFLAGS) PREFIX = $(TARGET_PREFIX) INC_PATH = -I/usr/local/include $(INCLUDES) LIB_PATH = -lfcfsapi -lfdirclient -lfsapi -lfsclient \ -lfcfsauthclient -lfastcommon -lserverframe SHARED_OBJS = com_fastken_fcfs_FCFSPosixAPI.lo \ com_fastken_fcfs_FCFSDirectory.lo \ com_fastken_fcfs_FCFSFile.lo \ global.lo common.lo ALL_OBJS = $(SHARED_OBJS) ALL_PRGS = SHARED_LIBS = libfcfsjni.so ALL_LIBS = $(SHARED_LIBS) all: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) libfcfsjni.so: $(SHARED_OBJS) $(COMPILE) -o $@ -shared $(SHARED_OBJS) $(LIB_PATH) .c.o: $(COMPILE) -c -o $@ $< $(INC_PATH) .c.lo: $(COMPILE) -c -fPIC -o $@ $< $(INC_PATH) install: mkdir -p $(PREFIX)/lib64 mkdir -p $(PREFIX)/lib install -m 755 $(SHARED_LIBS) $(PREFIX)/lib64 install -m 755 $(SHARED_LIBS) $(PREFIX)/lib clean: rm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) ================================================ FILE: src/java/jni/com_fastken_fcfs_FCFSConstants.h ================================================ /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_fastken_fcfs_FCFSConstants */ #ifndef _Included_com_fastken_fcfs_FCFSConstants #define _Included_com_fastken_fcfs_FCFSConstants #ifdef __cplusplus extern "C" { #endif #undef com_fastken_fcfs_FCFSConstants_X_OK #define com_fastken_fcfs_FCFSConstants_X_OK 1L #undef com_fastken_fcfs_FCFSConstants_W_OK #define com_fastken_fcfs_FCFSConstants_W_OK 2L #undef com_fastken_fcfs_FCFSConstants_R_OK #define com_fastken_fcfs_FCFSConstants_R_OK 4L #undef com_fastken_fcfs_FCFSConstants_XATTR_CREATE #define com_fastken_fcfs_FCFSConstants_XATTR_CREATE 1L #undef com_fastken_fcfs_FCFSConstants_XATTR_REPLACE #define com_fastken_fcfs_FCFSConstants_XATTR_REPLACE 2L #undef com_fastken_fcfs_FCFSConstants_SEEK_SET #define com_fastken_fcfs_FCFSConstants_SEEK_SET 0L #undef com_fastken_fcfs_FCFSConstants_SEEK_CUR #define com_fastken_fcfs_FCFSConstants_SEEK_CUR 1L #undef com_fastken_fcfs_FCFSConstants_SEEK_END #define com_fastken_fcfs_FCFSConstants_SEEK_END 2L #undef com_fastken_fcfs_FCFSConstants_O_RDONLY #define com_fastken_fcfs_FCFSConstants_O_RDONLY 0L #undef com_fastken_fcfs_FCFSConstants_O_WRONLY #define com_fastken_fcfs_FCFSConstants_O_WRONLY 1L #undef com_fastken_fcfs_FCFSConstants_O_RDWR #define com_fastken_fcfs_FCFSConstants_O_RDWR 2L #undef com_fastken_fcfs_FCFSConstants_O_CREAT #define com_fastken_fcfs_FCFSConstants_O_CREAT 64L #undef com_fastken_fcfs_FCFSConstants_O_EXCL #define com_fastken_fcfs_FCFSConstants_O_EXCL 128L #undef com_fastken_fcfs_FCFSConstants_O_TRUNC #define com_fastken_fcfs_FCFSConstants_O_TRUNC 512L #undef com_fastken_fcfs_FCFSConstants_O_APPEND #define com_fastken_fcfs_FCFSConstants_O_APPEND 1024L #undef com_fastken_fcfs_FCFSConstants_O_NOFOLLOW #define com_fastken_fcfs_FCFSConstants_O_NOFOLLOW 131072L #undef com_fastken_fcfs_FCFSConstants_O_CLOEXEC #define com_fastken_fcfs_FCFSConstants_O_CLOEXEC 524288L #ifdef __cplusplus } #endif #endif ================================================ FILE: src/java/jni/com_fastken_fcfs_FCFSDirectory.c ================================================ #include "fastcfs/api/std/posix_api.h" #include "global.h" #include "common.h" #include "com_fastken_fcfs_FCFSDirectory.h" jobject JNICALL Java_com_fastken_fcfs_FCFSDirectory_next (JNIEnv *env, jobject obj) { long handler; DIR *dir; struct dirent *dirent; jstring name; handler = (*env)->CallLongMethod(env, obj, g_fcfs_jni_global_vars.dir.getHandler); if (handler == 0) { fcfs_jni_throw_null_pointer_exception(env); return NULL; } dir = (DIR *)handler; if ((dirent=fcfs_readdir(dir)) == NULL) { return NULL; } name = (*env)->NewStringUTF(env, dirent->d_name); return (*env)->NewObject(env, g_fcfs_jni_global_vars.dirent.clazz, g_fcfs_jni_global_vars.dirent.constructor2, dirent->d_ino, name); } void JNICALL Java_com_fastken_fcfs_FCFSDirectory_seek (JNIEnv *env, jobject obj, jlong loc) { long handler; DIR *dir; handler = (*env)->CallLongMethod(env, obj, g_fcfs_jni_global_vars.dir.getHandler); if (handler == 0) { fcfs_jni_throw_null_pointer_exception(env); return; } dir = (DIR *)handler; fcfs_seekdir(dir, loc); } jlong JNICALL Java_com_fastken_fcfs_FCFSDirectory_tell (JNIEnv *env, jobject obj) { long handler; DIR *dir; handler = (*env)->CallLongMethod(env, obj, g_fcfs_jni_global_vars.dir.getHandler); if (handler == 0) { fcfs_jni_throw_null_pointer_exception(env); return -1; } dir = (DIR *)handler; return fcfs_telldir(dir); } void JNICALL Java_com_fastken_fcfs_FCFSDirectory_rewind (JNIEnv *env, jobject obj) { long handler; DIR *dir; handler = (*env)->CallLongMethod(env, obj, g_fcfs_jni_global_vars.dir.getHandler); if (handler == 0) { fcfs_jni_throw_null_pointer_exception(env); return; } dir = (DIR *)handler; fcfs_rewinddir(dir); } void JNICALL Java_com_fastken_fcfs_FCFSDirectory_close (JNIEnv *env, jobject obj) { long handler; DIR *dir; handler = (*env)->CallLongMethod(env, obj, g_fcfs_jni_global_vars.dir.getHandler); if (handler == 0) { fcfs_jni_throw_null_pointer_exception(env); return; } dir = (DIR *)handler; fcfs_closedir(dir); (*env)->CallVoidMethod(env, obj, g_fcfs_jni_global_vars. dir.setHandler, 0); } ================================================ FILE: src/java/jni/com_fastken_fcfs_FCFSDirectory.h ================================================ /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_fastken_fcfs_FCFSDirectory */ #ifndef _Included_com_fastken_fcfs_FCFSDirectory #define _Included_com_fastken_fcfs_FCFSDirectory #ifdef __cplusplus extern "C" { #endif /* * Class: com_fastken_fcfs_FCFSDirectory * Method: doInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_doInit (JNIEnv *, jclass); /* * Class: com_fastken_fcfs_FCFSDirectory * Method: next * Signature: ()Lcom/fastken/fcfs/FCFSDirectory/Entry; */ JNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSDirectory_next (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSDirectory * Method: seek * Signature: (J)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_seek (JNIEnv *, jobject, jlong); /* * Class: com_fastken_fcfs_FCFSDirectory * Method: tell * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_fastken_fcfs_FCFSDirectory_tell (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSDirectory * Method: rewind * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_rewind (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSDirectory * Method: close * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_close (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/java/jni/com_fastken_fcfs_FCFSFile.c ================================================ #include "fastcfs/api/std/posix_api.h" #include "global.h" #include "common.h" #include "com_fastken_fcfs_FCFSFile.h" void JNICALL Java_com_fastken_fcfs_FCFSFile_close (JNIEnv *env, jobject obj) { int fd; fd = (*env)->CallIntMethod(env, obj, g_fcfs_jni_global_vars.file.getFD); if (fd < 0) { return; } fcfs_close(fd); (*env)->CallVoidMethod(env, obj, g_fcfs_jni_global_vars.file.setFD, -1); } #define FILE_OBJ_FETCH_FD(retval) \ int fd; \ \ fd = (*env)->CallIntMethod(env, obj, g_fcfs_jni_global_vars.file.getFD); \ if (fd < 0) { \ fcfs_jni_throw_null_pointer_exception(env); \ return retval; \ } \ static inline void throw_file_exception(JNIEnv *env, const int fd, const int err_no) { FCFSPosixAPIFileInfo *handle; if ((handle=fcfs_fd_manager_get(fd)) == NULL) { fcfs_jni_throw_exception(env, strerror(err_no)); } else { fcfs_jni_throw_filesystem_exception(env, handle->filename.str, err_no); } } jobject JNICALL Java_com_fastken_fcfs_FCFSFile_stat (JNIEnv *env, jobject obj) { struct stat stat; FILE_OBJ_FETCH_FD(NULL); if (fcfs_fstat(fd, &stat) != 0) { throw_file_exception(env, fd, errno != 0 ? errno : ENOENT); return NULL; } return (*env)->NewObject(env, g_fcfs_jni_global_vars.fstat.clazz, g_fcfs_jni_global_vars.fstat.constructor10, stat.st_ino, stat.st_mode, stat.st_nlink, stat.st_uid, stat.st_gid, stat.st_rdev, stat.st_size, (jlong)stat.st_atime * 1000LL, (jlong)stat.st_mtime * 1000LL, (jlong)stat.st_ctime * 1000LL); } jobject JNICALL Java_com_fastken_fcfs_FCFSFile_statvfs (JNIEnv *env, jobject obj) { struct statvfs stat; SFSpaceStat space; FILE_OBJ_FETCH_FD(NULL); if (fcfs_fstatvfs(fd, &stat) != 0) { throw_file_exception(env, fd, errno != 0 ? errno : ENOENT); return NULL; } space.total = (int64_t)stat.f_blocks * stat.f_bsize; space.avail = (int64_t)stat.f_bavail * stat.f_bsize; space.used = space.total - space.avail; return (*env)->NewObject(env, g_fcfs_jni_global_vars.statvfs.clazz, g_fcfs_jni_global_vars.statvfs.constructor6, space.total, space.avail, space.used, stat.f_files, stat.f_favail, stat.f_files - stat.f_favail); } jbyteArray JNICALL Java_com_fastken_fcfs_FCFSFile_getxattr (JNIEnv *env, jobject obj, jstring jname) { jboolean *isCopy = NULL; const char *name; char holder[4 * 1024]; char *buff; int result; int size; int length; jbyteArray value; FILE_OBJ_FETCH_FD(NULL); name = (*env)->GetStringUTFChars(env, jname, isCopy); buff = holder; size = sizeof(holder); if ((length=fcfs_fgetxattr(fd, name, buff, size)) < 0) { result = errno != 0 ? errno : ENOENT; if (result == EOVERFLOW) { if ((size=fcfs_fgetxattr(fd, name, buff, 0)) < 0) { result = errno != 0 ? errno : ENOENT; } else { if ((buff=malloc(size)) == NULL) { result = ENOMEM; } else { if ((length=fcfs_fgetxattr(fd, name, buff, size)) < 0) { result = errno != 0 ? errno : ENOENT; } else { result = 0; } } } } if (result != 0) { throw_file_exception(env, fd, result); (*env)->ReleaseStringUTFChars(env, jname, name); return NULL; } } (*env)->ReleaseStringUTFChars(env, jname, name); value = (*env)->NewByteArray(env, length); (*env)->SetByteArrayRegion(env, value, 0, length, (const jbyte *)buff); if (buff != holder) { free(buff); } return value; } jobject JNICALL Java_com_fastken_fcfs_FCFSFile_listxattr (JNIEnv *env, jobject obj) { char holder[4 * 1024]; char *buff; int result; int size; int length; jobject list_obj; FILE_OBJ_FETCH_FD(NULL); buff = holder; size = sizeof(holder); if ((length=fcfs_flistxattr(fd, buff, size)) < 0) { result = errno != 0 ? errno : ENOENT; if (result == EOVERFLOW) { if ((size=fcfs_flistxattr(fd, buff, 0)) < 0) { result = errno != 0 ? errno : ENOENT; } else { if ((buff=malloc(size)) == NULL) { result = ENOMEM; } else { if ((length=fcfs_flistxattr(fd, buff, size)) < 0) { result = errno != 0 ? errno : ENOENT; } else { result = 0; } } } } if (result != 0) { throw_file_exception(env, fd, result); return NULL; } } list_obj = fcfs_jni_convert_to_list(env, buff, length); if (buff != holder) { free(buff); } return list_obj; } void JNICALL Java_com_fastken_fcfs_FCFSFile_sync (JNIEnv *env, jobject obj) { FILE_OBJ_FETCH_FD(); if (fcfs_fsync(fd) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } } void JNICALL Java_com_fastken_fcfs_FCFSFile_datasync (JNIEnv *env, jobject obj) { FILE_OBJ_FETCH_FD(); if (fcfs_fdatasync(fd) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } } #define FILE_CHECK_ARRAY_BOUNDS_EX(b, off, len, size, retval) \ do { \ size = (*env)->GetArrayLength(env, b); \ if (off < 0 || off >= size) { \ fcfs_jni_throw_out_of_bounds_exception(env, off); \ return retval; \ } \ if (off + len > size) { \ fcfs_jni_throw_out_of_bounds_exception(env, off + len); \ return retval; \ } \ } while (0) #define FILE_CHECK_ARRAY_BOUNDS(b, off, len, size) \ FILE_CHECK_ARRAY_BOUNDS_EX(b, off, len, size, ) void JNICALL Java_com_fastken_fcfs_FCFSFile_write (JNIEnv *env, jobject obj, jbyteArray bs, jint off, jint len) { jbyte *ba; jsize size; FILE_OBJ_FETCH_FD(); FILE_CHECK_ARRAY_BOUNDS(bs, off, len, size); ba = (*env)->GetByteArrayElements(env, bs, NULL); if (fcfs_write(fd, ba + off, len) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } (*env)->ReleaseByteArrayElements(env, bs, ba, 0); } jint JNICALL Java_com_fastken_fcfs_FCFSFile_read (JNIEnv *env, jobject obj, jbyteArray bs, jint off, jint len) { jbyte *ba; jsize size; int bytes; FILE_OBJ_FETCH_FD(-1); FILE_CHECK_ARRAY_BOUNDS_EX(bs, off, len, size, -1); ba = (*env)->GetByteArrayElements(env, bs, NULL); if ((bytes=fcfs_read(fd, ba + off, len)) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } (*env)->ReleaseByteArrayElements(env, bs, ba, 0); return bytes; } jlong JNICALL Java_com_fastken_fcfs_FCFSFile_lseek (JNIEnv *env, jobject obj, jlong offset, jint whence) { jlong position; FILE_OBJ_FETCH_FD(-1); if ((position=fcfs_lseek(fd, offset, whence)) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } return position; } void JNICALL Java_com_fastken_fcfs_FCFSFile_allocate (JNIEnv *env, jobject obj, jint mode, jlong offset, jlong length) { FILE_OBJ_FETCH_FD(); if (fcfs_fallocate(fd, mode, offset, length) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } } void JNICALL Java_com_fastken_fcfs_FCFSFile_truncate (JNIEnv *env, jobject obj, jlong length) { FILE_OBJ_FETCH_FD(); if (fcfs_ftruncate(fd, length) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } } jboolean JNICALL Java_com_fastken_fcfs_FCFSFile_lock (JNIEnv *env, jobject obj, jlong position, jlong length, jboolean shared, jboolean blocked) { struct flock lock; int result; FILE_OBJ_FETCH_FD(false); lock.l_type = (shared ? F_RDLCK : F_WRLCK); lock.l_whence = SEEK_SET; lock.l_start = position; lock.l_len = length; lock.l_pid = getpid(); if (fcfs_fcntl(fd, (blocked ? F_SETLKW : F_SETLK), &lock) < 0) { result = errno != 0 ? errno : EIO; if (blocked || result != EWOULDBLOCK) { throw_file_exception(env, fd, result); } return false; } return true; } void JNICALL Java_com_fastken_fcfs_FCFSFile_unlock (JNIEnv *env, jobject obj, jlong position, jlong length) { struct flock lock; FILE_OBJ_FETCH_FD(); lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = position; lock.l_len = length; lock.l_pid = getpid(); if (fcfs_fcntl(fd, F_SETLK, &lock) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } } void JNICALL Java_com_fastken_fcfs_FCFSFile_utimes (JNIEnv *env, jobject obj, jlong atime, jlong mtime) { struct timeval times[2]; FILE_OBJ_FETCH_FD(); times[0].tv_sec = atime / 1000; times[0].tv_usec = (atime % 1000) * 1000; times[1].tv_sec = mtime / 1000; times[1].tv_usec = (mtime % 1000) * 1000; if (fcfs_futimes(fd, times) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } } void JNICALL Java_com_fastken_fcfs_FCFSFile_chown (JNIEnv *env, jobject obj, jint owner, jint group) { FILE_OBJ_FETCH_FD(); if (fcfs_fchown(fd, owner, group) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } } void JNICALL Java_com_fastken_fcfs_FCFSFile_chmod (JNIEnv *env, jobject obj, jint mode) { FILE_OBJ_FETCH_FD(); if (fcfs_fchmod(fd, mode) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } } void JNICALL Java_com_fastken_fcfs_FCFSFile_setxattr (JNIEnv *env, jobject obj, jstring jname, jbyteArray bs, jint off, jint len, jint flags) { const char *name; jbyte *ba; jsize size; FILE_OBJ_FETCH_FD(); FILE_CHECK_ARRAY_BOUNDS(bs, off, len, size); ba = (*env)->GetByteArrayElements(env, bs, NULL); name = (*env)->GetStringUTFChars(env, jname, NULL); if (fcfs_fsetxattr(fd, name, ba + off, len, fcfs_jni_convert_setxattr_flags(flags)) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jname, name); (*env)->ReleaseByteArrayElements(env, bs, ba, 0); } void JNICALL Java_com_fastken_fcfs_FCFSFile_removexattr (JNIEnv *env, jobject obj, jstring jname) { const char *name; FILE_OBJ_FETCH_FD(); name = (*env)->GetStringUTFChars(env, jname, NULL); if (fcfs_fremovexattr(fd, name) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jname, name); } void JNICALL Java_com_fastken_fcfs_FCFSFile_chdir (JNIEnv *env, jobject obj) { FILE_OBJ_FETCH_FD(); if (fcfs_fchdir(fd) < 0) { throw_file_exception(env, fd, errno != 0 ? errno : EIO); } } ================================================ FILE: src/java/jni/com_fastken_fcfs_FCFSFile.h ================================================ /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_fastken_fcfs_FCFSFile */ #ifndef _Included_com_fastken_fcfs_FCFSFile #define _Included_com_fastken_fcfs_FCFSFile #ifdef __cplusplus extern "C" { #endif /* * Class: com_fastken_fcfs_FCFSFile * Method: doInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_doInit (JNIEnv *, jclass); /* * Class: com_fastken_fcfs_FCFSFile * Method: close * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_close (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSFile * Method: sync * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_sync (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSFile * Method: datasync * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_datasync (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSFile * Method: write * Signature: ([BII)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_write (JNIEnv *, jobject, jbyteArray, jint, jint); /* * Class: com_fastken_fcfs_FCFSFile * Method: read * Signature: ([BII)I */ JNIEXPORT jint JNICALL Java_com_fastken_fcfs_FCFSFile_read (JNIEnv *, jobject, jbyteArray, jint, jint); /* * Class: com_fastken_fcfs_FCFSFile * Method: lseek * Signature: (JI)J */ JNIEXPORT jlong JNICALL Java_com_fastken_fcfs_FCFSFile_lseek (JNIEnv *, jobject, jlong, jint); /* * Class: com_fastken_fcfs_FCFSFile * Method: allocate * Signature: (IJJ)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_allocate (JNIEnv *, jobject, jint, jlong, jlong); /* * Class: com_fastken_fcfs_FCFSFile * Method: truncate * Signature: (J)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_truncate (JNIEnv *, jobject, jlong); /* * Class: com_fastken_fcfs_FCFSFile * Method: stat * Signature: ()Lcom/fastken/fcfs/FCFSFileStat; */ JNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSFile_stat (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSFile * Method: lock * Signature: (JJZZ)Z */ JNIEXPORT jboolean JNICALL Java_com_fastken_fcfs_FCFSFile_lock (JNIEnv *, jobject, jlong, jlong, jboolean, jboolean); /* * Class: com_fastken_fcfs_FCFSFile * Method: unlock * Signature: (JJ)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_unlock (JNIEnv *, jobject, jlong, jlong); /* * Class: com_fastken_fcfs_FCFSFile * Method: utimes * Signature: (JJ)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_utimes (JNIEnv *, jobject, jlong, jlong); /* * Class: com_fastken_fcfs_FCFSFile * Method: chown * Signature: (II)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_chown (JNIEnv *, jobject, jint, jint); /* * Class: com_fastken_fcfs_FCFSFile * Method: chmod * Signature: (I)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_chmod (JNIEnv *, jobject, jint); /* * Class: com_fastken_fcfs_FCFSFile * Method: setxattr * Signature: (Ljava/lang/String;[BIII)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_setxattr (JNIEnv *, jobject, jstring, jbyteArray, jint, jint, jint); /* * Class: com_fastken_fcfs_FCFSFile * Method: removexattr * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_removexattr (JNIEnv *, jobject, jstring); /* * Class: com_fastken_fcfs_FCFSFile * Method: getxattr * Signature: (Ljava/lang/String;)[B */ JNIEXPORT jbyteArray JNICALL Java_com_fastken_fcfs_FCFSFile_getxattr (JNIEnv *, jobject, jstring); /* * Class: com_fastken_fcfs_FCFSFile * Method: listxattr * Signature: ()Ljava/util/List; */ JNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSFile_listxattr (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSFile * Method: chdir * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_chdir (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSFile * Method: statvfs * Signature: ()Lcom/fastken/fcfs/FCFSVFSStat; */ JNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSFile_statvfs (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/java/jni/com_fastken_fcfs_FCFSPosixAPI.c ================================================ #include "fastcfs/api/std/posix_api.h" #include "global.h" #include "common.h" #include "com_fastken_fcfs_FCFSPosixAPI.h" void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_init (JNIEnv *env, jobject obj, jstring poolname, jstring filename) { const char *log_prefix_name = "papi"; char *ns; char *config_filename; FCFSPosixAPIContext *ctx; int result; ctx = malloc(sizeof(FCFSPosixAPIContext)); if (ctx == NULL) { fcfs_jni_throw_exception(env, "Out of Memory"); return; } ns = (char *)((*env)->GetStringUTFChars(env, poolname, NULL)); config_filename = (char *)((*env)->GetStringUTFChars( env, filename, NULL)); result = fcfs_posix_api_init_start_ex(ctx, log_prefix_name, ns, config_filename); (*env)->ReleaseStringUTFChars(env, poolname, ns); (*env)->ReleaseStringUTFChars(env, filename, config_filename); if (result != 0) { free(ctx); fcfs_jni_throw_filesystem_exception(env, "/", result); return; } (*env)->CallVoidMethod(env, obj, g_fcfs_jni_global_vars. papi.setHandler, (long)ctx); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_destroy (JNIEnv *env, jobject obj) { long handler; FCFSPosixAPIContext *ctx; handler = (*env)->CallLongMethod(env, obj, g_fcfs_jni_global_vars.papi.getHandler); if (handler == 0) { return; } ctx = (FCFSPosixAPIContext *)handler; fcfs_posix_api_stop_ex(ctx); fcfs_posix_api_destroy_ex(ctx); free(ctx); (*env)->CallVoidMethod(env, obj, g_fcfs_jni_global_vars. papi.setHandler, 0); } #define PAPI_SET_CTX_AND_PATH_EX(retval, path) \ long handler; \ const char *path; \ FCFSPosixAPIContext *ctx; \ \ handler = (*env)->CallLongMethod(env, obj, \ g_fcfs_jni_global_vars.papi.getHandler); \ if (handler == 0) { \ fcfs_jni_throw_null_pointer_exception(env); \ return retval; \ } \ \ ctx = (FCFSPosixAPIContext *)handler; \ path = (*env)->GetStringUTFChars(env, j##path, NULL) #define PAPI_SET_CTX_AND_PATH(retval) \ PAPI_SET_CTX_AND_PATH_EX(retval, path) jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_opendir (JNIEnv *env, jobject obj, jstring jpath) { DIR *dir; PAPI_SET_CTX_AND_PATH(NULL); if ((dir=fcfs_opendir_ex(ctx, path)) == NULL) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : ENOENT); (*env)->ReleaseStringUTFChars(env, jpath, path); return NULL; } (*env)->ReleaseStringUTFChars(env, jpath, path); return (*env)->NewObject(env, g_fcfs_jni_global_vars.dir.clazz, g_fcfs_jni_global_vars.dir.constructor1, (long)dir); } jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_open (JNIEnv *env, jobject obj, jstring jpath, jint flags, jint mode) { int fd; PAPI_SET_CTX_AND_PATH(NULL); if ((fd=fcfs_open_ex(ctx, path, fcfs_jni_convert_open_flags(flags), mode, fcfs_papi_tpid_type_tid)) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : ENOENT); (*env)->ReleaseStringUTFChars(env, jpath, path); return NULL; } (*env)->ReleaseStringUTFChars(env, jpath, path); return (*env)->NewObject(env, g_fcfs_jni_global_vars.file.clazz, g_fcfs_jni_global_vars.file.constructor1, fd); } jstring JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_getcwd (JNIEnv *env, jobject obj) { long handler; char path[PATH_MAX]; FCFSPosixAPIContext *ctx; handler = (*env)->CallLongMethod(env, obj, g_fcfs_jni_global_vars.papi.getHandler); if (handler == 0) { fcfs_jni_throw_null_pointer_exception(env); return NULL; } ctx = (FCFSPosixAPIContext *)handler; if (fcfs_getcwd_ex(ctx, path, sizeof(path)) == NULL) { fcfs_jni_throw_filesystem_exception(env, "/", errno != 0 ? errno : ENAMETOOLONG); return NULL; } return (*env)->NewStringUTF(env, path); } jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_stat (JNIEnv *env, jobject obj, jstring jpath, jboolean followlink) { int ret; struct stat stat; PAPI_SET_CTX_AND_PATH(NULL); if (followlink) { ret = fcfs_stat_ex(ctx, path, &stat); } else { ret = fcfs_lstat_ex(ctx, path, &stat); } if (ret != 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : ENOENT); (*env)->ReleaseStringUTFChars(env, jpath, path); return NULL; } (*env)->ReleaseStringUTFChars(env, jpath, path); return (*env)->NewObject(env, g_fcfs_jni_global_vars.fstat.clazz, g_fcfs_jni_global_vars.fstat.constructor10, stat.st_ino, stat.st_mode, stat.st_nlink, stat.st_uid, stat.st_gid, stat.st_rdev, stat.st_size, (jlong)stat.st_atime * 1000LL, (jlong)stat.st_mtime * 1000LL, (jlong)stat.st_ctime * 1000LL); } jstring JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_readlink (JNIEnv *env, jobject obj, jstring jpath) { char buff[PATH_MAX]; PAPI_SET_CTX_AND_PATH(NULL); if (fcfs_readlink_ex(ctx, path, buff, sizeof(buff)) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : ENOENT); (*env)->ReleaseStringUTFChars(env, jpath, path); return NULL; } (*env)->ReleaseStringUTFChars(env, jpath, path); return (*env)->NewStringUTF(env, buff); } jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_statvfs (JNIEnv *env, jobject obj, jstring jpath) { struct statvfs stat; SFSpaceStat space; PAPI_SET_CTX_AND_PATH(NULL); if (fcfs_statvfs_ex(ctx, path, &stat) != 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : ENOENT); (*env)->ReleaseStringUTFChars(env, jpath, path); return NULL; } (*env)->ReleaseStringUTFChars(env, jpath, path); space.total = (int64_t)stat.f_blocks * stat.f_bsize; space.avail = (int64_t)stat.f_bavail * stat.f_bsize; space.used = space.total - space.avail; return (*env)->NewObject(env, g_fcfs_jni_global_vars.statvfs.clazz, g_fcfs_jni_global_vars.statvfs.constructor6, space.total, space.avail, space.used, stat.f_files, stat.f_favail, stat.f_files - stat.f_favail); } jbyteArray JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_getxattr (JNIEnv *env, jobject obj, jstring jpath, jstring jname, jboolean followlink) { const char *name; char holder[4 * 1024]; char *buff; int result; int size; int length; jbyteArray value; PAPI_SET_CTX_AND_PATH(NULL); name = (*env)->GetStringUTFChars(env, jname, NULL); buff = holder; size = sizeof(holder); if (followlink) { length = fcfs_getxattr_ex(ctx, path, name, buff, size); } else { length = fcfs_lgetxattr_ex(ctx, path, name, buff, size); } if (length < 0) { result = errno != 0 ? errno : ENOENT; if (result == EOVERFLOW) { if (followlink) { size = fcfs_getxattr_ex(ctx, path, name, buff, 0); } else { size = fcfs_lgetxattr_ex(ctx, path, name, buff, 0); } if (size < 0) { result = errno != 0 ? errno : ENOENT; } else { if ((buff=malloc(size)) == NULL) { result = ENOMEM; } else { if (followlink) { length = fcfs_getxattr_ex(ctx, path, name, buff, size); } else { length = fcfs_lgetxattr_ex(ctx, path, name, buff, size); } if (length < 0) { result = errno != 0 ? errno : ENOENT; } else { result = 0; } } } } if (result != 0) { fcfs_jni_throw_filesystem_exception(env, path, result); (*env)->ReleaseStringUTFChars(env, jpath, path); (*env)->ReleaseStringUTFChars(env, jname, name); return NULL; } } (*env)->ReleaseStringUTFChars(env, jpath, path); (*env)->ReleaseStringUTFChars(env, jname, name); value = (*env)->NewByteArray(env, length); (*env)->SetByteArrayRegion(env, value, 0, length, (const jbyte *)buff); if (buff != holder) { free(buff); } return value; } jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_listxattr (JNIEnv *env, jobject obj, jstring jpath, jboolean followlink) { char holder[4 * 1024]; char *buff; int result; int size; int length; jobject list_obj; PAPI_SET_CTX_AND_PATH(NULL); buff = holder; size = sizeof(holder); if (followlink) { length = fcfs_listxattr_ex(ctx, path, buff, size); } else { length = fcfs_llistxattr_ex(ctx, path, buff, size); } if (length < 0) { result = errno != 0 ? errno : ENOENT; if (result == EOVERFLOW) { if (followlink) { size = fcfs_listxattr_ex(ctx, path, buff, 0); } else { size = fcfs_llistxattr_ex(ctx, path, buff, 0); } if (size < 0) { result = errno != 0 ? errno : ENOENT; } else { if ((buff=malloc(size)) == NULL) { result = ENOMEM; } else { if (followlink) { length = fcfs_listxattr_ex(ctx, path, buff, size); } else { length = fcfs_llistxattr_ex(ctx, path, buff, size); } if (length < 0) { result = errno != 0 ? errno : ENOENT; } else { result = 0; } } } } if (result != 0) { fcfs_jni_throw_filesystem_exception(env, path, result); (*env)->ReleaseStringUTFChars(env, jpath, path); return NULL; } } (*env)->ReleaseStringUTFChars(env, jpath, path); list_obj = fcfs_jni_convert_to_list(env, buff, length); if (buff != holder) { free(buff); } return list_obj; } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_truncate (JNIEnv *env, jobject obj, jstring jpath, jlong length) { PAPI_SET_CTX_AND_PATH(); if (fcfs_truncate_ex(ctx, path, length) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_link (JNIEnv *env, jobject obj, jstring jpath1, jstring jpath2) { const char *path2; const char *path; PAPI_SET_CTX_AND_PATH_EX(, path1); path2 = (*env)->GetStringUTFChars(env, jpath2, NULL); if (fcfs_link_ex(ctx, path1, path2) < 0) { if (errno == ENOENT) { path = path1; } else { path = path2; } fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath1, path1); (*env)->ReleaseStringUTFChars(env, jpath2, path2); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_symlink (JNIEnv *env, jobject obj, jstring jlink, jstring jpath) { const char *path; PAPI_SET_CTX_AND_PATH_EX(, link); path = (*env)->GetStringUTFChars(env, jpath, NULL); if (fcfs_symlink_ex(ctx, link, path) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jlink, link); (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_rename (JNIEnv *env, jobject obj, jstring jpath1, jstring jpath2) { const char *path2; const char *path; PAPI_SET_CTX_AND_PATH_EX(, path1); path2 = (*env)->GetStringUTFChars(env, jpath2, NULL); if (fcfs_rename_ex(ctx, path1, path2) < 0) { if (errno == ENOENT) { path = path1; } else { path = path2; } fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath1, path1); (*env)->ReleaseStringUTFChars(env, jpath2, path2); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mknod (JNIEnv *env, jobject obj, jstring jpath, jint mode, jint dev) { PAPI_SET_CTX_AND_PATH(); if (fcfs_mknod_ex(ctx, path, mode, dev) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mkfifo (JNIEnv *env, jobject obj, jstring jpath, jint mode) { PAPI_SET_CTX_AND_PATH(); if (fcfs_mkfifo_ex(ctx, path, mode) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_access (JNIEnv *env, jobject obj, jstring jpath, jint mode) { PAPI_SET_CTX_AND_PATH(); if (fcfs_access_ex(ctx, path, mode) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath, path); } jboolean JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_exists (JNIEnv *env, jobject obj, jstring jpath) { jboolean ret; PAPI_SET_CTX_AND_PATH(false); if (fcfs_access_ex(ctx, path, F_OK) == 0) { ret = true; } else { if (errno != ENOENT) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } ret = false; } (*env)->ReleaseStringUTFChars(env, jpath, path); return ret; } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_utimes (JNIEnv *env, jobject obj, jstring jpath, jlong atime, jlong mtime) { struct timeval times[2]; PAPI_SET_CTX_AND_PATH(); times[0].tv_sec = atime / 1000; times[0].tv_usec = (atime % 1000) * 1000; times[1].tv_sec = mtime / 1000; times[1].tv_usec = (mtime % 1000) * 1000; if (fcfs_utimes_ex(ctx, path, times) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_unlink (JNIEnv *env, jobject obj, jstring jpath) { PAPI_SET_CTX_AND_PATH(); if (fcfs_unlink_ex(ctx, path) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mkdir (JNIEnv *env, jobject obj, jstring jpath, jint mode) { PAPI_SET_CTX_AND_PATH(); if (fcfs_mkdir_ex(ctx, path, mode) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_rmdir (JNIEnv *env, jobject obj, jstring jpath) { PAPI_SET_CTX_AND_PATH(); if (fcfs_rmdir_ex(ctx, path) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chown (JNIEnv *env, jobject obj, jstring jpath, jint owner, jint group, jboolean followlink) { int ret; PAPI_SET_CTX_AND_PATH(); if (followlink) { ret = fcfs_chown_ex(ctx, path, owner, group); } else { ret = fcfs_lchown_ex(ctx, path, owner, group); } if (ret != 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : ENOENT); } (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chmod (JNIEnv *env, jobject obj, jstring jpath, jint mode) { PAPI_SET_CTX_AND_PATH(); if (fcfs_chmod_ex(ctx, path, mode) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chdir (JNIEnv *env, jobject obj, jstring jpath) { PAPI_SET_CTX_AND_PATH(); if (fcfs_chdir_ex(ctx, path) < 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : EIO); } (*env)->ReleaseStringUTFChars(env, jpath, path); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_setxattr (JNIEnv *env, jobject obj, jstring jpath, jstring jname, jbyteArray b, jint off, jint len, jint flags, jboolean followlink) { const char *name; int ret; jbyte *ba; jsize size; PAPI_SET_CTX_AND_PATH(); size = (*env)->GetArrayLength(env, b); if (off < 0 || off >= size) { fcfs_jni_throw_out_of_bounds_exception(env, off); (*env)->ReleaseStringUTFChars(env, jpath, path); return; } if (off + len > size) { fcfs_jni_throw_out_of_bounds_exception(env, off + len); (*env)->ReleaseStringUTFChars(env, jpath, path); return; } name = (*env)->GetStringUTFChars(env, jname, NULL); ba = (*env)->GetByteArrayElements(env, b, NULL); if (followlink) { ret = fcfs_setxattr_ex(ctx, path, name, ba + off, len, fcfs_jni_convert_setxattr_flags(flags)); } else { ret = fcfs_lsetxattr_ex(ctx, path, name, ba + off, len, fcfs_jni_convert_setxattr_flags(flags)); } if (ret != 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : ENOENT); } (*env)->ReleaseStringUTFChars(env, jpath, path); (*env)->ReleaseStringUTFChars(env, jname, name); (*env)->ReleaseByteArrayElements(env, b, ba, 0); } void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_removexattr (JNIEnv *env, jobject obj, jstring jpath, jstring jname, jboolean followlink) { const char *name; int ret; PAPI_SET_CTX_AND_PATH(); name = (*env)->GetStringUTFChars(env, jname, NULL); if (followlink) { ret = fcfs_removexattr_ex(ctx, path, name); } else { ret = fcfs_lremovexattr_ex(ctx, path, name); } if (ret != 0) { fcfs_jni_throw_filesystem_exception(env, path, errno != 0 ? errno : ENOENT); } (*env)->ReleaseStringUTFChars(env, jpath, path); (*env)->ReleaseStringUTFChars(env, jname, name); } ================================================ FILE: src/java/jni/com_fastken_fcfs_FCFSPosixAPI.h ================================================ /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_fastken_fcfs_FCFSPosixAPI */ #ifndef _Included_com_fastken_fcfs_FCFSPosixAPI #define _Included_com_fastken_fcfs_FCFSPosixAPI #ifdef __cplusplus extern "C" { #endif /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: doInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_doInit (JNIEnv *, jclass); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: init * Signature: (Ljava/lang/String;Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_init (JNIEnv *, jobject, jstring, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: destroy * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_destroy (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: opendir * Signature: (Ljava/lang/String;)Lcom/fastken/fcfs/FCFSDirectory; */ JNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_opendir (JNIEnv *, jobject, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: open * Signature: (Ljava/lang/String;II)Lcom/fastken/fcfs/FCFSFile; */ JNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_open (JNIEnv *, jobject, jstring, jint, jint); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: getcwd * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_getcwd (JNIEnv *, jobject); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: truncate * Signature: (Ljava/lang/String;J)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_truncate (JNIEnv *, jobject, jstring, jlong); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: stat * Signature: (Ljava/lang/String;Z)Lcom/fastken/fcfs/FCFSFileStat; */ JNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_stat (JNIEnv *, jobject, jstring, jboolean); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: link * Signature: (Ljava/lang/String;Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_link (JNIEnv *, jobject, jstring, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: symlink * Signature: (Ljava/lang/String;Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_symlink (JNIEnv *, jobject, jstring, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: readlink * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_readlink (JNIEnv *, jobject, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: mknod * Signature: (Ljava/lang/String;II)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mknod (JNIEnv *, jobject, jstring, jint, jint); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: mkfifo * Signature: (Ljava/lang/String;I)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mkfifo (JNIEnv *, jobject, jstring, jint); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: access * Signature: (Ljava/lang/String;I)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_access (JNIEnv *, jobject, jstring, jint); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: exists * Signature: (Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_exists (JNIEnv *, jobject, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: utimes * Signature: (Ljava/lang/String;JJ)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_utimes (JNIEnv *, jobject, jstring, jlong, jlong); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: unlink * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_unlink (JNIEnv *, jobject, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: rename * Signature: (Ljava/lang/String;Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_rename (JNIEnv *, jobject, jstring, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: mkdir * Signature: (Ljava/lang/String;I)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mkdir (JNIEnv *, jobject, jstring, jint); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: rmdir * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_rmdir (JNIEnv *, jobject, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: chown * Signature: (Ljava/lang/String;IIZ)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chown (JNIEnv *, jobject, jstring, jint, jint, jboolean); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: chmod * Signature: (Ljava/lang/String;I)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chmod (JNIEnv *, jobject, jstring, jint); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: statvfs * Signature: (Ljava/lang/String;)Lcom/fastken/fcfs/FCFSVFSStat; */ JNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_statvfs (JNIEnv *, jobject, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: chdir * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chdir (JNIEnv *, jobject, jstring); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: setxattr * Signature: (Ljava/lang/String;Ljava/lang/String;[BIIIZ)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_setxattr (JNIEnv *, jobject, jstring, jstring, jbyteArray, jint, jint, jint, jboolean); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: removexattr * Signature: (Ljava/lang/String;Ljava/lang/String;Z)V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_removexattr (JNIEnv *, jobject, jstring, jstring, jboolean); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: getxattr * Signature: (Ljava/lang/String;Ljava/lang/String;Z)[B */ JNIEXPORT jbyteArray JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_getxattr (JNIEnv *, jobject, jstring, jstring, jboolean); /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: listxattr * Signature: (Ljava/lang/String;Z)Ljava/util/List; */ JNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_listxattr (JNIEnv *, jobject, jstring, jboolean); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/java/jni/common.c ================================================ /* * Copyright (c) 2022 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include "fastcommon/common_define.h" #include "com_fastken_fcfs_FCFSConstants.h" #include "common.h" void fcfs_jni_throw_exception(JNIEnv *env, const char *message) { jclass clazz; clazz = (*env)->FindClass(env, "java/lang/Exception"); if (clazz == NULL) { return; } (*env)->ThrowNew(env, clazz, message); } void fcfs_jni_throw_null_pointer_exception(JNIEnv *env) { jclass clazz; clazz = (*env)->FindClass(env, "java/lang/NullPointerException"); if (clazz == NULL) { return; } (*env)->ThrowNew(env, clazz, "null pointer"); } void fcfs_jni_throw_out_of_bounds_exception(JNIEnv *env, const int index) { jclass clazz; jmethodID constructor1; clazz = (*env)->FindClass(env, "java/lang/ArrayIndexOutOfBoundsException"); if (clazz == NULL) { return; } constructor1 = (*env)->GetMethodID(env, clazz, "", "(I)V"); (*env)->Throw(env, (*env)->NewObject(env, clazz, constructor1, index)); } void fcfs_jni_throw_filesystem_exception(JNIEnv *env, const char *file, const int err_no) { const char *cname; bool need_error; jclass clazz; switch (err_no) { case ENOENT: cname = "java/nio/file/NoSuchFileException"; need_error = false; break; case EEXIST: cname = "java/nio/file/FileAlreadyExistsException"; need_error = false; break; case EPERM: cname = "java/nio/file/AccessDeniedException"; need_error = false; break; case ENOTEMPTY: cname = "java/nio/file/DirectoryNotEmptyException"; need_error = false; break; case ELOOP: cname = "java/nio/file/FileSystemLoopException"; need_error = false; break; case ENOTDIR: cname = "java/nio/file/NotDirectoryException"; need_error = false; break; case ENOLINK: cname = "java/nio/file/NotLinkException"; need_error = false; break; default: cname = "java/nio/file/FileSystemException"; need_error = true; break; } clazz = (*env)->FindClass(env, cname); if (clazz == NULL) { return; } if (need_error) { jmethodID constructor3; jobject obj; constructor3 = (*env)->GetMethodID(env, clazz, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); obj = (*env)->NewObject(env, clazz, constructor3, (*env)->NewStringUTF(env, file), NULL, (*env)->NewStringUTF(env, strerror(err_no))); (*env)->Throw(env, obj); } else { (*env)->ThrowNew(env, clazz, file); } } jobject fcfs_jni_convert_to_list(JNIEnv *env, const char *buff, const int length) { jclass lclazz; jobject lobj; jmethodID lconstructor; jmethodID ladd; const char *name; const char *end; lclazz = (*env)->FindClass(env, "java/util/ArrayList"); lconstructor = (*env)->GetMethodID(env, lclazz, "", "()V"); lobj = (*env)->NewObject(env, lclazz, lconstructor); if (length == 0) { return lobj; } ladd = (*env)->GetMethodID(env, lclazz, "add", "(Ljava/lang/Object;)Z"); name = buff; end = buff + length; do { (*env)->CallBooleanMethod(env, lobj, ladd, (*env)->NewStringUTF(env, name)); name += strlen(name) + 1; } while (name < end); return lobj; } int fcfs_jni_convert_open_flags(const int flags) { int new_flags; new_flags = 0; if ((flags & com_fastken_fcfs_FCFSConstants_O_WRONLY)) { new_flags |= O_WRONLY; } if ((flags & com_fastken_fcfs_FCFSConstants_O_RDWR)) { new_flags |= O_RDWR; } if ((flags & com_fastken_fcfs_FCFSConstants_O_CREAT)) { new_flags |= O_CREAT; } if ((flags & com_fastken_fcfs_FCFSConstants_O_EXCL)) { new_flags |= O_EXCL; } if ((flags & com_fastken_fcfs_FCFSConstants_O_TRUNC)) { new_flags |= O_TRUNC; } if ((flags & com_fastken_fcfs_FCFSConstants_O_APPEND)) { new_flags |= O_APPEND; } if ((flags & com_fastken_fcfs_FCFSConstants_O_NOFOLLOW)) { new_flags |= O_NOFOLLOW; } if ((flags & com_fastken_fcfs_FCFSConstants_O_CLOEXEC)) { new_flags |= O_CLOEXEC; } return new_flags; } int fcfs_jni_convert_setxattr_flags(const int flags) { int new_flags; new_flags = 0; if ((flags & com_fastken_fcfs_FCFSConstants_XATTR_CREATE)) { new_flags |= XATTR_CREATE; } if ((flags & com_fastken_fcfs_FCFSConstants_XATTR_REPLACE)) { new_flags |= XATTR_REPLACE; } return new_flags; } ================================================ FILE: src/java/jni/common.h ================================================ /* * Copyright (c) 2022 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_JNI_COMMON_H #define _FCFS_JNI_COMMON_H #include #ifdef __cplusplus extern "C" { #endif void fcfs_jni_throw_exception(JNIEnv *env, const char *message); void fcfs_jni_throw_null_pointer_exception(JNIEnv *env); void fcfs_jni_throw_out_of_bounds_exception(JNIEnv *env, const int index); void fcfs_jni_throw_filesystem_exception(JNIEnv *env, const char *file, const int err_no); jobject fcfs_jni_convert_to_list(JNIEnv *env, const char *buff, const int length); int fcfs_jni_convert_open_flags(const int flags); int fcfs_jni_convert_setxattr_flags(const int flags); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/java/jni/global.c ================================================ /* * Copyright (c) 2022 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "fastcommon/common_define.h" #include "global.h" FCFSJNIGlobalVars g_fcfs_jni_global_vars = {{NULL, NULL}}; void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_doInit (JNIEnv *env, jclass clazz) { g_fcfs_jni_global_vars.papi.setHandler = (*env)->GetMethodID( env, clazz, "setHandler", "(J)V"); g_fcfs_jni_global_vars.papi.getHandler = (*env)->GetMethodID( env, clazz, "getHandler", "()J"); g_fcfs_jni_global_vars.dir.clazz = (*env)->FindClass(env, "com/fastken/fcfs/FCFSDirectory"); g_fcfs_jni_global_vars.dir.clazz = (*env)->NewGlobalRef(env, g_fcfs_jni_global_vars.dir.clazz); g_fcfs_jni_global_vars.file.clazz = (*env)->FindClass(env, "com/fastken/fcfs/FCFSFile"); g_fcfs_jni_global_vars.file.clazz = (*env)->NewGlobalRef(env, g_fcfs_jni_global_vars.file.clazz); g_fcfs_jni_global_vars.statvfs.clazz = (*env)->FindClass(env, "com/fastken/fcfs/FCFSVFSStat"); g_fcfs_jni_global_vars.statvfs.clazz = (*env)->NewGlobalRef(env, g_fcfs_jni_global_vars.statvfs.clazz); } void JNICALL Java_com_fastken_fcfs_FCFSDirectory_doInit (JNIEnv *env, jclass clazz) { g_fcfs_jni_global_vars.dir.constructor1 = (*env)->GetMethodID( env, clazz, "", "(J)V"); g_fcfs_jni_global_vars.dir.setHandler = (*env)->GetMethodID( env, clazz, "setHandler", "(J)V"); g_fcfs_jni_global_vars.dir.getHandler = (*env)->GetMethodID( env, clazz, "getHandler", "()J"); g_fcfs_jni_global_vars.dirent.clazz = (*env)->FindClass(env, "com/fastken/fcfs/FCFSDirectory$Entry"); g_fcfs_jni_global_vars.dirent.clazz = (*env)->NewGlobalRef(env, g_fcfs_jni_global_vars.dirent.clazz); } void JNICALL Java_com_fastken_fcfs_FCFSDirectory_00024Entry_doInit (JNIEnv *env, jclass clazz) { g_fcfs_jni_global_vars.dirent.constructor2 = (*env)->GetMethodID( env, clazz, "", "(JLjava/lang/String;)V"); } void JNICALL Java_com_fastken_fcfs_FCFSFile_doInit (JNIEnv *env, jclass clazz) { g_fcfs_jni_global_vars.file.constructor1 = (*env)->GetMethodID( env, clazz, "", "(I)V"); g_fcfs_jni_global_vars.file.setFD = (*env)->GetMethodID( env, clazz, "setFD", "(I)V"); g_fcfs_jni_global_vars.file.getFD = (*env)->GetMethodID( env, clazz, "getFD", "()I"); g_fcfs_jni_global_vars.fstat.clazz = (*env)->FindClass(env, "com/fastken/fcfs/FCFSFileStat"); g_fcfs_jni_global_vars.fstat.clazz = (*env)->NewGlobalRef(env, g_fcfs_jni_global_vars.fstat.clazz); } void JNICALL Java_com_fastken_fcfs_FCFSFileStat_doInit (JNIEnv *env, jclass clazz) { g_fcfs_jni_global_vars.fstat.constructor10 = (*env)->GetMethodID( env, clazz, "", "(JIIIIIJJJJ)V"); } void JNICALL Java_com_fastken_fcfs_FCFSVFSStat_doInit (JNIEnv *env, jclass clazz) { g_fcfs_jni_global_vars.statvfs.constructor6 = (*env)->GetMethodID( env, clazz, "", "(JJJJJJ)V"); } ================================================ FILE: src/java/jni/global.h ================================================ /* * Copyright (c) 2022 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_JNI_GLOBAL_H #define _FCFS_JNI_GLOBAL_H #include typedef struct { struct { jmethodID setHandler; jmethodID getHandler; } papi; struct { jclass clazz; jmethodID constructor1; jmethodID setHandler; jmethodID getHandler; } dir; struct { jclass clazz; jmethodID constructor1; jmethodID setFD; jmethodID getFD; } file; struct { jclass clazz; jmethodID constructor2; } dirent; struct { jclass clazz; jmethodID constructor10; } fstat; struct { jclass clazz; jmethodID constructor6; } statvfs; } FCFSJNIGlobalVars; #ifdef __cplusplus extern "C" { #endif extern FCFSJNIGlobalVars g_fcfs_jni_global_vars; /* * Class: com_fastken_fcfs_FCFSPosixAPI * Method: doInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_doInit (JNIEnv *, jclass); /* * Class: com_fastken_fcfs_FCFSDirectory * Method: doInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_doInit (JNIEnv *, jclass); /* * Class: com_fastken_fcfs_FCFSDirectory_Entry * Method: doInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_00024Entry_doInit (JNIEnv *, jclass); /* * Class: com_fastken_fcfs_FCFSFile * Method: doInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_doInit (JNIEnv *, jclass); /* * Class: com_fastken_fcfs_FCFSFileStat * Method: doInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFileStat_doInit (JNIEnv *, jclass); /* * Class: com_fastken_fcfs_FCFSVFSStat * Method: doInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSVFSStat_doInit (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/java/src/main/java/com/fastken/fcfs/FCFSConstants.java ================================================ package com.fastken.fcfs; public class FCFSConstants { //for access mode public static final int F_OK = 0; //check file existence public static final int X_OK = 1; //execute permission public static final int W_OK = 2; //write permission public static final int R_OK = 4; //read permission //for setxattr flags /* perform a pure create operation, which fails if the named attribute exists already. */ public static final int XATTR_CREATE = (1 << 0); /*Perform a pure replace operation, which fails if the named attribute does not already exist. */ public static final int XATTR_REPLACE = (1 << 1); //for lseek whence /* The file offset is set to offset bytes. */ public static final int SEEK_SET = 0; /* The file offset is set to its current location plus offset bytes. */ public static final int SEEK_CUR = 1; /* The file offset is set to the size of the file plus offset bytes. */ public static final int SEEK_END = 2; //for file open flags public static final int O_RDONLY = 00; public static final int O_WRONLY = 01; public static final int O_RDWR = 02; public static final int O_CREAT = 0100; public static final int O_EXCL = 0200; public static final int O_TRUNC = 01000; public static final int O_APPEND = 02000; public static final int O_NOFOLLOW = 0400000; public static final int O_CLOEXEC = 02000000; } ================================================ FILE: src/java/src/main/java/com/fastken/fcfs/FCFSDirectory.java ================================================ package com.fastken.fcfs; import java.nio.file.FileSystemException; public class FCFSDirectory { public static class Entry { private long inode; private String name; private static native void doInit(); static { doInit(); } public Entry(long inode, String name) { this.inode = inode; this.name = name; } public long getInode() { return this.inode; } public String getName() { return this.name; } } private static native void doInit(); static { doInit(); } public native Entry next(); public native void seek(long loc); public native long tell(); public native void rewind(); public native void close(); private long handler; public FCFSDirectory(long handler) { this.handler = handler; } public void setHandler(long handler) { this.handler = handler; } public long getHandler() { return this.handler; } } ================================================ FILE: src/java/src/main/java/com/fastken/fcfs/FCFSFile.java ================================================ package com.fastken.fcfs; import java.util.List; public class FCFSFile { private static native void doInit(); static { doInit(); } public native void close(); public native void sync(); public native void datasync(); public native void write(byte[] bs, int off, int len); public native int read(byte[] bs, int off, int len); public native long lseek(long offset, int whence); public native void allocate(int mode, long offset, long length); public native void truncate(long length); public native FCFSFileStat stat(); public native boolean lock(long position, long length, boolean shared, boolean blocked); public native void unlock(long position, long length); public native void utimes(long atime, long mtime); public native void chown(int owner, int group); public native void chmod(int mode); public native void setxattr(String name, byte[] bs, int off, int len, int flags); public native void removexattr(String name); public native byte[] getxattr(String name); public native List listxattr(); public native void chdir(); public native FCFSVFSStat statvfs(); private int fd; public FCFSFile(int fd) { this.fd = fd; } public void setFD(int fd) { this.fd = fd; } public int getFD() { return this.fd; } public void write(byte[] bs) { this.write(bs, 0, bs.length); } public int read(byte[] bs) { return this.read(bs, 0, bs.length); } public boolean lock(long position, long length, boolean shared) { final boolean blocked = true; return this.lock(position, length, shared, blocked); } public boolean tryLock(long position, long length, boolean shared) { final boolean blocked = false; return this.lock(position, length, shared, blocked); } public void setxattr(String name, byte[] bs, int flags) { this.setxattr(name, bs, 0, bs.length, flags); } public void setxattr(String name, byte[] bs) { final int flags = 0; this.setxattr(name, bs, 0, bs.length, flags); } } ================================================ FILE: src/java/src/main/java/com/fastken/fcfs/FCFSFileStat.java ================================================ package com.fastken.fcfs; public class FCFSFileStat { private static native void doInit(); static { doInit(); } private long inode; /* Inode number */ private int mode; /* File type and mode */ private int links; /* Number of hard links */ private int uid; /* User ID of owner */ private int gid; /* Group ID of owner */ private int rdev; /* Device ID (if special file) */ private long size; /* Total size, in bytes */ private long atime; /* Time of last access */ private long mtime; /* Time of last modification */ private long ctime; /* Time of last status change */ public FCFSFileStat(long inode, int mode, int links, int uid, int gid, int rdev, long size, long atime, long mtime, long ctime) { this.inode = inode; this.mode = mode; this.links = links; this.uid = uid; this.gid = gid; this.rdev = rdev; this.size = size; this.atime = atime; this.mtime = mtime; this.ctime = ctime; } public long getInode() { return this.inode; } public long getMode() { return this.mode; } public long getLinks() { return this.links; } public long getUid() { return this.uid; } public long getGid() { return this.gid; } public long getRdev() { return this.rdev; } public long getSize() { return this.size; } public long getAtime() { return this.atime; } public long getMtime() { return this.mtime; } public long getCtime() { return this.ctime; } public String toString() { return "inode: " + this.inode + ", mode: " + this.mode + ", links: " + this.links + ", uid: " + this.uid + ", gid: " + this.gid + ", rdev: " + this.rdev + ", size: " + this.size + ", atime: " + this.atime + ", mtime: " + this.mtime + ", ctime: " + this.ctime; } } ================================================ FILE: src/java/src/main/java/com/fastken/fcfs/FCFSPosixAPI.java ================================================ package com.fastken.fcfs; import java.util.List; import java.util.Map; import java.util.HashMap; import java.io.File; import java.io.UnsupportedEncodingException; import java.nio.file.FileSystemException; public class FCFSPosixAPI { private static HashMap instances = new HashMap(); private static final String charset = "UTF-8"; private static String libraryFilename = null; private static native void doInit(); private native void init(String ns, String configFilename); private native void destroy(); public native FCFSDirectory opendir(String path); public native FCFSFile open(String path, int flags, int mode); public native String getcwd(); public native void truncate(String path, long length); public native FCFSFileStat stat(String path, boolean followlink); public native void link(String path1, String path2); public native void symlink(String link, String path); public native String readlink(String path); public native void mknod(String path, int mode, int dev); public native void mkfifo(String path, int mode); public native void access(String path, int mode); public native boolean exists(String path); public native void utimes(String path, long atime, long mtime); public native void unlink(String path); public native void rename(String path1, String path2); public native void mkdir(String path, int mode); public native void rmdir(String path); public native void chown(String path, int owner, int group, boolean followlink); public native void chmod(String path, int mode); public native FCFSVFSStat statvfs(String path); public native void chdir(String path); public native void setxattr(String path, String name, byte[] b, int off, int len, int flags, boolean followlink); public native void removexattr(String path, String name, boolean followlink); public native byte[] getxattr(String path, String name, boolean followlink); public native List listxattr(String path, boolean followlink); private long handler; public static String getLibraryFilename() { return libraryFilename; } /** * set libfcfsjni.so filename to load * @param filename the full filename * @return none */ public static void setLibraryFilename(String filename) { if (libraryFilename == null) { System.load(filename); //load the library libraryFilename = filename; doInit(); } else if (libraryFilename.equals(filename)) { System.err.println("[WARNING] library " + libraryFilename + " already loaded"); } else { throw new RuntimeException("library " + libraryFilename + " already loaded, can't change to " + filename); } } // private for singleton private FCFSPosixAPI(String ns, String configFilename) { init(ns, configFilename); } public void setHandler(long handler) { this.handler = handler; } public long getHandler() { return this.handler; } protected void finalize() throws Throwable { super.finalize(); this.close(); } private void close() { System.out.println("close"); destroy(); } /** * get FCFSPosixAPI instance * @param ns the namespace / poolname * @param configFilename the config filename such as /etc/fastcfs/fcfs/fuse.conf * @return FCFSPosixAPI object */ public synchronized static FCFSPosixAPI getInstance(String ns, String configFilename) { String key = ns + "@" + configFilename; FCFSPosixAPI obj = instances.get(key); if (obj == null) { obj = new FCFSPosixAPI(ns, configFilename); instances.put(key, obj); } return obj; } /** * clear FCFSPosixAPI instances * @return none */ public synchronized static void clearInstances() { instances.clear(); } /** * open file for read * @param path the filename to open * @return FCFSFile instance */ public FCFSFile open(String path) { final int mode = 0; return this.open(path, FCFSConstants.O_RDONLY, mode); } public void setxattr(String path, String name, byte[] b, int flags) { final boolean followlink = true; this.setxattr(path, name, b, 0, b.length, flags, followlink); } public void setxattr(String path, String name, byte[] b) { final int flags = 0; final boolean followlink = true; this.setxattr(path, name, b, 0, b.length, flags, followlink); } public void lsetxattr(String path, String name, byte[] b, int flags) { final boolean followlink = false; this.setxattr(path, name, b, 0, b.length, flags, followlink); } public void lsetxattr(String path, String name, byte[] b) { final int flags = 0; final boolean followlink = false; this.setxattr(path, name, b, 0, b.length, flags, followlink); } public FCFSFileStat stat(String path) { final boolean followlink = true; return this.stat(path, followlink); } public FCFSFileStat lstat(String path) { final boolean followlink = false; return this.stat(path, followlink); } public static void main(String[] args) throws Exception { final String ns = "fs"; final String configFilename = "/etc/fastcfs/fcfs/fuse.conf"; final boolean followlink = false; String path; FCFSPosixAPI papi; File f = new File("/usr/lib/libfcfsjni.so"); if (f.exists()) { FCFSPosixAPI.setLibraryFilename(f.getAbsolutePath()); } else { FCFSPosixAPI.setLibraryFilename("/usr/local/lib/libfcfsjni.so"); } papi = FCFSPosixAPI.getInstance(ns, configFilename); path = "/opt/fastcfs/fuse/"; String filename = path + "test.txt"; String filename1 = filename; String filename2 = path + "test2.txt"; FCFSDirectory dir = papi.opendir(path); FCFSDirectory.Entry dirent; FCFSFile file; System.out.println("cwd: " + papi.getcwd()); while ((dirent=dir.next()) != null) { //System.out.println("inode: " + dirent.getInode() + ", name: " + dirent.getName()); } dir.close(); try { papi.unlink(filename1); } catch(Exception ex) { } if (!papi.exists(filename1)) { file = papi.open(filename1, FCFSConstants.O_WRONLY | FCFSConstants.O_CREAT, 0755); file.write(new String("hello world!").getBytes(charset)); file.close(); System.out.println(filename1 + " created"); } try { papi.unlink(filename2); } catch(Exception ex) { } papi.symlink("./test.txt", filename2); //papi.symlink(filename1, filename2); System.out.println("readlink: " + papi.readlink(filename2)); byte[] bs = new byte[1024]; file = papi.open(filename2); int length = file.read(bs); file.close(); System.out.println("content: " + new String(bs, 0, length, charset)); System.out.println("fstat: " + papi.stat(filename)); papi.truncate(filename1, 4 * 1024); System.out.println("fstat: " + papi.stat(filename2)); //System.out.println("statvfs: " + papi.statvfs(path)); papi.setxattr(path, "myxattr", new String("myvalue").getBytes(charset)); List list; list = papi.listxattr(path, followlink); for (String name : list) { System.out.println("name: " + name + ", value: " + new String(papi.getxattr(path, name, followlink), charset)); } file = papi.open(filename); System.out.println("fstat: " + file.stat()); //System.out.println("fstatvfs: " + file.statvfs()); list = file.listxattr(); for (String name : list) { System.out.println("name: " + name + ", value: " + new String(file.getxattr(name), charset)); } file.close(); papi.close(); } } ================================================ FILE: src/java/src/main/java/com/fastken/fcfs/FCFSVFSStat.java ================================================ package com.fastken.fcfs; public class FCFSVFSStat { public static class Stat { private long total; private long avail; private long used; public Stat(long total, long avail, long used) { this.total = total; this.avail = avail; this.used = used; } public long getTotal() { return this.total; } public long getAvail() { return this.avail; } public long getUsed() { return this.used; } public String toString() { return "total: " + this.total + ", avail: " + this.avail + ", used: " + this.used; } } private Stat space; private Stat inode; static { doInit(); } private static native void doInit(); public FCFSVFSStat(long spaceTotal, long spaceAvail, long spaceUsed, long inodeTotal, long inodeAvail, long inodeUsed) { this.space = new Stat(spaceTotal, spaceAvail, spaceUsed); this.inode = new Stat(inodeTotal, inodeAvail, inodeUsed); } public Stat getSpaceStat() { return this.space; } public Stat getInodeStat() { return this.inode; } public String toString() { return "space { " + this.space.toString() + " }" + ", inode { " + this.inode.toString() + " }"; } } ================================================ FILE: src/preload/Makefile.in ================================================ .SUFFIXES: .c .lo COMPILE = $(CC) $(CFLAGS) INC_PATH = -I../common -I../include LIB_PATH = $(LIBS) -L../api -ldl -lfcfsapi -lfsclient -lfsapi -lfdirclient -lfastcommon -lserverframe -lfcfsauthclient TARGET_LIB = $(TARGET_PREFIX)/$(LIB_VERSION) SHARED_OBJS = global.lo api.lo ALL_OBJS = $(SHARED_OBJS) ALL_PRGS = SHARED_LIBS = libfcfspreload.so ALL_LIBS = $(SHARED_LIBS) all: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) libfcfspreload.so: $(SHARED_OBJS) $(COMPILE) -o $@ -shared $(SHARED_OBJS) $(LIB_PATH) .c.lo: $(COMPILE) -c -fPIC -o $@ $< $(INC_PATH) install: mkdir -p $(TARGET_LIB) mkdir -p $(TARGET_PREFIX)/lib install -m 755 $(SHARED_LIBS) $(TARGET_LIB) @BUILDROOT=$$(echo "$(TARGET_PREFIX)" | grep BUILDROOT); \ if [ -z "$$BUILDROOT" ] && [ ! -e $(TARGET_PREFIX)/lib/libfcfspreload.so ]; then ln -s $(TARGET_LIB)/libfcfspreload.so $(TARGET_PREFIX)/lib/libfcfspreload.so; fi clean: rm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) ================================================ FILE: src/preload/api.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include "fastcommon/logger.h" #include "global.h" #include "api.h" #ifdef FCFS_PRELOAD_WITH_PAPI static int counter = 0; #endif #define FCFS_LOG_DEBUG(format, ...) \ if (g_fcfs_preload_global_vars.inited && g_log_context. \ log_level >= LOG_DEBUG) logDebug(format, ##__VA_ARGS__) // log_level >= LOG_DEBUG) fprintf(stderr, format, ##__VA_ARGS__) __attribute__ ((constructor)) static void preload_global_init(void) { int result; pid_t pid; char *ns = "fs"; char *config_filename; config_filename = getenv("FCFS_PRELOAD_CONFIG_FILENAME"); if (config_filename == NULL) { config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME; } log_init(); pid = getpid(); fprintf(stderr, "pid: %d, file: "__FILE__", line: %d, inited: %d, " "constructor, config_filename: %s\n", pid, __LINE__, g_fcfs_preload_global_vars.inited, config_filename); if ((result=fcfs_preload_global_init()) != 0) { return; } #ifdef FCFS_PRELOAD_WITH_CAPI if ((result=fcfs_capi_init()) != 0) { return; } #endif FCFS_LOG_DEBUG("file: "__FILE__", line: %d, pid: %d, " "constructor\n", __LINE__, pid); log_set_fd_flags(&g_log_context, O_CLOEXEC); if ((result=fcfs_posix_api_init("fcfs_preload", ns, config_filename)) != 0) { return; } FCFS_LOG_DEBUG("pid: %d, file: "__FILE__", line: %d, " "constructor\n", pid, __LINE__); if ((result=fcfs_posix_api_start()) != 0) { return; } g_fcfs_preload_global_vars.cwd_call_type = FCFS_PRELOAD_CALL_SYSTEM; g_fcfs_preload_global_vars.inited = true; FCFS_LOG_DEBUG("pid: %d, file: "__FILE__", line: %d, inited: %d, " "log_fd: %d\n", pid, __LINE__, g_fcfs_preload_global_vars.inited, g_log_context.log_fd); fprintf(stderr, "pid: %d, parent pid: %d, base path: %s, " "log_level: %d, log_fd: %d\n", getpid(), getppid(), SF_G_BASE_PATH_STR, g_log_context.log_level, g_log_context.log_fd); } __attribute__ ((destructor)) static void preload_global_destroy(void) { FCFS_LOG_DEBUG("pid: %d, file: "__FILE__", line: %d, " "destructor\n", getpid(), __LINE__); if (g_fcfs_preload_global_vars.inited) { fcfs_posix_api_stop(); } } static inline void *fcfs_dlsym1(const char *fname) { void *func; if ((func=dlsym(RTLD_NEXT, fname)) == NULL) { FCFS_LOG_DEBUG("file: "__FILE__", line: %d, " "function %s not exist!", __LINE__, fname); } return func; } static inline void *fcfs_dlsym2(const char *fname1, const char *fname2) { void *func; if ((func=dlsym(RTLD_NEXT, fname1)) != NULL) { return func; } if ((func=dlsym(RTLD_NEXT, fname2)) == NULL) { FCFS_LOG_DEBUG("file: "__FILE__", line: %d, " "function %s | %s not exist!", __LINE__, fname1, fname2); } return func; } #ifdef FCFS_PRELOAD_WITH_PAPI static inline int do_open(const char *path, int flags, int mode) { int fd; if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { fd = fcfs_open(path, flags, mode); } else { fd = syscall(SYS_open, path, flags, mode); } FCFS_LOG_DEBUG("%d. @func: %s, path: %s, mode: %o, fd: %d\n", ++counter, __FUNCTION__, path, mode, fd); return fd; } int _open_(const char *path, int flags, ...) { va_list ap; int mode; va_start(ap, flags); mode = va_arg(ap, int); va_end(ap); FCFS_LOG_DEBUG("#func: %s, path: %s, mode: %o\n", __FUNCTION__, path, mode); return do_open(path, flags, mode); } int __open_nocancel(const char *path, int flags, ...) { va_list ap; int mode; va_start(ap, flags); mode = va_arg(ap, int); va_end(ap); FCFS_LOG_DEBUG("#func: %s, path: %s, mode: %o\n", __FUNCTION__, path, mode); return do_open(path, flags, mode); } int open64(const char *path, int flags, ...) { va_list ap; int mode; va_start(ap, flags); mode = va_arg(ap, int); va_end(ap); FCFS_LOG_DEBUG("#func: %s, path: %s, mode: %o\n", __FUNCTION__, path, mode); return do_open(path, flags, mode); } int __open(const char *path, int flags, int mode) { FCFS_LOG_DEBUG("#func: %s, path: %s, mode: %o\n", __FUNCTION__, path, mode); return do_open(path, flags, mode); } int __open_2_(const char *path, int flags) { FCFS_LOG_DEBUG("#func: %s, path: %s\n", __FUNCTION__, path); return do_open(path, flags, 0666); } int __open64_2_(const char *path, int flags) { FCFS_LOG_DEBUG("#func: %s, path: %s\n", __FUNCTION__, path); return do_open(path, flags, 0666); } static inline int do_creat(const char *path, mode_t mode) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_creat(path, mode); } else { return syscall(SYS_creat, path, mode); } } int _creat_(const char *path, mode_t mode) { return do_creat(path, mode); } int creat64(const char *path, mode_t mode) { return do_creat(path, mode); } static inline int do_truncate(const char *path, off_t length) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_truncate(path, length); } else { return syscall(SYS_truncate, path, length); } } int _truncate_(const char *path, off_t length) { return do_truncate(path, length); } int truncate64(const char *path, off_t length) { return do_truncate(path, length); } int lstat(const char *path, struct stat *buf) { FCFS_LOG_DEBUG("%d. func: %s, line: %d\n", ++counter, __FUNCTION__, __LINE__); if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_lstat(path, buf); } else { return syscall(SYS_lstat, path, buf); } } static inline int do_lxstat(int ver, const char *path, struct stat *buf) { FCFS_LOG_DEBUG("%d. func: %s, ver: %d, path: %s\n", ++counter, __FUNCTION__, ver, path); if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_lstat(path, buf); } else { if (g_fcfs_preload_global_vars.__lxstat == NULL) { g_fcfs_preload_global_vars.__lxstat = fcfs_dlsym1("__lxstat"); } return g_fcfs_preload_global_vars.__lxstat(ver, path, buf); } } int __lxstat_(int ver, const char *path, struct stat *buf) { return do_lxstat(ver, path, buf); } int __lxstat64(int ver, const char *path, struct stat64 *buf) { return do_lxstat(ver, path, (struct stat *)buf); } int stat(const char *path, struct stat *buf) { FCFS_LOG_DEBUG("%d. func: %s, line: %d, path: %s\n", ++counter, __FUNCTION__, __LINE__, path); if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_stat(path, buf); } else { return syscall(SYS_stat, path, buf); } } static inline int do_xstat(int ver, const char *path, struct stat *buf) { FCFS_LOG_DEBUG("%d. func: %s, ver: %d, path: %s\n", ++counter, __FUNCTION__, ver, path); if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_stat(path, buf); } else { if (g_fcfs_preload_global_vars.__xstat == NULL) { g_fcfs_preload_global_vars.__xstat = fcfs_dlsym1("__xstat"); } return g_fcfs_preload_global_vars.__xstat(ver, path, buf); } } int __xstat_(int ver, const char *path, struct stat *buf) { return do_xstat(ver, path, buf); } int __xstat64(int ver, const char *path, struct stat64 *buf) { return do_xstat(ver, path, (struct stat *)buf); } int link(const char *path1, const char *path2) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path1) || FCFS_PRELOAD_IS_MY_MOUNTPOINT(path2)) { return fcfs_link(path1, path2); } else { return syscall(SYS_link, path1, path2); } } int symlink(const char *link, const char *path) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_symlink(link, path); } else { return syscall(SYS_symlink, link, path); } } ssize_t readlink(const char *path, char *buff, size_t size) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_readlink(path, buff, size); } else { return syscall(SYS_readlink, path, buff, size); } } int mknod(const char *path, mode_t mode, dev_t dev) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_mknod(path, mode, dev); } else { return syscall(SYS_mknod, path, mode, dev); } } int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_mknod(path, mode, *dev); } else { if (g_fcfs_preload_global_vars.__xmknod == NULL) { g_fcfs_preload_global_vars.__xmknod = fcfs_dlsym1("__xmknod"); } return g_fcfs_preload_global_vars.__xmknod(ver, path, mode, dev); } } int mkfifo(const char *path, mode_t mode) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_mkfifo(path, mode); } else { if (g_fcfs_preload_global_vars.mkfifo == NULL) { g_fcfs_preload_global_vars.mkfifo = fcfs_dlsym1("mkfifo"); } return g_fcfs_preload_global_vars.mkfifo(path, mode); } } int access(const char *path, int mode) { FCFS_LOG_DEBUG("%d. func: %s, path: %s, mode: %o\n", ++counter, __FUNCTION__, path, mode); if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_access(path, mode); } else { return syscall(SYS_access, path, mode); } } int eaccess(const char *path, int mode) { FCFS_LOG_DEBUG("func: %s, path: %s, mode: %o\n", __FUNCTION__, path, mode); if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_eaccess(path, mode); } else { if (g_fcfs_preload_global_vars.eaccess == NULL) { g_fcfs_preload_global_vars.eaccess = fcfs_dlsym1("eaccess"); } return g_fcfs_preload_global_vars.eaccess(path, mode); } } int euidaccess(const char *path, int mode) { FCFS_LOG_DEBUG("func: %s, path: %s, mode: %o\n", __FUNCTION__, path, mode); if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_euidaccess(path, mode); } else { if (g_fcfs_preload_global_vars.euidaccess == NULL) { g_fcfs_preload_global_vars.euidaccess = fcfs_dlsym1("euidaccess"); } return g_fcfs_preload_global_vars.euidaccess(path, mode); } } int utime(const char *path, const struct utimbuf *times) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_utime(path, times); } else { return syscall(SYS_utime, path, times); } } int utimes(const char *path, const struct timeval times[2]) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_utimes(path, times); } else { return syscall(SYS_utimes, path, times); } } int unlink(const char *path) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_unlink(path); } else { return syscall(SYS_unlink, path); } } int rename(const char *path1, const char *path2) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path1) || FCFS_PRELOAD_IS_MY_MOUNTPOINT(path2)) { return fcfs_rename(path1, path2); } else { return syscall(SYS_rename, path1, path2); } } int mkdir(const char *path, mode_t mode) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_mkdir(path, mode); } else { return syscall(SYS_mkdir, path, mode); } } int rmdir(const char *path) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_rmdir(path); } else { return syscall(SYS_rmdir, path); } } int chown(const char *path, uid_t owner, gid_t group) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_chown(path, owner, group); } else { return syscall(SYS_chown, path, owner, group); } } int lchown(const char *path, uid_t owner, gid_t group) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_lchown(path, owner, group); } else { return syscall(SYS_lchown, path, owner, group); } } int chmod(const char *path, mode_t mode) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_chmod(path, mode); } else { return syscall(SYS_chmod, path, mode); } } static inline int do_statvfs(const char *path, struct statvfs *buf) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_statvfs(path, buf); } else { if (g_fcfs_preload_global_vars.statvfs == NULL) { g_fcfs_preload_global_vars.statvfs = fcfs_dlsym2( "statvfs", "statvfs64"); } return g_fcfs_preload_global_vars.statvfs(path, buf); } } int _statvfs_(const char *path, struct statvfs *buf) { return do_statvfs(path, buf); } int statvfs64(const char *path, struct statvfs64 *buf) { return do_statvfs(path, (struct statvfs *)buf); } int setxattr(const char *path, const char *name, const void *value, size_t size, int flags) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_setxattr(path, name, value, size, flags); } else { return syscall(SYS_setxattr, path, name, value, size, flags); } } int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_lsetxattr(path, name, value, size, flags); } else { return syscall(SYS_lsetxattr, path, name, value, size, flags); } } ssize_t getxattr(const char *path, const char *name, void *value, size_t size) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_getxattr(path, name, value, size); } else { return syscall(SYS_getxattr, path, name, value, size); } } ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_lgetxattr(path, name, value, size); } else { return syscall(SYS_lgetxattr, path, name, value, size); } } ssize_t listxattr(const char *path, char *list, size_t size) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_listxattr(path, list, size); } else { return syscall(SYS_listxattr, path, list, size); } } ssize_t llistxattr(const char *path, char *list, size_t size) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_llistxattr(path, list, size); } else { return syscall(SYS_llistxattr, path, list, size); } } int removexattr(const char *path, const char *name) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_removexattr(path, name); } else { return syscall(SYS_removexattr, path, name); } } int lremovexattr(const char *path, const char *name) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_lremovexattr(path, name); } else { return syscall(SYS_lremovexattr, path, name); } } int chdir(const char *path) { int result; FCFS_LOG_DEBUG("%d. func: %s, line: %d, path: %s\n", ++counter, __FUNCTION__, __LINE__, path); if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { if ((result=fcfs_chdir(path)) == 0) { g_fcfs_preload_global_vars.cwd_call_type = FCFS_PRELOAD_CALL_FASTCFS; } } else { if ((result=syscall(SYS_chdir, path)) == 0) { g_fcfs_preload_global_vars.cwd_call_type = FCFS_PRELOAD_CALL_SYSTEM; } } return result; } int chroot(const char *path) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_chroot(path); } else { return syscall(SYS_chroot, path); } } DIR *opendir(const char *path) { FCFSPreloadDIRWrapper *wapper; DIR *dirp; int call_type; FCFS_LOG_DEBUG("%d. func: %s, path: %s\n", ++counter, __FUNCTION__, path); if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { call_type = FCFS_PRELOAD_CALL_FASTCFS; dirp = (DIR *)fcfs_opendir(path); } else { call_type = FCFS_PRELOAD_CALL_SYSTEM; if (g_fcfs_preload_global_vars.opendir == NULL) { g_fcfs_preload_global_vars.opendir = fcfs_dlsym1("opendir"); } dirp = g_fcfs_preload_global_vars.opendir(path); } if (dirp != NULL) { wapper = fc_malloc(sizeof(FCFSPreloadDIRWrapper)); wapper->call_type = call_type; wapper->dirp = dirp; return (DIR *)wapper; } else { return NULL; } } static inline int do_scandir(const char *path, struct dirent ***namelist, fcfs_dir_filter_func filter, fcfs_dir_compare_func compar) { if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { return fcfs_scandir(path, namelist, filter, compar); } else { if (g_fcfs_preload_global_vars.scandir == NULL) { g_fcfs_preload_global_vars.scandir = fcfs_dlsym1("scandir"); } return g_fcfs_preload_global_vars.scandir(path, namelist, filter, compar); } } int _scandir_(const char *path, struct dirent ***namelist, fcfs_dir_filter_func filter, fcfs_dir_compare_func compar) { return do_scandir(path, namelist, filter, compar); } int scandir64(const char * path, struct dirent64 ***namelist, int (*filter) (const struct dirent64 *), int (*compar) (const struct dirent64 **, const struct dirent64 **)) { return do_scandir(path, (struct dirent ***)namelist, (fcfs_dir_filter_func)filter, (fcfs_dir_compare_func)compar); } int close(int fd) { FCFS_LOG_DEBUG("%d. pid: %d, func: %s, line: %d, fd: %d\n", ++counter, getpid(), __FUNCTION__, __LINE__, fd); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_close(fd); } else { return syscall(SYS_close, fd); } } int fsync(int fd) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fsync(fd); } else { return syscall(SYS_fsync, fd); } } int fdatasync(int fd) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fdatasync(fd); } else { return syscall(SYS_fdatasync, fd); } } ssize_t write(int fd, const void *buff, size_t count) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_write(fd, buff, count); } else { return syscall(SYS_write, fd, buff, count); } } static inline ssize_t do_pwrite(int fd, const void *buff, size_t count, off_t offset) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_pwrite(fd, buff, count, offset); } else { return syscall(SYS_pwrite64, fd, buff, count, offset); } } ssize_t _pwrite_(int fd, const void *buff, size_t count, off_t offset) { return do_pwrite(fd, buff, count, offset); } ssize_t pwrite64(int fd, const void *buff, size_t count, off_t offset) { return do_pwrite(fd, buff, count, offset); } ssize_t writev(int fd, const struct iovec *iov, int iovcnt) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_writev(fd, iov, iovcnt); } else { return syscall(SYS_writev, fd, iov, iovcnt); } } static inline ssize_t do_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_pwritev(fd, iov, iovcnt, offset); } else { return syscall(SYS_writev, fd, iov, iovcnt, offset); } } ssize_t _pwritev_(int fd, const struct iovec *iov, int iovcnt, off_t offset) { return do_pwritev(fd, iov, iovcnt, offset); } ssize_t pwritev64(int fd, const struct iovec *iov, int iovcnt, off_t offset) { return do_pwritev(fd, iov, iovcnt, offset); } ssize_t read(int fd, void *buff, size_t count) { FCFS_LOG_DEBUG("%d. line: %d, func: %s, fd: %d, count: %d\n", ++counter, __LINE__, __FUNCTION__, fd, (int)count); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_read(fd, buff, count); } else { return syscall(SYS_read, fd, buff, count); } } ssize_t __read_chk(int fd, void *buff, size_t count, size_t size) { /* fprintf(stderr, "==== line: %d, func: %s, fd: %d, count: %d, size: %d ====\n", __LINE__, __FUNCTION__, fd, (int)count, (int)size); */ return read(fd, buff, size > 0 ? FC_MIN(count, size) : count); } ssize_t readahead(int fd, off64_t offset, size_t count) { FCFS_LOG_DEBUG("func: %s, fd: %d\n", __FUNCTION__, fd); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_readahead(fd, offset, count); } else { return syscall(SYS_readahead, fd, offset, count); } } static inline ssize_t do_pread(int fd, void *buff, size_t count, off_t offset) { FCFS_LOG_DEBUG("%d. func: %s, fd: %d\n", ++counter, __FUNCTION__, fd); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_pread(fd, buff, count, offset); } else { return syscall(SYS_pread64, fd, buff, count, offset); } } ssize_t _pread_(int fd, void *buff, size_t count, off_t offset) { return do_pread(fd, buff, count, offset); } ssize_t pread64(int fd, void *buff, size_t count, off_t offset) { return do_pread(fd, buff, count, offset); } ssize_t __pread_chk(int fd, void *buff, size_t count, off_t offset, size_t size) { /* fprintf(stderr, "==== line: %d, func: %s, fd: %d, count: %d, size: %d ====\n", __LINE__, __FUNCTION__, fd, (int)count, (int)size); */ return do_pread(fd, buff, size > 0 ? FC_MIN(count, size) : count, offset); } ssize_t __pread64_chk(int fd, void *buff, size_t count, off_t offset, size_t size) { /* fprintf(stderr, "==== line: %d, func: %s, fd: %d, count: %d, size: %d ====\n", __LINE__, __FUNCTION__, fd, (int)count, (int)size); */ return do_pread(fd, buff, size > 0 ? FC_MIN(count, size) : count, offset); } ssize_t readv(int fd, const struct iovec *iov, int iovcnt) { FCFS_LOG_DEBUG("%d. func: %s, fd: %d\n", ++counter, __FUNCTION__, fd); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_readv(fd, iov, iovcnt); } else { return syscall(SYS_readv, fd, iov, iovcnt); } } static inline ssize_t do_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_preadv(fd, iov, iovcnt, offset); } else { return syscall(SYS_preadv, fd, iov, iovcnt, offset); } } ssize_t _preadv_(int fd, const struct iovec *iov, int iovcnt, off_t offset) { FCFS_LOG_DEBUG("%d. func: %s, fd: %d\n", ++counter, __FUNCTION__, fd); return do_preadv(fd, iov, iovcnt, offset); } ssize_t preadv64(int fd, const struct iovec *iov, int iovcnt, off_t offset) { FCFS_LOG_DEBUG("%d. func: %s, fd: %d\n", ++counter, __FUNCTION__, fd); return do_preadv(fd, iov, iovcnt, offset); } static inline off_t do_lseek(int fd, off_t offset, int whence) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_lseek(fd, offset, whence); } else { return syscall(SYS_lseek, fd, offset, whence); } } off_t _lseek_(int fd, off_t offset, int whence) { FCFS_LOG_DEBUG("%d. func: %s, fd: %d\n", ++counter, __FUNCTION__, fd); return do_lseek(fd, offset, whence); } off_t __lseek(int fd, off_t offset, int whence) { FCFS_LOG_DEBUG("%d. func: %s, fd: %d\n", ++counter, __FUNCTION__, fd); return do_lseek(fd, offset, whence); } off_t lseek64(int fd, off_t offset, int whence) { FCFS_LOG_DEBUG("%d. func: %s, fd: %d\n", ++counter, __FUNCTION__, fd); return do_lseek(fd, offset, whence); } static inline int do_fallocate(int fd, int mode, off_t offset, off_t length) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fallocate(fd, mode, offset, length); } else { return syscall(SYS_fallocate, fd, mode, offset, length); } } int _fallocate_(int fd, int mode, off_t offset, off_t length) { return do_fallocate(fd, mode, offset, length); } int fallocate64(int fd, int mode, off_t offset, off_t length) { return do_fallocate(fd, mode, offset, length); } static inline int do_ftruncate(int fd, off_t length) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_ftruncate(fd, length); } else { return syscall(SYS_ftruncate, fd, length); } } int _ftruncate_(int fd, off_t length) { return do_ftruncate(fd, length); } int ftruncate64(int fd, off_t length) { return do_ftruncate(fd, length); } int fstat(int fd, struct stat *buf) { FCFS_LOG_DEBUG("%d. func: %s, fd: %d\n", ++counter, __FUNCTION__, fd); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fstat(fd, buf); } else { return syscall(SYS_fstat, fd, buf); } } static inline int do_fxstat(int ver, int fd, struct stat *buf) { FCFS_LOG_DEBUG("%d. func: %s, ver: %d, fd: %d\n", ++counter, __FUNCTION__, ver, fd); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fstat(fd, buf); } else { if (g_fcfs_preload_global_vars.__fxstat == NULL) { g_fcfs_preload_global_vars.__fxstat = fcfs_dlsym1("__fxstat"); } return g_fcfs_preload_global_vars.__fxstat(ver, fd, buf); } } int __fxstat_(int ver, int fd, struct stat *buf) { FCFS_LOG_DEBUG("func: %s, ver: %d, fd: %d\n", __FUNCTION__, ver, fd); return do_fxstat(ver, fd, buf); } int __fxstat64(int ver, int fd, struct stat64 *buf) { FCFS_LOG_DEBUG("func: %s, ver: %d, fd: %d\n", __FUNCTION__, ver, fd); return do_fxstat(ver, fd, (struct stat *)buf); } int flock(int fd, int operation) { FCFS_LOG_DEBUG("func: %s, fd: %d, operation: %d\n", __FUNCTION__, fd, operation); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_flock(fd, operation); } else { return syscall(SYS_flock, fd, operation); } } static int do_fcntl(int fd, int cmd, void *arg) { struct flock *lock; int flags; switch (cmd) { case F_GETLK: case F_SETLK: case F_SETLKW: lock = (struct flock *)arg; if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fcntl(fd, cmd, lock); } else { return syscall(SYS_fcntl, fd, cmd, lock); } default: flags = (long)arg; FCFS_LOG_DEBUG("func: %s, fd: %d, cmd: %d, flags: %d\n", __FUNCTION__, fd, cmd, flags); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fcntl(fd, cmd, flags); } else { return syscall(SYS_fcntl, fd, cmd, flags); } } } int _fcntl_(int fd, int cmd, ...) { va_list ap; void *arg; FCFS_LOG_DEBUG("%d. func: %s, fd: %d, cmd: %d\n", ++counter, __FUNCTION__, fd, cmd); va_start(ap, cmd); arg = va_arg(ap, void *); va_end(ap); return do_fcntl(fd, cmd, arg); } int fcntl64(int fd, int cmd, ...) { va_list ap; void *arg; FCFS_LOG_DEBUG("%d. func: %s, fd: %d, cmd: %d\n", ++counter, __FUNCTION__, fd, cmd); va_start(ap, cmd); arg = va_arg(ap, void *); va_end(ap); return do_fcntl(fd, cmd, arg); } int futimes(int fd, const struct timeval times[2]) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_futimes(fd, times); } else { if (g_fcfs_preload_global_vars.futimes == NULL) { g_fcfs_preload_global_vars.futimes = fcfs_dlsym1("futimes"); } return g_fcfs_preload_global_vars.futimes(fd, times); } } int futimens(int fd, const struct timespec times[2]) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_futimens(fd, times); } else { if (g_fcfs_preload_global_vars.futimens == NULL) { g_fcfs_preload_global_vars.futimens = fcfs_dlsym1("futimens"); } return g_fcfs_preload_global_vars.futimens(fd, times); } } int fchown(int fd, uid_t owner, gid_t group) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fchown(fd, owner, group); } else { return syscall(SYS_fchown, fd, owner, group); } } int fchmod(int fd, mode_t mode) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fchmod(fd, mode); } else { return syscall(SYS_fchmod, fd, mode); } } int fsetxattr(int fd, const char *name, const void *value, size_t size, int flags) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fsetxattr(fd, name, value, size, flags); } else { return syscall(SYS_fsetxattr, fd, name, value, size, flags); } } ssize_t fgetxattr(int fd, const char *name, void *value, size_t size) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fgetxattr(fd, name, value, size); } else { return syscall(SYS_fgetxattr, fd, name, value, size); } } ssize_t flistxattr(int fd, char *list, size_t size) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_flistxattr(fd, list, size); } else { return syscall(SYS_flistxattr, fd, list, size); } } int fremovexattr(int fd, const char *name) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fremovexattr(fd, name); } else { return syscall(SYS_fremovexattr, fd, name); } } int fchdir(int fd) { int result; if (FCFS_PAPI_IS_MY_FD(fd)) { if ((result=fcfs_fchdir(fd)) == 0) { g_fcfs_preload_global_vars.cwd_call_type = FCFS_PRELOAD_CALL_FASTCFS; } } else { if ((result=syscall(SYS_fchdir, fd)) == 0) { g_fcfs_preload_global_vars.cwd_call_type = FCFS_PRELOAD_CALL_SYSTEM; } } return result; } static inline int do_fstatvfs(int fd, struct statvfs *buf) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_fstatvfs(fd, buf); } else { if (g_fcfs_preload_global_vars.fstatvfs == NULL) { g_fcfs_preload_global_vars.fstatvfs = fcfs_dlsym2( "fstatvfs", "fstatvfs64"); } return g_fcfs_preload_global_vars.fstatvfs(fd, buf); } } int _fstatvfs_(int fd, struct statvfs *buf) { return do_fstatvfs(fd, buf); } int fstatvfs64(int fd, struct statvfs64 *buf) { return do_fstatvfs(fd, (struct statvfs *)buf); } int dup(int fd) { FCFS_LOG_DEBUG("#func: %s, fd: %d\n", __FUNCTION__, fd); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_dup(fd); } else { return syscall(SYS_dup, fd); } } int dup2(int fd1, int fd2) { FCFS_LOG_DEBUG("#func: %s, fd1: %d, fd2: %d\n", __FUNCTION__, fd1, fd2); if (FCFS_PAPI_IS_MY_FD(fd1) && FCFS_PAPI_IS_MY_FD(fd2)) { return fcfs_dup2(fd1, fd2); } else { return syscall(SYS_dup2, fd1, fd2); } } static inline void *do_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { FCFS_LOG_DEBUG("func: %s, fd: %d, offset: %"PRId64"\n", __FUNCTION__, fd, (int64_t)offset); if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_mmap(addr, length, prot, flags, fd, offset); } else { return (void *)syscall(SYS_mmap, addr, length, prot, flags, fd, offset); } } void *_mmap_(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { return do_mmap(addr, length, prot, flags, fd, offset); } void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { return do_mmap(addr, length, prot, flags, fd, offset); } DIR *fdopendir(int fd) { FCFSPreloadDIRWrapper *wapper; DIR *dirp; int call_type; FCFS_LOG_DEBUG("%d. func: %s, fd: %d\n", ++counter, __FUNCTION__, fd); if (FCFS_PAPI_IS_MY_FD(fd)) { call_type = FCFS_PRELOAD_CALL_FASTCFS; dirp = (DIR *)fcfs_fdopendir(fd); } else { call_type = FCFS_PRELOAD_CALL_SYSTEM; if (g_fcfs_preload_global_vars.fdopendir == NULL) { g_fcfs_preload_global_vars.fdopendir = fcfs_dlsym1("fdopendir"); } dirp = g_fcfs_preload_global_vars.fdopendir(fd); } if (dirp != NULL) { wapper = fc_malloc(sizeof(FCFSPreloadDIRWrapper)); wapper->call_type = call_type; wapper->dirp = dirp; return (DIR *)wapper; } else { return NULL; } } int symlinkat(const char *link, int fd, const char *path) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_symlinkat(link, fd, path); } else { return syscall(SYS_symlinkat, link, fd, path); } } static inline int do_openat(int fd, const char *path, int flags, int mode) { FCFS_LOG_DEBUG("func: %s, path: %s, mode: %o\n", __FUNCTION__, path, mode); if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_openat(fd, path, flags, mode); } else { return syscall(SYS_openat, fd, path, flags, mode); } } int _openat_(int fd, const char *path, int flags, ...) { va_list ap; int mode; va_start(ap, flags); mode = va_arg(ap, int); va_end(ap); FCFS_LOG_DEBUG("func: %s, path: %s, mode: %o\n", __FUNCTION__, path, mode); return do_openat(fd, path, flags, mode); } int openat64(int fd, const char *path, int flags, ...) { va_list ap; int mode; va_start(ap, flags); mode = va_arg(ap, int); va_end(ap); FCFS_LOG_DEBUG("func: %s, path: %s, mode: %o\n", __FUNCTION__, path, mode); return do_openat(fd, path, flags, mode); } int __openat_2_(int fd, const char *path, int flags) { FCFS_LOG_DEBUG("func: %s, path: %s\n", __FUNCTION__, path); return do_openat(fd, path, flags, 0666); } int __openat64_2(int fd, const char *path, int flags) { FCFS_LOG_DEBUG("func: %s, path: %s\n", __FUNCTION__, path); return do_openat(fd, path, flags, 0666); } int fstatat(int fd, const char *path, struct stat *buf, int flags) { FCFS_LOG_DEBUG("func: %s, fd: %d, path: %s\n", __FUNCTION__, fd, path); if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_fstatat(fd, path, buf, flags); } else { if (g_fcfs_preload_global_vars.fstatat == NULL) { g_fcfs_preload_global_vars.fstatat = fcfs_dlsym1("fstatat"); } return g_fcfs_preload_global_vars.fstatat(fd, path, buf, flags); } } static inline int do_fxstatat(int ver, int fd, const char *path, struct stat *buf, int flags) { FCFS_LOG_DEBUG("%d. line: %d, func: %s, fd: %d, path: %s\n", ++counter, __LINE__, __FUNCTION__, fd, path); if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_fstatat(fd, path, buf, flags); } else { if (g_fcfs_preload_global_vars.__fxstatat == NULL) { g_fcfs_preload_global_vars.__fxstatat = fcfs_dlsym1("__fxstatat"); } return g_fcfs_preload_global_vars.__fxstatat(ver, fd, path, buf, flags); } } int __fxstatat_(int ver, int fd, const char *path, struct stat *buf, int flags) { return do_fxstatat(ver, fd, path, buf, flags); } int __fxstatat64(int ver, int fd, const char *path, struct stat64 *buf, int flags) { return do_fxstatat(ver, fd, path, (struct stat *)buf, flags); } ssize_t readlinkat(int fd, const char *path, char *buff, size_t size) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_readlinkat(fd, path, buff, size); } else { return syscall(SYS_readlinkat, fd, path, buff, size); } } int mknodat(int fd, const char *path, mode_t mode, dev_t dev) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_mknodat(fd, path, mode, dev); } else { return syscall(SYS_mknodat, fd, path, mode, dev); } } int __xmknodat(int ver, int fd, const char *path, mode_t mode, dev_t *dev) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_mknodat(fd, path, mode, *dev); } else { if (g_fcfs_preload_global_vars.__xmknodat == NULL) { g_fcfs_preload_global_vars.__xmknodat = fcfs_dlsym1("__xmknodat"); } return g_fcfs_preload_global_vars.__xmknodat(ver, fd, path, mode, dev); } } int mkfifoat(int fd, const char *path, mode_t mode) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_mkfifoat(fd, path, mode); } else { if (g_fcfs_preload_global_vars.mkfifoat == NULL) { g_fcfs_preload_global_vars.mkfifoat = fcfs_dlsym1("mkfifoat"); } return g_fcfs_preload_global_vars.mkfifoat(fd, path, mode); } } int faccessat(int fd, const char *path, int mode, int flags) { FCFS_LOG_DEBUG("%d. func: %s, path: %s, mode: %o\n", ++counter, __FUNCTION__, path, mode); if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_faccessat(fd, path, mode, flags); } else { return syscall(SYS_faccessat, fd, path, mode, flags); } } int futimesat(int fd, const char *path, const struct timeval times[2]) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_futimesat(fd, path, times); } else { return syscall(SYS_futimesat, fd, path, times); } } int utimensat(int fd, const char *path, const struct timespec times[2], int flags) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_utimensat(fd, path, times, flags); } else { return syscall(SYS_utimensat, fd, path, times, flags); } } int unlinkat(int fd, const char *path, int flags) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_unlinkat(fd, path, flags); } else { return syscall(SYS_unlinkat, fd, path, flags); } } int mkdirat(int fd, const char *path, mode_t mode) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_mkdirat(fd, path, mode); } else { return syscall(SYS_futimesat, fd, path, mode); } } int fchownat(int fd, const char *path, uid_t owner, gid_t group, int flags) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_fchownat(fd, path, owner, group, flags); } else { return syscall(SYS_fchownat, fd, path, owner, group, flags); } } int fchmodat(int fd, const char *path, mode_t mode, int flags) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_fchmodat(fd, path, mode, flags); } else { return syscall(SYS_fchmodat, fd, path, mode, flags); } } int linkat(int fd1, const char *path1, int fd2, const char *path2, int flags) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd1, path1) || FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd2, path2)) { return fcfs_linkat(fd1, path1, fd2, path2, flags); } else { return syscall(SYS_linkat, fd1, path1, fd2, path2, flags); } } int renameat(int fd1, const char *path1, int fd2, const char *path2) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd1, path1) || FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd2, path2)) { return fcfs_renameat(fd1, path1, fd2, path2); } else { return syscall(SYS_renameat, fd1, path1, fd2, path2); } } int renameat2(int fd1, const char *path1, int fd2, const char *path2, unsigned int flags) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd1, path1) || FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd2, path2)) { return fcfs_renameat2(fd1, path1, fd2, path2, flags); } else { return syscall(SYS_renameat2, fd1, path1, fd2, path2, flags); } } static inline int do_scandirat(int fd, const char *path, struct dirent ***namelist, fcfs_dir_filter_func filter, fcfs_dir_compare_func compar) { if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) { return fcfs_scandirat(fd, path, namelist, filter, compar); } else { if (g_fcfs_preload_global_vars.scandirat == NULL) { g_fcfs_preload_global_vars.scandirat = fcfs_dlsym1("scandirat"); } return g_fcfs_preload_global_vars.scandirat( fd, path, namelist, filter, compar); } } int _scandirat_(int fd, const char *path, struct dirent ***namelist, fcfs_dir_filter_func filter, fcfs_dir_compare_func compar) { return do_scandirat(fd, path, namelist, filter, compar); } int scandirat64(int fd, const char *path, struct dirent64 ***namelist, int (*filter) (const struct dirent64 *), int (*compar) (const struct dirent64 **, const struct dirent64 **)) { return do_scandirat(fd, path, (struct dirent ***)namelist, (fcfs_dir_filter_func)filter, (fcfs_dir_compare_func)compar); } char *getcwd(char *buf, size_t size) { FCFS_LOG_DEBUG("%d. func: %s, line: %d, size: %d\n", ++counter, __FUNCTION__, __LINE__, (int)size); if (g_fcfs_preload_global_vars.cwd_call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_getcwd(buf, size); } else { return g_fcfs_preload_global_vars.getcwd(buf, size); } } char *getwd(char *buf) { if (g_fcfs_preload_global_vars.cwd_call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_getwd(buf); } else { return g_fcfs_preload_global_vars.getwd(buf); } } int closedir(DIR *dirp) { FCFSPreloadDIRWrapper *wapper; int result; FCFS_LOG_DEBUG("%d. func: %s, line: %d\n", ++counter, __FUNCTION__, __LINE__); wapper = (FCFSPreloadDIRWrapper *)dirp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { result = fcfs_closedir(wapper->dirp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.closedir == NULL) { g_fcfs_preload_global_vars.closedir = fcfs_dlsym1("closedir"); } result = g_fcfs_preload_global_vars.closedir(wapper->dirp); } else { errno = EBADF; return -1; } free(wapper); return result; } static inline struct dirent *do_readdir(DIR *dirp) { FCFSPreloadDIRWrapper *wapper; wapper = (FCFSPreloadDIRWrapper *)dirp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_readdir(wapper->dirp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.readdir == NULL) { g_fcfs_preload_global_vars.readdir = fcfs_dlsym1("readdir"); } return g_fcfs_preload_global_vars.readdir(wapper->dirp); } else { errno = EBADF; return NULL; } } struct dirent *_readdir_(DIR *dirp) { FCFS_LOG_DEBUG("%d. func: %s, line: %d\n", ++counter, __FUNCTION__, __LINE__); return do_readdir(dirp); } struct dirent64 *readdir64(DIR *dirp) { FCFS_LOG_DEBUG("%d. func: %s, line: %d\n", ++counter, __FUNCTION__, __LINE__); return (struct dirent64 *)do_readdir(dirp); } static inline int do_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) { FCFSPreloadDIRWrapper *wapper; wapper = (FCFSPreloadDIRWrapper *)dirp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_readdir_r(wapper->dirp, entry, result); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.readdir_r == NULL) { g_fcfs_preload_global_vars.readdir_r = fcfs_dlsym1("readdir_r"); } return g_fcfs_preload_global_vars.readdir_r( wapper->dirp, entry, result); } else { return EBADF; } } int _readdir_r_(DIR *dirp, struct dirent *entry, struct dirent **result) { FCFS_LOG_DEBUG("%d. func: %s, line: %d\n", ++counter, __FUNCTION__, __LINE__); return do_readdir_r(dirp, entry, result); } int readdir64_r(DIR *dirp, struct dirent64 *entry, struct dirent64 **result) { FCFS_LOG_DEBUG("%d. func: %s, line: %d\n", ++counter, __FUNCTION__, __LINE__); return do_readdir_r(dirp, (struct dirent *)entry, (struct dirent **)result); } void seekdir(DIR *dirp, long loc) { FCFSPreloadDIRWrapper *wapper; wapper = (FCFSPreloadDIRWrapper *)dirp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_seekdir(wapper->dirp, loc); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.seekdir == NULL) { g_fcfs_preload_global_vars.seekdir = fcfs_dlsym1("seekdir"); } return g_fcfs_preload_global_vars.seekdir(wapper->dirp, loc); } } long telldir(DIR *dirp) { FCFSPreloadDIRWrapper *wapper; wapper = (FCFSPreloadDIRWrapper *)dirp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_telldir(wapper->dirp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.telldir == NULL) { g_fcfs_preload_global_vars.telldir = fcfs_dlsym1("telldir"); } return g_fcfs_preload_global_vars.telldir(wapper->dirp); } else { errno = EBADF; return -1; } } void rewinddir(DIR *dirp) { FCFSPreloadDIRWrapper *wapper; wapper = (FCFSPreloadDIRWrapper *)dirp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_rewinddir(wapper->dirp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.rewinddir == NULL) { g_fcfs_preload_global_vars.rewinddir = fcfs_dlsym1("rewinddir"); } return g_fcfs_preload_global_vars.rewinddir(wapper->dirp); } } int dirfd(DIR *dirp) { FCFSPreloadDIRWrapper *wapper; wapper = (FCFSPreloadDIRWrapper *)dirp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_dirfd(wapper->dirp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.dirfd == NULL) { g_fcfs_preload_global_vars.dirfd = fcfs_dlsym1("dirfd"); } return g_fcfs_preload_global_vars.dirfd(wapper->dirp); } else { errno = EBADF; return -1; } } int vdprintf(int fd, const char *format, va_list ap) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_vdprintf(fd, format, ap); } else { if (g_fcfs_preload_global_vars.vdprintf == NULL) { g_fcfs_preload_global_vars.vdprintf = fcfs_dlsym1("vdprintf"); } return g_fcfs_preload_global_vars.vdprintf(fd, format, ap); } } int dprintf(int fd, const char *format, ...) { va_list ap; int bytes; va_start(ap, format); bytes = vdprintf(fd, format, ap); va_end(ap); return bytes; } static inline int do_lockf(int fd, int cmd, off_t len) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_lockf(fd, cmd, len); } else { if (g_fcfs_preload_global_vars.lockf == NULL) { g_fcfs_preload_global_vars.lockf = fcfs_dlsym2( "lockf", "lockf64"); } return g_fcfs_preload_global_vars.lockf(fd, cmd, len); } } int _lockf_(int fd, int cmd, off_t len) { return do_lockf(fd, cmd, len); } int lockf64(int fd, int cmd, off_t len) { return do_lockf(fd, cmd, len); } static inline int do_posix_fallocate(int fd, off_t offset, off_t len) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_posix_fallocate(fd, offset, len); } else { if (g_fcfs_preload_global_vars.posix_fallocate == NULL) { g_fcfs_preload_global_vars.posix_fallocate = fcfs_dlsym2( "posix_fallocate", "posix_fallocate64"); } return g_fcfs_preload_global_vars.posix_fallocate(fd, offset, len); } } int _posix_fallocate_(int fd, off_t offset, off_t len) { return do_posix_fallocate(fd, offset, len); } int posix_fallocate64(int fd, off_t offset, off_t len) { return do_posix_fallocate(fd, offset, len); } int _posix_fadvise_(int fd, off_t offset, off_t len, int advice) { if (FCFS_PAPI_IS_MY_FD(fd)) { return fcfs_posix_fadvise(fd, offset, len, advice); } else { if (g_fcfs_preload_global_vars.posix_fadvise == NULL) { g_fcfs_preload_global_vars.posix_fadvise = fcfs_dlsym2( "posix_fadvise", "posix_fadvise64"); } return g_fcfs_preload_global_vars.posix_fadvise( fd, offset, len, advice); } } int posix_fadvise64(int fd, off_t offset, off_t len, int advice) { return _posix_fadvise_(fd, offset, len, advice); } int unsetenv(const char *name) { FCFS_LOG_DEBUG("pid: %d, func: %s, line: %d, name: %s\n", getpid(), __FUNCTION__, __LINE__, name); if (g_fcfs_preload_global_vars.unsetenv == NULL) { g_fcfs_preload_global_vars.unsetenv = fcfs_dlsym1("unsetenv"); } return g_fcfs_preload_global_vars.unsetenv(name); } int clearenv(void) { FCFS_LOG_DEBUG("pid: %d, func: %s, line: %d\n", getpid(), __FUNCTION__, __LINE__); if (g_fcfs_preload_global_vars.clearenv == NULL) { g_fcfs_preload_global_vars.clearenv = fcfs_dlsym1("clearenv"); } return g_fcfs_preload_global_vars.clearenv(); } #endif #ifdef FCFS_PRELOAD_WITH_CAPI static inline FILE *do_fopen(const char *path, const char *mode) { FCFSPreloadFILEWrapper *wapper; FILE *fp; int call_type; if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) { call_type = FCFS_PRELOAD_CALL_FASTCFS; fp = fcfs_fopen(path, mode); } else { call_type = FCFS_PRELOAD_CALL_SYSTEM; if (g_fcfs_preload_global_vars.fopen == NULL) { g_fcfs_preload_global_vars.fopen = fcfs_dlsym2("fopen", "fopen64"); } fp = g_fcfs_preload_global_vars.fopen(path, mode); FCFS_LOG_DEBUG("func: %s, line: %d, fp ========== %p\n", __FUNCTION__, __LINE__, fp); } FCFS_LOG_DEBUG("func: %s, line: %d, fp: %p\n", __FUNCTION__, __LINE__, fp); if (fp != NULL) { wapper = fc_malloc(sizeof(FCFSPreloadFILEWrapper)); wapper->call_type = call_type; wapper->fp = fp; return (FILE *)wapper; } else { return NULL; } } FILE *_fopen_(const char *path, const char *mode) { FCFS_LOG_DEBUG("pid: %d, func: %s, line: %d, path: %s, mode: %s, inited: %d\n", getpid(), __FUNCTION__, __LINE__, path, mode, g_fcfs_preload_global_vars.inited); return do_fopen(path, mode); } FILE *fopen64(const char *path, const char *mode) { FCFS_LOG_DEBUG("pid: %d, func: %s, line: %d, path: %s, mode: %s\n", getpid(), __FUNCTION__, __LINE__, path, mode); return do_fopen(path, mode); } FILE *_IO_fdopen(int fd, const char *mode) { FCFSPreloadFILEWrapper *wapper; FILE *fp; int call_type; FCFS_LOG_DEBUG("====== func: %s, line: %d, fd: %d, mode: %s\n", __FUNCTION__, __LINE__, fd, mode); if (FCFS_PAPI_IS_MY_FD(fd)) { call_type = FCFS_PRELOAD_CALL_FASTCFS; fp = fcfs_fdopen(fd, mode); } else { call_type = FCFS_PRELOAD_CALL_SYSTEM; if (g_fcfs_preload_global_vars.fdopen == NULL) { g_fcfs_preload_global_vars.fdopen = fcfs_dlsym1("fdopen"); } fp = g_fcfs_preload_global_vars.fdopen(fd, mode); } if (fp != NULL) { wapper = fc_malloc(sizeof(FCFSPreloadFILEWrapper)); wapper->call_type = call_type; wapper->fp = fp; return (FILE *)wapper; } else { return NULL; } } FILE *fdopen(int fd, const char *mode) { return _IO_fdopen(fd, mode); } FILE *_freopen_(const char *path, const char *mode, FILE *fp) { FCFSPreloadFILEWrapper *wapper; wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("====== func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { fp = fcfs_freopen(path, mode, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.freopen == NULL) { g_fcfs_preload_global_vars.freopen = fcfs_dlsym1("freopen"); } fp = g_fcfs_preload_global_vars.freopen(path, mode, wapper->fp); } else { errno = EBADF; return NULL; } if (fp != NULL) { wapper->fp = fp; return (FILE *)wapper; } else { return NULL; } } FILE *freopen64(const char *path, const char *mode, FILE *fp) { return _freopen_(path, mode, fp); } #define CHECK_DEAL_STD_STREAM(funcname, ...) \ if (fp == stdin || fp == stderr || fp == stdout) { \ if (g_fcfs_preload_global_vars.funcname == NULL) { \ g_fcfs_preload_global_vars.funcname = fcfs_dlsym1(#funcname); \ } \ return g_fcfs_preload_global_vars.funcname(__VA_ARGS__); \ } #define CHECK_DEAL_STDIO_VOID(funcname, ...) \ if (fp == stdin || fp == stderr || fp == stdout) { \ if (g_fcfs_preload_global_vars.funcname == NULL) { \ g_fcfs_preload_global_vars.funcname = fcfs_dlsym1(#funcname); \ } \ g_fcfs_preload_global_vars.funcname(__VA_ARGS__); \ } int fclose(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fclose, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fclose(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fclose == NULL) { g_fcfs_preload_global_vars.fclose = fcfs_dlsym1("fclose"); } return g_fcfs_preload_global_vars.fclose(wapper->fp); } else { errno = EBADF; return EOF; } } int fcloseall() { int r1; int r2; FCFS_LOG_DEBUG("func: %s, line: %d\n", __FUNCTION__, __LINE__); r1 = fcfs_fcloseall(); if (g_fcfs_preload_global_vars.fcloseall == NULL) { g_fcfs_preload_global_vars.fcloseall = fcfs_dlsym1("fcloseall"); } r2 = g_fcfs_preload_global_vars.fcloseall(); return (r1 == 0 ? r2 : r1); } void flockfile(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STDIO_VOID(flockfile, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { fcfs_flockfile(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.flockfile == NULL) { g_fcfs_preload_global_vars.flockfile = fcfs_dlsym1("flockfile"); } g_fcfs_preload_global_vars.flockfile(wapper->fp); } } int ftrylockfile(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(ftrylockfile, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_ftrylockfile(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.ftrylockfile == NULL) { g_fcfs_preload_global_vars.ftrylockfile = fcfs_dlsym1("ftrylockfile"); } return g_fcfs_preload_global_vars.ftrylockfile(wapper->fp); } else { errno = EBADF; return EOF; } } void funlockfile(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STDIO_VOID(funlockfile, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { fcfs_funlockfile(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.funlockfile == NULL) { g_fcfs_preload_global_vars.funlockfile = fcfs_dlsym1("funlockfile"); } g_fcfs_preload_global_vars.funlockfile(wapper->fp); } } int fseek(FILE *fp, long offset, int whence) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fseek, fp, offset, whence); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fseek(wapper->fp, offset, whence); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fseek == NULL) { g_fcfs_preload_global_vars.fseek = fcfs_dlsym1("fseek"); } return g_fcfs_preload_global_vars.fseek(wapper->fp, offset, whence); } else { errno = EBADF; return EOF; } } int _fseeko_(FILE *fp, off_t offset, int whence) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fseeko, fp, offset, whence); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fseeko(wapper->fp, offset, whence); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fseeko == NULL) { g_fcfs_preload_global_vars.fseeko = fcfs_dlsym1("fseeko"); } return g_fcfs_preload_global_vars.fseeko(wapper->fp, offset, whence); } else { errno = EBADF; return EOF; } } int fseeko64(FILE *fp, off_t offset, int whence) { FCFS_LOG_DEBUG("func: %s, line: %d\n", __FUNCTION__, __LINE__); return _fseeko_(fp, offset, whence); } int __fseeko64(FILE *fp, off_t offset, int whence) { FCFS_LOG_DEBUG("func: %s, line: %d\n", __FUNCTION__, __LINE__); return _fseeko_(fp, offset, whence); } long ftell(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(ftell, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_ftell(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.ftell == NULL) { g_fcfs_preload_global_vars.ftell = fcfs_dlsym1("ftell"); } return g_fcfs_preload_global_vars.ftell(wapper->fp); } else { errno = EBADF; return EOF; } } long _IO_ftell(FILE *fp) { return ftell(fp); } static inline off_t do_ftello(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(ftello, fp); FCFS_LOG_DEBUG("func: %s, line: %d\n", __FUNCTION__, __LINE__); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_ftello(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.ftello == NULL) { g_fcfs_preload_global_vars.ftello = fcfs_dlsym1("ftello"); } return g_fcfs_preload_global_vars.ftello(wapper->fp); } else { errno = EBADF; return EOF; } } off_t _ftello_(FILE *fp) { FCFS_LOG_DEBUG("func: %s, line: %d\n", __FUNCTION__, __LINE__); return do_ftello(fp); } off_t ftello64(FILE *fp) { FCFS_LOG_DEBUG("func: %s, line: %d\n", __FUNCTION__, __LINE__); return do_ftello(fp); } off_t __ftello64(FILE *fp) { FCFS_LOG_DEBUG("func: %s, line: %d\n", __FUNCTION__, __LINE__); return do_ftello(fp); } void rewind(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STDIO_VOID(rewind, fp); FCFS_LOG_DEBUG("func: %s, line: %d\n", __FUNCTION__, __LINE__); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { fcfs_rewind(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.rewind == NULL) { g_fcfs_preload_global_vars.rewind = fcfs_dlsym1("rewind"); } g_fcfs_preload_global_vars.rewind(wapper->fp); } } int _fgetpos_(FILE *fp, fpos_t *pos) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fgetpos, fp, pos); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fgetpos(wapper->fp, pos); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fgetpos == NULL) { g_fcfs_preload_global_vars.fgetpos = fcfs_dlsym1("fgetpos"); } return g_fcfs_preload_global_vars.fgetpos(wapper->fp, pos); } else { errno = EBADF; return EOF; } } int fgetpos64(FILE *fp, fpos_t *pos) { return _fgetpos_(fp, pos); } int _IO_fgetpos(FILE *fp, fpos_t *pos) { return _fgetpos_(fp, pos); } int _IO_fgetpos64(FILE *fp, fpos_t *pos) { return _fgetpos_(fp, pos); } int _fsetpos_(FILE *fp, const fpos_t *pos) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fsetpos, fp, pos); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fsetpos(wapper->fp, pos); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fsetpos == NULL) { g_fcfs_preload_global_vars.fsetpos = fcfs_dlsym1("fsetpos"); } return g_fcfs_preload_global_vars.fsetpos(wapper->fp, pos); } else { errno = EBADF; return EOF; } } int fsetpos64(FILE *fp, const fpos_t *pos) { return _fsetpos_(fp, pos); } int _IO_fsetpos(FILE *fp, const fpos_t *pos) { return _fsetpos_(fp, pos); } int _IO_fsetpos64(FILE *fp, const fpos_t *pos) { return _fsetpos_(fp, pos); } int fgetc_unlocked(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fgetc_unlocked, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fgetc_unlocked(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fgetc_unlocked == NULL) { g_fcfs_preload_global_vars.fgetc_unlocked = fcfs_dlsym1("fgetc_unlocked"); } return g_fcfs_preload_global_vars.fgetc_unlocked(wapper->fp); } else { errno = EBADF; return EOF; } } int fputc_unlocked(int c, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fputc_unlocked, c, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fputc_unlocked(c, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fputc_unlocked == NULL) { g_fcfs_preload_global_vars.fputc_unlocked = fcfs_dlsym1("fputc_unlocked"); } return g_fcfs_preload_global_vars.fputc_unlocked(c, wapper->fp); } else { errno = EBADF; return EOF; } } int getc_unlocked(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(getc_unlocked, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_getc_unlocked(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.getc_unlocked == NULL) { g_fcfs_preload_global_vars.getc_unlocked = fcfs_dlsym1("getc_unlocked"); } return g_fcfs_preload_global_vars.getc_unlocked(wapper->fp); } else { errno = EBADF; return EOF; } } int putc_unlocked(int c, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(putc_unlocked, c, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_putc_unlocked(c, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.putc_unlocked == NULL) { g_fcfs_preload_global_vars.putc_unlocked = fcfs_dlsym1("putc_unlocked"); } return g_fcfs_preload_global_vars.putc_unlocked(c, wapper->fp); } else { errno = EBADF; return EOF; } } void clearerr_unlocked(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STDIO_VOID(clearerr_unlocked, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { fcfs_clearerr_unlocked(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.clearerr_unlocked == NULL) { g_fcfs_preload_global_vars.clearerr_unlocked = fcfs_dlsym1("clearerr_unlocked"); } g_fcfs_preload_global_vars.clearerr_unlocked(wapper->fp); } } int feof_unlocked(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(feof_unlocked, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d", __FUNCTION__, __LINE__); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_feof_unlocked(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.feof_unlocked == NULL) { g_fcfs_preload_global_vars.feof_unlocked = fcfs_dlsym1("feof_unlocked"); } return g_fcfs_preload_global_vars.feof_unlocked(wapper->fp); } else { errno = EBADF; return EOF; } } #ifdef _IO_feof_unlocked #undef _IO_feof_unlocked #endif int _IO_feof_unlocked(FILE *fp) { return feof_unlocked(fp); } int ferror_unlocked(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(ferror_unlocked, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d", __FUNCTION__, __LINE__); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_ferror_unlocked(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.ferror_unlocked == NULL) { g_fcfs_preload_global_vars.ferror_unlocked = fcfs_dlsym1("ferror_unlocked"); } return g_fcfs_preload_global_vars.ferror_unlocked(wapper->fp); } else { errno = EBADF; return EOF; } } #ifdef _IO_ferror_unlocked #undef _IO_ferror_unlocked #endif int _IO_ferror_unlocked(FILE *fp) { return ferror_unlocked(fp); } int fileno_unlocked(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fileno_unlocked, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d", __FUNCTION__, __LINE__); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fileno_unlocked(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fileno_unlocked == NULL) { g_fcfs_preload_global_vars.fileno_unlocked = fcfs_dlsym1("fileno_unlocked"); } return g_fcfs_preload_global_vars.fileno_unlocked(wapper->fp); } else { errno = EBADF; return EOF; } } int fflush_unlocked(FILE *fp) { FCFSPreloadFILEWrapper *wapper; if (fp == NULL) { return g_fcfs_preload_global_vars.fflush_unlocked(fp); } CHECK_DEAL_STD_STREAM(fflush_unlocked, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fflush_unlocked(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fflush_unlocked == NULL) { g_fcfs_preload_global_vars.fflush_unlocked = fcfs_dlsym1("fflush_unlocked"); } return g_fcfs_preload_global_vars.fflush_unlocked(wapper->fp); } else { errno = EBADF; return EOF; } } size_t fread_unlocked(void *buff, size_t size, size_t n, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fread_unlocked, buff, size, n, fp); FCFS_LOG_DEBUG("func: %s, line: %d, size: %d, nmemb: %d\n", __FUNCTION__, __LINE__, (int)size, (int)n); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fread_unlocked(buff, size, n, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fread_unlocked == NULL) { g_fcfs_preload_global_vars.fread_unlocked = fcfs_dlsym1("fread_unlocked"); } //return g_fcfs_preload_global_vars.fread_unlocked(buff, size, n, wapper->fp); int bytes = g_fcfs_preload_global_vars.fread_unlocked(buff, size, n, wapper->fp); FCFS_LOG_DEBUG("func: %s, line: %d, size: %d, nmemb: %d, bytes: %d\n", __FUNCTION__, __LINE__, (int)size, (int)n, bytes); return bytes; } else { errno = EBADF; return EOF; } } size_t fwrite_unlocked(const void *buff, size_t size, size_t n, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fwrite_unlocked, buff, size, n, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fwrite_unlocked(buff, size, n, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fwrite_unlocked == NULL) { g_fcfs_preload_global_vars.fwrite_unlocked = fcfs_dlsym1("fwrite_unlocked"); } return g_fcfs_preload_global_vars.fwrite_unlocked(buff, size, n, wapper->fp); } else { errno = EBADF; return EOF; } } char *fgets_unlocked(char *s, int size, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fgets_unlocked, s, size, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fgets_unlocked(s, size, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fgets_unlocked == NULL) { g_fcfs_preload_global_vars.fgets_unlocked = fcfs_dlsym1("fgets_unlocked"); } return g_fcfs_preload_global_vars.fgets_unlocked(s, size, wapper->fp); } else { errno = EBADF; return NULL; } } ssize_t __libc_readline_unlocked (FILE *fp, char *buff, size_t size) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(__libc_readline_unlocked, fp, buff, size); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_readline_unlocked(wapper->fp, buff, size); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.__libc_readline_unlocked == NULL) { g_fcfs_preload_global_vars.__libc_readline_unlocked = fcfs_dlsym1("__libc_readline_unlocked"); } return g_fcfs_preload_global_vars.__libc_readline_unlocked( wapper->fp, buff, size); } else { errno = EBADF; return -1; } } int fputs_unlocked(const char *s, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fputs_unlocked, s, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fputs_unlocked(s, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fputs_unlocked == NULL) { g_fcfs_preload_global_vars.fputs_unlocked = fcfs_dlsym1("fputs_unlocked"); } return g_fcfs_preload_global_vars.fputs_unlocked(s, wapper->fp); } else { errno = EBADF; return EOF; } } void clearerr(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STDIO_VOID(clearerr, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { fcfs_clearerr(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.clearerr == NULL) { g_fcfs_preload_global_vars.clearerr = fcfs_dlsym1("clearerr"); } g_fcfs_preload_global_vars.clearerr(wapper->fp); } } int _IO_feof(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(feof, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_feof(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.feof == NULL) { g_fcfs_preload_global_vars.feof = fcfs_dlsym1("feof"); } return g_fcfs_preload_global_vars.feof(wapper->fp); } else { errno = EBADF; return EOF; } } int _feof_(FILE *fp) { return _IO_feof(fp); } int _ferror_(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(feof, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_ferror(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.ferror == NULL) { g_fcfs_preload_global_vars.ferror = fcfs_dlsym1("ferror"); } return g_fcfs_preload_global_vars.ferror(wapper->fp); } else { errno = EBADF; return EOF; } } int _IO_ferror(FILE *fp) { return _ferror_(fp); } int fileno(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fileno, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fileno(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fileno == NULL) { g_fcfs_preload_global_vars.fileno = fcfs_dlsym1("fileno"); } return g_fcfs_preload_global_vars.fileno(wapper->fp); } else { errno = EBADF; return EOF; } } int fgetc(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fgetc, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fgetc(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fgetc == NULL) { g_fcfs_preload_global_vars.fgetc = fcfs_dlsym1("fgetc"); } return g_fcfs_preload_global_vars.fgetc(wapper->fp); } else { errno = EBADF; return EOF; } } char *fgets(char *s, int size, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fgets, s, size, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fgets(s, size, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fgets == NULL) { g_fcfs_preload_global_vars.fgets = fcfs_dlsym1("fgets"); } return g_fcfs_preload_global_vars.fgets(s, size, wapper->fp); } else { errno = EBADF; return NULL; } } #ifdef getc #undef getc #endif int getc(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(getc, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_getc(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.getc == NULL) { g_fcfs_preload_global_vars.getc = fcfs_dlsym1("getc"); } return g_fcfs_preload_global_vars.getc(wapper->fp); } else { errno = EBADF; return EOF; } } int ungetc(int c, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(ungetc, c, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_ungetc(c, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.ungetc == NULL) { g_fcfs_preload_global_vars.ungetc = fcfs_dlsym1("ungetc"); } return g_fcfs_preload_global_vars.ungetc(c, wapper->fp); } else { errno = EBADF; return EOF; } } int fputc(int c, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fputc, c, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fputc(c, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fputc == NULL) { g_fcfs_preload_global_vars.fputc = fcfs_dlsym1("fputc"); } return g_fcfs_preload_global_vars.fputc(c, wapper->fp); } else { errno = EBADF; return EOF; } } int fputs(const char *s, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fputs, s, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fputs(s, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fputs == NULL) { g_fcfs_preload_global_vars.fputs = fcfs_dlsym1("fputs"); } return g_fcfs_preload_global_vars.fputs(s, wapper->fp); } else { errno = EBADF; return EOF; } } #ifdef putc #undef putc #endif int putc(int c, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(putc, c, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_putc(c, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.putc == NULL) { g_fcfs_preload_global_vars.putc = fcfs_dlsym1("putc"); } return g_fcfs_preload_global_vars.putc(c, wapper->fp); } else { errno = EBADF; return EOF; } } size_t fread(void *buff, size_t size, size_t nmemb, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fread, buff, size, nmemb, fp); FCFS_LOG_DEBUG("func: %s, line: %d, size: %d, nmemb: %d\n", __FUNCTION__, __LINE__, (int)size, (int)nmemb); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fread(buff, size, nmemb, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fread == NULL) { g_fcfs_preload_global_vars.fread = fcfs_dlsym1("fread"); } return g_fcfs_preload_global_vars.fread(buff, size, nmemb, wapper->fp); } else { errno = EBADF; return EOF; } } size_t fwrite(const void *buff, size_t size, size_t nmemb, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(fwrite, buff, size, nmemb, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fwrite(buff, size, nmemb, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fwrite == NULL) { g_fcfs_preload_global_vars.fwrite = fcfs_dlsym1("fwrite"); } return g_fcfs_preload_global_vars.fwrite(buff, size, nmemb, wapper->fp); } else { errno = EBADF; return EOF; } } int vfprintf(FILE *fp, const char *format, va_list ap) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(vfprintf, fp, format, ap); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_vfprintf(wapper->fp, format, ap); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.vfprintf == NULL) { g_fcfs_preload_global_vars.vfprintf = fcfs_dlsym1("vfprintf"); } return g_fcfs_preload_global_vars.vfprintf(wapper->fp, format, ap); } else { errno = EBADF; return EOF; } } int fprintf(FILE *fp, const char *format, ...) { va_list ap; int result; va_start(ap, format); result = vfprintf(fp, format, ap); va_end(ap); return result; } int __vfprintf_chk(FILE *fp, int flag, const char *format, va_list ap) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(__vfprintf_chk, fp, flag, format, ap); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_vfprintf(wapper->fp, format, ap); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.__vfprintf_chk == NULL) { g_fcfs_preload_global_vars.__vfprintf_chk = fcfs_dlsym1("__vfprintf_chk"); } return g_fcfs_preload_global_vars.__vfprintf_chk( wapper->fp, flag, format, ap); } else { errno = EBADF; return EOF; } } int __fprintf_chk(FILE *fp, int flag, const char *format, ...) { va_list ap; int result; va_start(ap, format); result = __vfprintf_chk(fp, flag, format, ap); va_end(ap); return result; } ssize_t getdelim(char **line, size_t *size, int delim, FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(getdelim, line, size, delim, fp); wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_getdelim(line, size, delim, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.getdelim == NULL) { g_fcfs_preload_global_vars.getdelim = fcfs_dlsym1("getdelim"); } return g_fcfs_preload_global_vars.getdelim(line, size, delim, wapper->fp); } else { errno = EBADF; return EOF; } } ssize_t __getdelim(char **line, size_t *size, int delim, FILE *fp) { FCFS_LOG_DEBUG("func: %s, line: %d\n", __FUNCTION__, __LINE__); return getdelim(line, size, delim, fp); } ssize_t getline(char **line, size_t *size, FILE *fp) { FCFSPreloadFILEWrapper *wapper; wapper = (FCFSPreloadFILEWrapper *)fp; FCFS_LOG_DEBUG("func: %s, line: %d, wapper: %p, fp: %p\n", __FUNCTION__, __LINE__, wapper, wapper->fp); if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_getline(line, size, wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.getline == NULL) { g_fcfs_preload_global_vars.getline = fcfs_dlsym1("getline"); } return g_fcfs_preload_global_vars.getline(line, size, wapper->fp); } else { errno = EBADF; return EOF; } } ssize_t _IO_getline(char **line, size_t *size, FILE *fp) { return getline(line, size, fp); } int vfscanf(FILE *fp, const char *format, va_list ap) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(vfscanf, fp, format, ap); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { //TODO errno = EOPNOTSUPP; return EOF; //return fcfs_vfscanf(wapper->fp, format, ap); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.vfscanf == NULL) { g_fcfs_preload_global_vars.vfscanf = fcfs_dlsym1("vfscanf"); } return g_fcfs_preload_global_vars.vfscanf(wapper->fp, format, ap); } else { errno = EBADF; return EOF; } } int fscanf(FILE *fp, const char *format, ...) { va_list ap; int count; va_start(ap, format); count = vfscanf(fp, format, ap); va_end(ap); return count; } int setvbuf(FILE *fp, char *buf, int mode, size_t size) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(setvbuf, fp, buf, mode, size); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_setvbuf(wapper->fp, buf, mode, size); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.setvbuf == NULL) { g_fcfs_preload_global_vars.setvbuf = fcfs_dlsym1("setvbuf"); } return g_fcfs_preload_global_vars.setvbuf(wapper->fp, buf, mode, size); } else { errno = EBADF; return EOF; } } int _IO_setvbuf(FILE *fp, char *buf, int mode, size_t size) { return setvbuf(fp, buf, mode, size); } void setbuf(FILE *fp, char *buf) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(setbuf, fp, buf); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { fcfs_setbuf(wapper->fp, buf); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.setbuf == NULL) { g_fcfs_preload_global_vars.setbuf = fcfs_dlsym1("setbuf"); } g_fcfs_preload_global_vars.setbuf(wapper->fp, buf); } } void setbuffer(FILE *fp, char *buf, size_t size) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(setbuffer, fp, buf, size); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { fcfs_setbuffer(wapper->fp, buf, size); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.setbuffer == NULL) { g_fcfs_preload_global_vars.setbuffer = fcfs_dlsym1("setbuffer"); } g_fcfs_preload_global_vars.setbuffer(wapper->fp, buf, size); } } void _IO_setbuffer(FILE *fp, char *buf, size_t size) { setbuffer(fp, buf, size); } void setlinebuf(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(setlinebuf, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { fcfs_setlinebuf(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.setlinebuf == NULL) { g_fcfs_preload_global_vars.setlinebuf = fcfs_dlsym1("setlinebuf"); } g_fcfs_preload_global_vars.setlinebuf(wapper->fp); } } int fflush(FILE *fp) { FCFSPreloadFILEWrapper *wapper; if (fp == NULL) { return g_fcfs_preload_global_vars.fflush(fp); } CHECK_DEAL_STD_STREAM(fflush, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return fcfs_fflush(wapper->fp); } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.fflush == NULL) { g_fcfs_preload_global_vars.fflush = fcfs_dlsym1("fflush"); } return g_fcfs_preload_global_vars.fflush(wapper->fp); } else { errno = EBADF; return EOF; } } int _IO_fflush(FILE *fp) { return fflush(fp); } int __uflow(FILE *fp) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(__uflow, fp); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return 0; } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.__uflow == NULL) { g_fcfs_preload_global_vars.__uflow = fcfs_dlsym1("__uflow"); } return g_fcfs_preload_global_vars.__uflow(wapper->fp); } else { errno = EBADF; return EOF; } } int __overflow(FILE *fp, int ch) { FCFSPreloadFILEWrapper *wapper; CHECK_DEAL_STD_STREAM(__overflow, fp, ch); wapper = (FCFSPreloadFILEWrapper *)fp; if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) { return 0; } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) { if (g_fcfs_preload_global_vars.__overflow == NULL) { g_fcfs_preload_global_vars.__overflow = fcfs_dlsym1("__overflow"); } return g_fcfs_preload_global_vars.__overflow(wapper->fp, ch); } else { errno = EBADF; return EOF; } } #endif ================================================ FILE: src/preload/api.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_PRELOAD_API_H #define _FCFS_PRELOAD_API_H #include "types.h" #define FCFS_PRELOAD_IS_MY_MOUNTPOINT(path) \ FCFS_API_IS_MY_MOUNTPOINT(path) #define FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path) \ (FCFS_PAPI_IS_MY_FD(fd) || FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) #ifdef fread_unlocked #undef fread_unlocked #endif #ifdef fwrite_unlocked #undef fwrite_unlocked #endif #ifdef __cplusplus extern "C" { #endif int closedir(DIR *dirp); struct dirent *_readdir_(DIR *dirp) __asm__ ("" "readdir"); struct dirent64 *readdir64(DIR *dirp); int _readdir_r_(DIR *dirp, struct dirent *entry, struct dirent **result) __asm__ ("" "readdir_r"); int readdir64_r(DIR *dirp, struct dirent64 *entry, struct dirent64 **result); void seekdir(DIR *dirp, long loc); long telldir(DIR *dirp); void rewinddir(DIR *dirp); int dirfd(DIR *dirp); int close(int fd); int fsync(int fd); int fdatasync(int fd); ssize_t write(int fd, const void *buff, size_t count); ssize_t _pwrite_(int fd, const void *buff, size_t count, off_t offset) __asm__ ("" "pwrite"); ssize_t pwrite64(int fd, const void *buff, size_t count, off_t offset); ssize_t writev(int fd, const struct iovec *iov, int iovcnt); ssize_t _pwritev_(int fd, const struct iovec *iov, int iovcnt, off_t offset) __asm__ ("" "pwritev"); ssize_t pwritev64(int fd, const struct iovec *iov, int iovcnt, off_t offset); ssize_t read(int fd, void *buff, size_t count); ssize_t __read_chk(int fd, void *buff, size_t count, size_t size); ssize_t _pread_(int fd, void *buff, size_t count, off_t offset) __asm__ ("" "pread"); ssize_t pread64(int fd, void *buff, size_t count, off_t offset); ssize_t __pread_chk(int fd, void *buff, size_t count, off_t offset, size_t size); ssize_t __pread64_chk(int fd, void *buff, size_t count, off_t offset, size_t size); ssize_t readv(int fd, const struct iovec *iov, int iovcnt); ssize_t _preadv_(int fd, const struct iovec *iov, int iovcnt, off_t offset) __asm__ ("" "preadv"); ssize_t preadv64(int fd, const struct iovec *iov, int iovcnt, off_t offset); ssize_t readahead(int fd, off64_t offset, size_t count); off_t _lseek_(int fd, off_t offset, int whence) __asm__ ("" "lseek"); off_t __lseek(int fd, off_t offset, int whence); off_t lseek64(int fd, off_t offset, int whence); int _fallocate_(int fd, int mode, off_t offset, off_t length) __asm__ ("" "fallocate"); int fallocate64(int fd, int mode, off_t offset, off_t length); int _ftruncate_(int fd, off_t length) __asm__ ("" "ftruncate"); int ftruncate64(int fd, off_t length); int __fxstat_(int ver, int fd, struct stat *buf) __asm__ ("" "__fxstat"); int __fxstat64(int ver, int fd, struct stat64 *buf); int fstat(int fd, struct stat *buf); int flock(int fd, int operation); int _fcntl_(int fd, int cmd, ...) __asm__ ("" "fcntl"); int fcntl64(int fd, int cmd, ...); int futimes(int fd, const struct timeval times[2]); int futimens(int fd, const struct timespec times[2]); int fchown(int fd, uid_t owner, gid_t group); int fchmod(int fd, mode_t mode); int fsetxattr(int fd, const char *name, const void *value, size_t size, int flags); ssize_t fgetxattr(int fd, const char *name, void *value, size_t size); ssize_t flistxattr(int fd, char *list, size_t size); int fremovexattr(int fd, const char *name); int fchdir(int fd); int _fstatvfs_(int fd, struct statvfs *buf) __asm__ ("" "fstatvfs"); int fstatvfs64(int fd, struct statvfs64 *buf); int dup(int fd); int dup2(int fd1, int fd2); void *_mmap_(void *addr, size_t length, int prot, int flags, int fd, off_t offset) __asm__ ("" "mmap"); void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off_t offset); DIR *fdopendir(int fd); int symlinkat(const char *link, int fd, const char *path); int _openat_(int fd, const char *path, int flags, ...) __asm__ ("" "openat"); int openat64(int fd, const char *path, int flags, ...); int __openat_2_(int fd, const char *path, int flags) __asm__ ("" "__openat_2"); int __openat64_2(int fd, const char *path, int flags); int __fxstatat_(int ver, int fd, const char *path, struct stat *buf, int flags) __asm__ ("" "__fxstatat"); int __fxstatat64(int ver, int fd, const char *path, struct stat64 *buf, int flags); int fstatat(int fd, const char *path, struct stat *buf, int flags); ssize_t readlinkat(int fd, const char *path, char *buff, size_t size); int __xmknodat(int ver, int fd, const char *path, mode_t mode, dev_t *dev); int mknodat(int fd, const char *path, mode_t mode, dev_t dev); int mkfifoat(int fd, const char *path, mode_t mode); int faccessat(int fd, const char *path, int mode, int flags); int futimesat(int fd, const char *path, const struct timeval times[2]); int utimensat(int fd, const char *path, const struct timespec times[2], int flags); int unlinkat(int fd, const char *path, int flags); int mkdirat(int fd, const char *path, mode_t mode); int fchownat(int fd, const char *path, uid_t owner, gid_t group, int flags); int fchmodat(int fd, const char *path, mode_t mode, int flags); int linkat(int fd1, const char *path1, int fd2, const char *path2, int flags); int renameat(int fd1, const char *path1, int fd2, const char *path2); int renameat2(int fd1, const char *path1, int fd2, const char *path2, unsigned int flags); int _scandirat_(int fd, const char *path, struct dirent ***namelist, fcfs_dir_filter_func filter, fcfs_dir_compare_func compar) __asm__ ("" "scandirat"); int scandirat64(int fd, const char *path, struct dirent64 ***namelist, int (*filter) (const struct dirent64 *), int (*compar) (const struct dirent64 **, const struct dirent64 **)); char *getcwd(char *buf, size_t size); char *getwd(char *buf); int _open_(const char *path, int flags, ...) __asm__ ("" "open"); int open64(const char *path, int flags, ...); int __open(const char *path, int flags, int mode); int __open_2_(const char *path, int flags) __asm__ ("" "__open_2"); int __open64_2_(const char *path, int flags); int _creat_(const char *path, mode_t mode) __asm__ ("" "creat"); int creat64(const char *path, mode_t mode); int _truncate_(const char *path, off_t length) __asm__ ("" "truncate"); int truncate64(const char *path, off_t length); int __lxstat_(int ver, const char *path, struct stat *buf) __asm__ ("" "__lxstat"); int __lxstat64(int ver, const char *path, struct stat64 *buf); int lstat(const char *path, struct stat *buf); int __xstat_(int ver, const char *path, struct stat *buf) __asm__ ("" "__xstat"); int __xstat64(int ver, const char *path, struct stat64 *buf); int stat(const char *path, struct stat *buf); int link(const char *path1, const char *path2); int symlink(const char *link, const char *path); ssize_t readlink(const char *path, char *buff, size_t size); int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev); int mknod(const char *path, mode_t mode, dev_t dev); int mkfifo(const char *path, mode_t mode); int access(const char *path, int mode); int euidaccess(const char *path, int mode); int eaccess(const char *path, int mode); int utime(const char *path, const struct utimbuf *times); int utimes(const char *path, const struct timeval times[2]); int unlink(const char *path); int rename(const char *path1, const char *path2); int mkdir(const char *path, mode_t mode); int rmdir(const char *path); int chown(const char *path, uid_t owner, gid_t group); int lchown(const char *path, uid_t owner, gid_t group); int chmod(const char *path, mode_t mode); int _statvfs_(const char *path, struct statvfs *buf) __asm__ ("" "statvfs"); int statvfs64(const char *path, struct statvfs64 *buf); int setxattr(const char *path, const char *name, const void *value, size_t size, int flags); int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags); ssize_t getxattr(const char *path, const char *name, void *value, size_t size); ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size); ssize_t listxattr(const char *path, char *list, size_t size); ssize_t llistxattr(const char *path, char *list, size_t size); int removexattr(const char *path, const char *name); int lremovexattr(const char *path, const char *name); int chdir(const char *path); int chroot(const char *path); int unsetenv(const char *name); int clearenv(void); DIR *opendir(const char *path); int _scandir_(const char *path, struct dirent ***namelist, fcfs_dir_filter_func filter, fcfs_dir_compare_func compar) __asm__ ("" "scandir"); int scandir64(const char *path, struct dirent64 ***namelist, int (*filter) (const struct dirent64 *), int (*compar) (const struct dirent64 **, const struct dirent64 **)); int dprintf(int fd, const char *format, ...); int vdprintf(int fd, const char *format, va_list ap); int _lockf_(int fd, int cmd, off_t len) __asm__ ("" "lockf"); int lockf64(int fd, int cmd, off_t len); int _posix_fallocate_(int fd, off_t offset, off_t len) __asm__ ("" "posix_fallocate"); int posix_fallocate64(int fd, off_t offset, off_t len); int _posix_fadvise_(int fd, off_t offset, off_t len, int advice) __asm__ ("" "posix_fadvise"); int posix_fadvise64(int fd, off_t offset, off_t len, int advice); FILE *_fopen_(const char *path, const char *mode) __asm__ ("" "fopen"); FILE *fopen64(const char *path, const char *mode); FILE *fdopen(int fd, const char *mode); FILE *_freopen_(const char *path, const char *mode, FILE *fp) __asm__ ("" "freopen"); FILE *freopen64(const char *path, const char *mode, FILE *fp); int fclose(FILE *fp); int fcloseall(); void flockfile(FILE *fp); int ftrylockfile(FILE *fp); void funlockfile(FILE *fp); int fseek(FILE *fp, long offset, int whence); int _fseeko_(FILE *fp, off_t offset, int whence) __asm__ ("" "fseeko"); int fseeko64(FILE *fp, off_t offset, int whence); long ftell(FILE *fp); off_t _ftello_(FILE *fp) __asm__ ("" "ftello"); off_t ftello64(FILE *fp); void rewind(FILE *fp); int _fgetpos_(FILE *fp, fpos_t *pos) __asm__ ("" "fgetpos"); int fgetpos64(FILE *fp, fpos_t *pos); int _fsetpos_(FILE *fp, const fpos_t *pos) __asm__ ("" "fsetpos"); int fsetpos64(FILE *fp, const fpos_t *pos); int fgetc_unlocked(FILE *fp); int fputc_unlocked(int c, FILE *fp); int getc_unlocked(FILE *fp); int putc_unlocked(int c, FILE *fp); void clearerr_unlocked(FILE *fp); int feof_unlocked(FILE *fp); int ferror_unlocked(FILE *fp); int fileno_unlocked(FILE *fp); int fflush_unlocked(FILE *fp); size_t fread_unlocked(void *buff, size_t size, size_t n, FILE *fp); size_t fwrite_unlocked(const void *buff, size_t size, size_t n, FILE *fp); char *fgets_unlocked(char *s, int size, FILE *fp); int fputs_unlocked(const char *s, FILE *fp); void clearerr(FILE *fp); int _feof_(FILE *fp) __asm__ ("" "feof"); int _ferror_(FILE *fp) __asm__ ("" "ferror"); int fileno(FILE *fp); int fgetc(FILE *fp); char *fgets(char *s, int size, FILE *fp); int getc(FILE *fp); int ungetc(int c, FILE *fp); int fputc(int c, FILE *fp); int fputs(const char *s, FILE *fp); int putc(int c, FILE *fp); size_t fread(void *buff, size_t size, size_t nmemb, FILE *fp); size_t fwrite(const void *buff, size_t size, size_t nmemb, FILE *fp); int fprintf(FILE *fp, const char *format, ...); int vfprintf(FILE *fp, const char *format, va_list ap); int __fprintf_chk (FILE *fp, int flag, const char *format, ...); int __vfprintf_chk (FILE *fp, int flag, const char *format, va_list ap); ssize_t getdelim(char **line, size_t *size, int delim, FILE *fp); ssize_t getline(char **line, size_t *size, FILE *fp); int fscanf(FILE *fp, const char *format, ...); int vfscanf(FILE *fp, const char *format, va_list ap); int setvbuf(FILE *fp, char *buf, int mode, size_t size); void setbuf(FILE *fp, char *buf); void setbuffer(FILE *fp, char *buf, size_t size); void setlinebuf(FILE *fp); int fflush(FILE *fp); long syscall(long number, ...); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/preload/global.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "global.h" FCFSPreloadGlobalVars g_fcfs_preload_global_vars; static inline void *dlsym_one(const char *fname, const bool required) { int log_level; void *func; if ((func=dlsym(RTLD_NEXT, fname)) == NULL) { log_level = (required ? LOG_ERR : LOG_DEBUG); log_it_ex(&g_log_context, log_level, "file: "__FILE__", line: %d, " "function %s not exist!", __LINE__, fname); } return func; } static inline void *dlsym_two(const char *fname1, const char *fname2, const bool required) { int log_level; void *func; if ((func=dlsym(RTLD_NEXT, fname1)) != NULL) { return func; } if ((func=dlsym(RTLD_NEXT, fname2)) == NULL) { log_level = (required ? LOG_ERR : LOG_DEBUG); log_it_ex(&g_log_context, log_level, "file: "__FILE__", line: %d, " "function %s | %s not exist!", __LINE__, fname1, fname2); } return func; } static int dlsym_papi() { bool required; g_fcfs_preload_global_vars.unsetenv = dlsym_one("unsetenv", true); g_fcfs_preload_global_vars.clearenv = dlsym_one("clearenv", true); g_fcfs_preload_global_vars.fstatat = dlsym_one("fstatat", false); required = (g_fcfs_preload_global_vars.fstatat == NULL); g_fcfs_preload_global_vars.__xstat = dlsym_two( "__xstat", "__xstat64", required); g_fcfs_preload_global_vars.__lxstat = dlsym_two( "__lxstat", "__lxstat64", required); g_fcfs_preload_global_vars.__fxstat = dlsym_two( "__fxstat", "__fxstat64", required); g_fcfs_preload_global_vars.__fxstatat = dlsym_two( "__fxstatat", "__fxstatat64", required); g_fcfs_preload_global_vars.__xmknod = dlsym_one("__xmknod", false); g_fcfs_preload_global_vars.__xmknodat = dlsym_one("__xmknodat", false); g_fcfs_preload_global_vars.mkfifo = dlsym_one("mkfifo", true); g_fcfs_preload_global_vars.mkfifoat = dlsym_one("mkfifoat", false); g_fcfs_preload_global_vars.euidaccess = dlsym_one("euidaccess", false); g_fcfs_preload_global_vars.eaccess = dlsym_one("eaccess", false); g_fcfs_preload_global_vars.futimes = dlsym_one("futimes", true); g_fcfs_preload_global_vars.futimens = dlsym_one("futimens", true); g_fcfs_preload_global_vars.statvfs = dlsym_two( "statvfs", "statvfs64", true); g_fcfs_preload_global_vars.fstatvfs = dlsym_two( "fstatvfs", "fstatvfs64", true); g_fcfs_preload_global_vars.lockf = dlsym_two("lockf", "lockf64", true); g_fcfs_preload_global_vars.posix_fallocate = dlsym_two( "posix_fallocate", "posix_fallocate64", true); g_fcfs_preload_global_vars.posix_fadvise = dlsym_two( "posix_fadvise", "posix_fadvise64", true); g_fcfs_preload_global_vars.vdprintf = dlsym_one("vdprintf", true); g_fcfs_preload_global_vars.opendir = dlsym_one("opendir", true); g_fcfs_preload_global_vars.fdopendir = dlsym_one("fdopendir", true); g_fcfs_preload_global_vars.closedir = dlsym_one("closedir", true); g_fcfs_preload_global_vars.readdir = dlsym_two( "readdir", "readdir64", true); g_fcfs_preload_global_vars.readdir_r = dlsym_two( "readdir_r", "readdir64_r", true); g_fcfs_preload_global_vars.seekdir = dlsym_one("seekdir", true); g_fcfs_preload_global_vars.telldir = dlsym_one("telldir", true); g_fcfs_preload_global_vars.rewinddir = dlsym_one("rewinddir", true); g_fcfs_preload_global_vars.dirfd = dlsym_one("dirfd", true); g_fcfs_preload_global_vars.scandir = dlsym_one("scandir", true); g_fcfs_preload_global_vars.scandirat = dlsym_one("scandirat", false); g_fcfs_preload_global_vars.getcwd = dlsym_one("getcwd", true); g_fcfs_preload_global_vars.getwd = dlsym_one("getwd", true); return 0; } static int dlsym_capi() { g_fcfs_preload_global_vars.fopen = dlsym_two("fopen", "fopen64", true); g_fcfs_preload_global_vars.fdopen = dlsym_one("fdopen", true); g_fcfs_preload_global_vars.freopen = dlsym_two("freopen", "freopen64", true); g_fcfs_preload_global_vars.fclose = dlsym_one("fclose", true); g_fcfs_preload_global_vars.fcloseall = dlsym_one("fcloseall", true); g_fcfs_preload_global_vars.flockfile = dlsym_one("flockfile", true); g_fcfs_preload_global_vars.ftrylockfile = dlsym_one("ftrylockfile", true); g_fcfs_preload_global_vars.funlockfile = dlsym_one("funlockfile", true); g_fcfs_preload_global_vars.fseek = dlsym_one("fseek", true); g_fcfs_preload_global_vars.fseeko = dlsym_two("fseeko", "fseeko64", true); g_fcfs_preload_global_vars.ftell = dlsym_one("ftell", true); g_fcfs_preload_global_vars.ftello = dlsym_two("ftello", "ftello64", true); g_fcfs_preload_global_vars.rewind = dlsym_one("rewind", true); g_fcfs_preload_global_vars.fgetpos = dlsym_one("fgetpos", true); g_fcfs_preload_global_vars.fsetpos = dlsym_one("fsetpos", true); g_fcfs_preload_global_vars.fgetc_unlocked = dlsym_one("fgetc_unlocked", true); g_fcfs_preload_global_vars.fputc_unlocked = dlsym_one("fputc_unlocked", true); g_fcfs_preload_global_vars.getc_unlocked = dlsym_one("getc_unlocked", true); g_fcfs_preload_global_vars.putc_unlocked = dlsym_one("putc_unlocked", true); g_fcfs_preload_global_vars.clearerr_unlocked = dlsym_one("clearerr_unlocked", true); g_fcfs_preload_global_vars.feof_unlocked = dlsym_one("feof_unlocked", true); g_fcfs_preload_global_vars.ferror_unlocked = dlsym_one("ferror_unlocked", true); g_fcfs_preload_global_vars.fileno_unlocked = dlsym_one("fileno_unlocked", true); g_fcfs_preload_global_vars.fflush_unlocked = dlsym_one("fflush_unlocked", true); g_fcfs_preload_global_vars.fread_unlocked = dlsym_one("fread_unlocked", true); g_fcfs_preload_global_vars.fwrite_unlocked = dlsym_one("fwrite_unlocked", true); g_fcfs_preload_global_vars.fgets_unlocked = dlsym_one("fgets_unlocked", true); g_fcfs_preload_global_vars.fputs_unlocked = dlsym_one("fputs_unlocked", true); g_fcfs_preload_global_vars.clearerr = dlsym_one("clearerr", true); g_fcfs_preload_global_vars.feof = dlsym_one("feof", true); g_fcfs_preload_global_vars.ferror = dlsym_one("ferror", true); g_fcfs_preload_global_vars.fileno = dlsym_one("fileno", true); g_fcfs_preload_global_vars.fgetc = dlsym_one("fgetc", true); g_fcfs_preload_global_vars.fgets = dlsym_one("fgets", true); g_fcfs_preload_global_vars.getc = dlsym_one("getc", true); g_fcfs_preload_global_vars.ungetc = dlsym_one("ungetc", true); g_fcfs_preload_global_vars.fputc = dlsym_one("fputc", true); g_fcfs_preload_global_vars.fputs = dlsym_one("fputs", true); g_fcfs_preload_global_vars.putc = dlsym_one("putc", true); g_fcfs_preload_global_vars.fread = dlsym_one("fread", true); g_fcfs_preload_global_vars.fwrite = dlsym_one("fwrite", true); g_fcfs_preload_global_vars.fprintf = dlsym_one("fprintf", true); g_fcfs_preload_global_vars.vfprintf = dlsym_one("vfprintf", true); g_fcfs_preload_global_vars.__fprintf_chk = dlsym_one("__fprintf_chk", false); g_fcfs_preload_global_vars.__vfprintf_chk = dlsym_one("__vfprintf_chk", false); g_fcfs_preload_global_vars.getdelim = dlsym_one("getdelim", true); g_fcfs_preload_global_vars.getline = dlsym_one("getline", true); g_fcfs_preload_global_vars.fscanf = dlsym_one("fscanf", true); g_fcfs_preload_global_vars.vfscanf = dlsym_one("vfscanf", true); g_fcfs_preload_global_vars.setvbuf = dlsym_one("setvbuf", true); g_fcfs_preload_global_vars.setbuf = dlsym_one("setbuf", true); g_fcfs_preload_global_vars.setbuffer = dlsym_one("setbuffer", true); g_fcfs_preload_global_vars.setlinebuf = dlsym_one("setlinebuf", true); g_fcfs_preload_global_vars.fflush = dlsym_one("fflush", true); return 0; } static inline int dlsym_all() { int result; if ((result=dlsym_papi()) != 0) { return result; } return dlsym_capi(); } int fcfs_preload_global_init() { return dlsym_all(); } ================================================ FILE: src/preload/global.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_PRELOAD_GLOBAL_H #define _FCFS_PRELOAD_GLOBAL_H #include "types.h" #ifdef __cplusplus extern "C" { #endif extern FCFSPreloadGlobalVars g_fcfs_preload_global_vars; int fcfs_preload_global_init(); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/preload/types.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_PRELOAD_TYPES_H #define _FCFS_PRELOAD_TYPES_H #include "fastcfs/api/std/posix_api.h" #ifdef OS_LINUX typedef struct __dirstream DIR; #endif #define FCFS_PRELOAD_CALL_SYSTEM 1645611685 #define FCFS_PRELOAD_CALL_FASTCFS 1645611708 typedef struct fcfs_preload_dir_wrapper { int call_type; DIR *dirp; } FCFSPreloadDIRWrapper; typedef struct fcfs_preload_file_wrapper { int call_type; FILE *fp; } FCFSPreloadFILEWrapper; typedef struct fcfs_preload_global_vars { bool inited; int cwd_call_type; struct { int (*unsetenv)(const char *name); int (*clearenv)(void); struct { struct { int (*fstatat)(int fd, const char *path, struct stat *buf, int flags); }; struct { int (*__xstat)(int ver, const char *path, struct stat *buf); int (*__lxstat)(int ver, const char *path, struct stat *buf); int (*__fxstat)(int ver, int fd, struct stat *buf); int (*__fxstatat)(int ver, int fd, const char *path, struct stat *buf, int flags); }; }; int (*__xmknod)(int ver, const char *path, mode_t mode, dev_t *dev); int (*__xmknodat)(int ver, int fd, const char *path, mode_t mode, dev_t *dev); int (*mkfifo)(const char *path, mode_t mode); int (*mkfifoat)(int fd, const char *path, mode_t mode); int (*euidaccess)(const char *path, int mode); int (*eaccess)(const char *path, int mode); int (*futimes)(int fd, const struct timeval times[2]); int (*futimens)(int fd, const struct timespec times[2]); int (*statvfs)(const char *path, struct statvfs *buf); int (*fstatvfs)(int fd, struct statvfs *buf); int (*lockf)(int fd, int cmd, off_t len); int (*posix_fallocate)(int fd, off_t offset, off_t len); int (*posix_fadvise)(int fd, off_t offset, off_t len, int advice); int (*vdprintf)(int fd, const char *format, va_list ap); DIR *(*opendir)(const char *path); DIR *(*fdopendir)(int fd); int (*closedir)(DIR *dirp); struct dirent *(*readdir)(DIR *dirp); int (*readdir_r)(DIR *dirp, struct dirent *entry, struct dirent **result); void (*seekdir)(DIR *dirp, long loc); long (*telldir)(DIR *dirp); void (*rewinddir)(DIR *dirp); int (*dirfd)(DIR *dirp); int (*scandir)(const char *path, struct dirent ***namelist, fcfs_dir_filter_func filter, fcfs_dir_compare_func compar); int (*scandirat)(int fd, const char *path, struct dirent ***namelist, fcfs_dir_filter_func filter, fcfs_dir_compare_func compar); int (*scandir64)(const char *path, struct dirent ***namelist, fcfs_dir_filter_func filter, fcfs_dir_compare_func compar); int (*scandirat64)(int fd, const char *path, struct dirent ***namelist, fcfs_dir_filter_func filter, fcfs_dir_compare_func compar); char *(*getcwd)(char *buf, size_t size); char *(*getwd)(char *buf); int (*__uflow)(FILE *fp); int (*__overflow)(FILE *fp, int ch); }; struct { FILE * (*fopen)(const char *path, const char *mode); FILE * (*fdopen)(int fd, const char *mode); FILE * (*freopen)(const char *path, const char *mode, FILE *fp); int (*fclose)(FILE *fp); int (*fcloseall)(); void (*flockfile)(FILE *fp); int (*ftrylockfile)(FILE *fp); void (*funlockfile)(FILE *fp); int (*fseek)(FILE *fp, long offset, int whence); int (*fseeko)(FILE *fp, off_t offset, int whence); long (*ftell)(FILE *fp); off_t (*ftello)(FILE *fp); void (*rewind)(FILE *fp); int (*fgetpos)(FILE *fp, fpos_t *pos); int (*fsetpos)(FILE *fp, const fpos_t *pos); int (*fgetc_unlocked)(FILE *fp); int (*fputc_unlocked)(int c, FILE *fp); int (*getc_unlocked)(FILE *fp); int (*putc_unlocked)(int c, FILE *fp); void (*clearerr_unlocked)(FILE *fp); int (*feof_unlocked)(FILE *fp); int (*ferror_unlocked)(FILE *fp); int (*fileno_unlocked)(FILE *fp); int (*fflush_unlocked)(FILE *fp); size_t (*fread_unlocked)(void *buff, size_t size, size_t n, FILE *fp); size_t (*fwrite_unlocked)(const void *buff, size_t size, size_t n, FILE *fp); char * (*fgets_unlocked)(char *s, int size, FILE *fp); int (*fputs_unlocked)(const char *s, FILE *fp); void (*clearerr)(FILE *fp); int (*feof)(FILE *fp); int (*ferror)(FILE *fp); int (*fileno)(FILE *fp); int (*fgetc)(FILE *fp); char * (*fgets)(char *s, int size, FILE *fp); int (*getc)(FILE *fp); int (*ungetc)(int c, FILE *fp); int (*fputc)(int c, FILE *fp); int (*fputs)(const char *s, FILE *fp); int (*putc)(int c, FILE *fp); size_t (*fread)(void *buff, size_t size, size_t nmemb, FILE *fp); size_t (*fwrite)(const void *buff, size_t size, size_t nmemb, FILE *fp); int (*fprintf)(FILE *fp, const char *format, ...); int (*vfprintf)(FILE *fp, const char *format, va_list ap); int (*__fprintf_chk)(FILE *fp, int flag, const char *format, ...); int (*__vfprintf_chk)(FILE *fp, int flag, const char *format, va_list ap); ssize_t (*getdelim)(char **line, size_t *size, int delim, FILE *fp); ssize_t (*getline)(char **line, size_t *size, FILE *fp); int (*fscanf)(FILE *fp, const char *format, ...); int (*vfscanf)(FILE *fp, const char *format, va_list ap); int (*setvbuf)(FILE *fp, char *buf, int mode, size_t size); void (*setbuf)(FILE *fp, char *buf); void (*setbuffer)(FILE *fp, char *buf, size_t size); void (*setlinebuf)(FILE *fp); int (*fflush)(FILE *fp); ssize_t (*__libc_readline_unlocked)(FILE *fp, char *buffer, size_t size); }; } FCFSPreloadGlobalVars; #endif ================================================ FILE: src/tools/Makefile.in ================================================ .SUFFIXES: .c .o .lo COMPILE = $(CC) $(CFLAGS) INC_PATH = -I../include -I/usr/local/include LIB_PATH = -L../api $(LIBS) -lfastcommon -lserverframe -lfcfsapi TARGET_PATH = $(TARGET_PREFIX)/bin STATIC_OBJS = ALL_PRGS = fcfs_active_test fcfs_pool_stat all: $(STATIC_OBJS) $(ALL_PRGS) .o: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c.o: $(COMPILE) -c -o $@ $< $(INC_PATH) install: mkdir -p $(TARGET_PATH) cp -f $(ALL_PRGS) $(TARGET_PATH) clean: rm -f $(STATIC_OBJS) $(ALL_PRGS) ================================================ FILE: src/tools/fcfs_active_test.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/connection_pool.h" #include "sf/sf_proto.h" static void usage(char *argv[]) { fprintf(stderr, "Usage: %s [options] \n" "\t-C | --connect-timeout=2: connect timeout in seconds\n" "\t-N | --network-timeout=10: network timeout in seconds\n\n", argv[0]); } int main(int argc, char *argv[]) { int ch; const struct option longopts[] = { {"connect-timeout", required_argument, NULL, 'C'}, {"network-timeout", required_argument, NULL, 'N'}, {NULL, 0, NULL, 0} }; char host[256]; ConnectionInfo conn; SFResponseInfo response; int connect_timeout; int network_timeout; int result; if (argc < 2) { usage(argv); return 1; } log_init(); //g_log_context.log_level = LOG_DEBUG; connect_timeout = 2; network_timeout = 10; while ((ch=getopt_long(argc, argv, "C:N:", longopts, NULL)) != -1) { switch (ch) { case 'C': connect_timeout = strtol(optarg, NULL, 10); break; case 'N': network_timeout = strtol(optarg, NULL, 10); break; default: usage(argv); return 1; } } if (argc - optind >= 2) { if (strchr(argv[optind], ':') != NULL) { fprintf(stderr, "Error: too many arguments!\n"); usage(argv); return 1; } snprintf(host, sizeof(host), "%s:%s", argv[optind], argv[optind+1]); } else if (argc - optind >= 1) { if (strchr(argv[optind], ':') == NULL) { fprintf(stderr, "Error: expect service port!\n"); usage(argv); return 1; } snprintf(host, sizeof(host), "%s", argv[optind]); } else { fprintf(stderr, "Error: expect host!\n"); usage(argv); return 1; } if ((result=conn_pool_parse_server_info(host, &conn, 0)) != 0) { return result; } if ((result=conn_pool_connect_server(&conn, connect_timeout)) != 0) { return result; } response.error.length = 0; if ((result=sf_active_test(&conn, &response, network_timeout)) != 0) { sf_log_network_error(&response, &conn, NULL, result); return result; } printf("OK\n"); return 0; } ================================================ FILE: src/tools/fcfs_pool_stat.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/connection_pool.h" #include "sf/sf_proto.h" #include "fastcfs/api/fcfs_api.h" static void usage(char *argv[]) { fprintf(stderr, "Usage: %s [-c config_file=%s] [poolname]\n", argv[0], FCFS_FUSE_DEFAULT_CONFIG_FILENAME); } int main(int argc, char *argv[]) { const bool publish = false; const char *config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME; struct statvfs stbuf; const char *poolname; char *ns_str; char ns_holder[NAME_MAX]; IniContext ini_context; int64_t total; int64_t used; int64_t avail; int ch; int result; log_init(); while ((ch=getopt(argc, argv, "c:h")) != -1) { switch (ch) { case 'h': usage(argv); return 0; case 'c': config_filename = optarg; break; default: usage(argv); return 1; } } if (argc - optind >= 1) { poolname = argv[optind]; } else { if ((result=iniLoadFromFile(config_filename, &ini_context)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, config_filename, result); return result; } ns_str = iniGetStrValue(FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, "namespace", &ini_context); if (ns_str == NULL || *ns_str == '\0') { logError("file: "__FILE__", line: %d, " "config file: %s, section: %s, item: namespace " "not exist or is empty", __LINE__, config_filename, FCFS_API_DEFAULT_FASTDIR_SECTION_NAME); return ENOENT; } fc_safe_strcpy(ns_holder, ns_str); poolname = ns_holder; iniFreeContext(&ini_context); } if ((result=fcfs_api_init_with_auth(poolname, config_filename, publish)) != 0) { return result; } if ((result=fcfs_api_statvfs("/", &stbuf)) == 0) { total = stbuf.f_blocks * stbuf.f_frsize; used = (stbuf.f_blocks - stbuf.f_bfree) * stbuf.f_frsize; avail = stbuf.f_bavail * stbuf.f_frsize; printf("{\"total\": %"PRId64", \"used\": %"PRId64", " "\"avail\": %"PRId64"}\n", total, used, avail); } return result; } ================================================ FILE: src/vote/client/Makefile.in ================================================ .SUFFIXES: .c .o .lo COMPILE = $(CC) $(CFLAGS) INC_PATH = -I../common LIB_PATH = $(LIBS) -lfastcommon -lserverframe TARGET_LIB = $(TARGET_PREFIX)/$(LIB_VERSION) FAST_SHARED_OBJS = ../common/vote_global.lo ../common/vote_proto.lo \ client_func.lo client_global.lo client_proto.lo FAST_STATIC_OBJS = ../common/vote_global.o ../common/vote_proto.o \ client_func.o client_global.o client_proto.o HEADER_FILES = ../common/vote_global.h ../common/vote_types.h \ ../common/vote_proto.h client_types.h client_func.h \ client_global.h client_proto.h fcfs_vote_client.h ALL_OBJS = $(FAST_STATIC_OBJS) $(FAST_SHARED_OBJS) ALL_PRGS = SHARED_LIBS = libfcfsvoteclient.so STATIC_LIBS = libfcfsvoteclient.a ALL_LIBS = $(SHARED_LIBS) $(STATIC_LIBS) all: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) libfcfsvoteclient.so: $(FAST_SHARED_OBJS) $(COMPILE) -o $@ -shared $(FAST_SHARED_OBJS) $(LIB_PATH) libfcfsvoteclient.a: $(FAST_STATIC_OBJS) ar rcs $@ $(FAST_STATIC_OBJS) .o: $(COMPILE) -o $@ $< $(FAST_STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c: $(COMPILE) -o $@ $< $(FAST_STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c.o: $(COMPILE) -c -o $@ $< $(INC_PATH) .c.lo: $(COMPILE) -c -fPIC -o $@ $< $(INC_PATH) install: mkdir -p $(TARGET_LIB) mkdir -p $(TARGET_PREFIX)/lib mkdir -p $(TARGET_PREFIX)/include/fastcfs/vote install -m 755 $(SHARED_LIBS) $(TARGET_LIB) install -m 644 $(HEADER_FILES) $(TARGET_PREFIX)/include/fastcfs/vote @BUILDROOT=$$(echo "$(TARGET_PREFIX)" | grep BUILDROOT); \ if [ -z "$$BUILDROOT" ] && [ "$(TARGET_LIB)" != "$(TARGET_PREFIX)/lib" ]; then ln -sf $(TARGET_LIB)/libfcfsvoteclient.so $(TARGET_PREFIX)/lib/libfcfsvoteclient.so; fi clean: rm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) ================================================ FILE: src/vote/client/client_func.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fastcommon/ini_file_reader.h" #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "sf/sf_global.h" #include "client_global.h" #include "client_func.h" static int fcfs_vote_client_do_init_ex(FCFSVoteClientContext *client_ctx, IniFullContext *ini_ctx) { int result; client_ctx->connect_timeout = iniGetIntValueEx( ini_ctx->section_name, "connect_timeout", ini_ctx->context, SF_DEFAULT_CONNECT_TIMEOUT, true); if (client_ctx->connect_timeout <= 0) { client_ctx->connect_timeout = SF_DEFAULT_CONNECT_TIMEOUT; } client_ctx->network_timeout = iniGetIntValueEx( ini_ctx->section_name, "network_timeout", ini_ctx->context, SF_DEFAULT_NETWORK_TIMEOUT, true); if (client_ctx->network_timeout <= 0) { client_ctx->network_timeout = SF_DEFAULT_NETWORK_TIMEOUT; } if ((result=sf_load_cluster_config(&client_ctx->cluster, ini_ctx, FCFS_VOTE_DEFAULT_CLUSTER_PORT)) != 0) { return result; } return 0; } void fcfs_vote_client_log_config_ex(FCFSVoteClientContext *client_ctx, const char *extra_config) { logInfo("fvote v%d.%d.%d, " "connect_timeout=%d, " "network_timeout=%d, " "vote_server_count=%d%s%s", g_fcfs_vote_global_vars.version.major, g_fcfs_vote_global_vars.version.minor, g_fcfs_vote_global_vars.version.patch, client_ctx->connect_timeout, client_ctx->network_timeout, FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg), extra_config != NULL ? ", " : "", extra_config != NULL ? extra_config : ""); } int fcfs_vote_client_load_from_file_ex1(FCFSVoteClientContext *client_ctx, IniFullContext *ini_ctx) { IniContext iniContext; int result; if (ini_ctx->context == NULL) { if ((result=iniLoadFromFile(ini_ctx->filename, &iniContext)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, ini_ctx->filename, result); return result; } ini_ctx->context = &iniContext; } result = fcfs_vote_client_do_init_ex(client_ctx, ini_ctx); if (ini_ctx->context == &iniContext) { iniFreeContext(&iniContext); ini_ctx->context = NULL; } return result; } int fcfs_vote_client_init_ex2(FCFSVoteClientContext *client_ctx, IniFullContext *ini_ctx) { int result; if ((result=fcfs_vote_client_load_from_file_ex1( client_ctx, ini_ctx)) != 0) { return result; } return 0; } int fcfs_vote_client_init_for_server(IniFullContext *ini_ctx, bool *vote_node_enabled) { g_fcfs_vote_client_vars.client_ctx.connect_timeout = SF_G_CONNECT_TIMEOUT; g_fcfs_vote_client_vars.client_ctx.network_timeout = SF_G_NETWORK_TIMEOUT; *vote_node_enabled = iniGetBoolValue(ini_ctx->section_name, "vote_node_enabled", ini_ctx->context, false); if (*vote_node_enabled) { return sf_load_cluster_config1(&g_fcfs_vote_client_vars.client_ctx. cluster, ini_ctx, "vote_node_cluster_filename", FCFS_VOTE_DEFAULT_CLUSTER_PORT); } else { return 0; } } void fcfs_vote_client_destroy_ex(FCFSVoteClientContext *client_ctx) { fc_server_destroy(&client_ctx->cluster.server_cfg); memset(client_ctx, 0, sizeof(FCFSVoteClientContext)); } ================================================ FILE: src/vote/client/client_func.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_VOTE_CLIENT_FUNC_H #define _FCFS_VOTE_CLIENT_FUNC_H #include "vote_global.h" #include "client_types.h" #ifdef __cplusplus extern "C" { #endif #define fcfs_vote_client_load_from_file(filename) \ fcfs_vote_client_load_from_file_ex((&g_fcfs_vote_client_vars. \ client_ctx), filename, NULL) #define fcfs_vote_client_init(filename) \ fcfs_vote_client_init_ex1((&g_fcfs_vote_client_vars. \ client_ctx), filename, NULL) #define fcfs_vote_client_destroy() \ fcfs_vote_client_destroy_ex((&g_fcfs_vote_client_vars.client_ctx)) #define fcfs_vote_client_log_config(client_ctx) \ fcfs_vote_client_log_config_ex(client_ctx, NULL) int fcfs_vote_client_load_from_file_ex1(FCFSVoteClientContext *client_ctx, IniFullContext *ini_ctx); /** * client initial from config file * params: * client_ctx: the client context * config_filename: the client config filename * section_name: the section name, NULL or empty for global section * return: 0 success, != 0 fail, return the error code **/ static inline int fcfs_vote_client_load_from_file_ex(FCFSVoteClientContext *client_ctx, const char *config_filename, const char *section_name) { IniFullContext ini_ctx; FAST_INI_SET_FULL_CTX(ini_ctx, config_filename, section_name); return fcfs_vote_client_load_from_file_ex1(client_ctx, &ini_ctx); } int fcfs_vote_client_init_ex2(FCFSVoteClientContext *client_ctx, IniFullContext *ini_ctx); static inline int fcfs_vote_client_init_ex1(FCFSVoteClientContext *client_ctx, const char *config_filename, const char *section_name) { IniFullContext ini_ctx; FAST_INI_SET_FULL_CTX(ini_ctx, config_filename, section_name); return fcfs_vote_client_init_ex2(client_ctx, &ini_ctx); } int fcfs_vote_client_init_for_server(IniFullContext *ini_ctx, bool *vote_node_enabled); /** * client destroy function * params: * client_ctx: tracker group * return: none **/ void fcfs_vote_client_destroy_ex(FCFSVoteClientContext *client_ctx); void fcfs_vote_client_log_config_ex(FCFSVoteClientContext *client_ctx, const char *extra_config); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/client/client_global.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "client_global.h" FCFSVoteClientGlobalVars g_fcfs_vote_client_vars; ================================================ FILE: src/vote/client/client_global.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_VOTE_CLIENT_GLOBAL_H #define _FCFS_VOTE_CLIENT_GLOBAL_H #include "vote_global.h" #include "client_types.h" typedef struct fcfs_vote_client_global_vars { FCFSVoteClientContext client_ctx; } FCFSVoteClientGlobalVars; #ifdef __cplusplus extern "C" { #endif extern FCFSVoteClientGlobalVars g_fcfs_vote_client_vars; #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/client/client_proto.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/ini_file_reader.h" #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/connection_pool.h" #include "client_global.h" #include "client_proto.h" static inline int get_spec_connection(FCFSVoteClientContext *client_ctx, const ConnectionInfo *target, ConnectionInfo *conn) { *conn = *target; conn->sock = -1; return conn_pool_connect_server(conn, client_ctx->connect_timeout); } static inline int make_connection(FCFSVoteClientContext *client_ctx, FCServerInfo *server, ConnectionInfo *conn) { return fc_server_make_connection(&server->group_addrs[client_ctx-> cluster.service_group_index].address_array, conn, "fvote", client_ctx->connect_timeout); } static int get_connection(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn) { int index; int i; int result; FCServerInfo *server; index = rand() % FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg); server = FC_SID_SERVERS(client_ctx->cluster.server_cfg) + index; if ((result=make_connection(client_ctx, server, conn)) == 0) { return 0; } i = (index + 1) % FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg); while (i != index) { server = FC_SID_SERVERS(client_ctx->cluster.server_cfg) + i; if ((result=make_connection(client_ctx, server, conn)) == 0) { return 0; } i = (i + 1) % FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg); } if (FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg) > 1) { logError("file: "__FILE__", line: %d, " "get vote connection fail, configured server count: %d", __LINE__, FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg)); } return result; } int vote_client_proto_get_master_connection_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn) { int result; FCFSVoteProtoHeader *header; FCFSVoteClientServerEntry master; SFResponseInfo response; FCFSVoteProtoGetServerResp server_resp; char out_buff[sizeof(FCFSVoteProtoHeader)]; if ((result=get_connection(client_ctx, conn)) != 0) { return result; } header = (FCFSVoteProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ, sizeof(out_buff) - sizeof(FCFSVoteProtoHeader)); response.error.length = 0; if ((result=sf_send_and_recv_response(conn, out_buff, sizeof(out_buff), &response, client_ctx->network_timeout, FCFS_VOTE_SERVICE_PROTO_GET_MASTER_RESP, (char *) &server_resp, sizeof(FCFSVoteProtoGetServerResp))) != 0) { vote_log_network_error(&response, conn, result); conn_pool_disconnect_server(conn); return result; } master.server_id = buff2int(server_resp.server_id); memcpy(master.conn.ip_addr, server_resp.ip_addr, IP_ADDRESS_SIZE); *(master.conn.ip_addr + IP_ADDRESS_SIZE - 1) = '\0'; master.conn.port = buff2short(server_resp.port); master.conn.comm_type = conn->comm_type; if (FC_CONNECTION_SERVER_EQUAL1(*conn, master.conn)) { return 0; } conn_pool_disconnect_server(conn); return get_spec_connection(client_ctx, &master.conn, conn); } int fcfs_vote_client_cluster_stat_ex(FCFSVoteClientContext *client_ctx, FCFSVoteClientClusterStatEntry *stats, const int size, int *count) { FCFSVoteProtoHeader *header; FCFSVoteProtoClusterStatRespBodyHeader *body_header; FCFSVoteProtoClusterStatRespBodyPart *body_part; FCFSVoteProtoClusterStatRespBodyPart *body_end; FCFSVoteClientClusterStatEntry *stat; ConnectionInfo conn; char out_buff[sizeof(FCFSVoteProtoHeader)]; char fixed_buff[8 * 1024]; char *in_buff; SFResponseInfo response; int result; int calc_size; if ((result=vote_client_proto_get_master_connection_ex( client_ctx, &conn)) != 0) { return result; } header = (FCFSVoteProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_REQ, sizeof(out_buff) - sizeof(FCFSVoteProtoHeader)); response.error.length = 0; in_buff = fixed_buff; if ((result=sf_send_and_check_response_header(&conn, out_buff, sizeof(out_buff), &response, client_ctx->network_timeout, FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_RESP)) == 0) { if (response.header.body_len > sizeof(fixed_buff)) { in_buff = (char *)fc_malloc(response.header.body_len); if (in_buff == NULL) { response.error.length = sprintf(response.error.message, "malloc %d bytes fail", response.header.body_len); result = ENOMEM; } } if (result == 0) { result = tcprecvdata_nb(conn.sock, in_buff, response. header.body_len, client_ctx->network_timeout); } } body_header = (FCFSVoteProtoClusterStatRespBodyHeader *)in_buff; body_part = (FCFSVoteProtoClusterStatRespBodyPart *)(in_buff + sizeof(FCFSVoteProtoClusterStatRespBodyHeader)); if (result == 0) { *count = buff2int(body_header->count); calc_size = sizeof(FCFSVoteProtoClusterStatRespBodyHeader) + (*count) * sizeof(FCFSVoteProtoClusterStatRespBodyPart); if (calc_size != response.header.body_len) { response.error.length = sprintf(response.error.message, "response body length: %d != calculate size: %d, " "server count: %d", response.header.body_len, calc_size, *count); result = EINVAL; } else if (size < *count) { response.error.length = sprintf(response.error.message, "entry size %d too small < %d", size, *count); *count = 0; result = ENOSPC; } } else { *count = 0; } if (result != 0) { vote_log_network_error(&response, &conn, result); } else { body_end = body_part + (*count); for (stat=stats; body_partis_online = body_part->is_online; stat->is_master = body_part->is_master; stat->server_id = buff2int(body_part->server_id); memcpy(stat->ip_addr, body_part->ip_addr, IP_ADDRESS_SIZE); *(stat->ip_addr + IP_ADDRESS_SIZE - 1) = '\0'; stat->port = buff2short(body_part->port); } } vote_client_proto_close_connection_ex(client_ctx, &conn); if (in_buff != fixed_buff) { if (in_buff != NULL) { free(in_buff); } } return result; } int vote_client_proto_join_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn, const FCFSVoteClientJoinRequest *r) { int result; FCFSVoteProtoHeader *header; FCFSVoteProtoClientJoinReq *req; SFResponseInfo response; char out_buff[sizeof(FCFSVoteProtoHeader) + sizeof(FCFSVoteProtoClientJoinReq)]; header = (FCFSVoteProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_REQ, sizeof(out_buff) - sizeof(FCFSVoteProtoHeader)); req = (FCFSVoteProtoClientJoinReq *)(header + 1); int2buff(r->server_id, req->server_id); int2buff(r->group_id, req->group_id); short2buff(r->response_size, req->response_size); req->service_id = r->service_id; req->is_leader = (r->is_leader ? 1 : 0); req->persistent = (r->persistent ? 1 : 0); memcpy(req->config_sign, client_ctx->cluster.md5_digest, SF_CLUSTER_CONFIG_SIGN_LEN); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, sizeof(out_buff), &response, client_ctx->network_timeout, FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_RESP)) != 0) { vote_log_network_error(&response, conn, result); } return result; } int vote_client_proto_get_vote_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn, const SFGetServerStatusRequest *r, char *in_buff, const int in_len) { int result; FCFSVoteProtoHeader *header; SFProtoGetServerStatusReq *req; SFResponseInfo response; char out_buff[sizeof(FCFSVoteProtoHeader) + sizeof(SFProtoGetServerStatusReq)]; header = (FCFSVoteProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, FCFS_VOTE_SERVICE_PROTO_GET_VOTE_REQ, sizeof(out_buff) - sizeof(FCFSVoteProtoHeader)); req = (SFProtoGetServerStatusReq *)(header + 1); sf_proto_get_server_status_pack(r, req); response.error.length = 0; if ((result=sf_send_and_recv_response(conn, out_buff, sizeof(out_buff), &response, client_ctx->network_timeout, FCFS_VOTE_SERVICE_PROTO_GET_VOTE_RESP, in_buff, in_len)) != 0) { vote_log_network_error(&response, conn, result); } return result; } int vote_client_proto_notify_next_leader_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn, const unsigned char req_cmd) { int result; FCFSVoteProtoHeader *header; SFResponseInfo response; char out_buff[sizeof(FCFSVoteProtoHeader)]; header = (FCFSVoteProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, req_cmd, sizeof(out_buff) - sizeof(FCFSVoteProtoHeader)); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, sizeof(out_buff), &response, client_ctx-> network_timeout, SF_PROTO_ACK)) != 0) { vote_log_network_error(&response, conn, result); } return result; } int vote_client_proto_active_check_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn) { int result; FCFSVoteProtoHeader *header; SFResponseInfo response; char out_buff[sizeof(FCFSVoteProtoHeader)]; header = (FCFSVoteProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_REQ, sizeof(out_buff) - sizeof(FCFSVoteProtoHeader)); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, sizeof(out_buff), &response, client_ctx->network_timeout, FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_RESP)) != 0) { vote_log_network_error(&response, conn, result); } return result; } int fcfs_vote_client_get_vote_ex(FCFSVoteClientContext *client_ctx, const FCFSVoteClientJoinRequest *join_request, const unsigned char *servers_sign, const unsigned char *cluster_sign, char *in_buff, const int in_len) { int result; ConnectionInfo conn; SFGetServerStatusRequest status_request; if ((result=fcfs_vote_client_join_ex(client_ctx, &conn, join_request)) == 0) { status_request.servers_sign = servers_sign; status_request.cluster_sign = cluster_sign; status_request.server_id = join_request->server_id; status_request.is_leader = join_request->is_leader; result = vote_client_proto_get_vote_ex(client_ctx, &conn, &status_request, in_buff, in_len); vote_client_proto_close_connection_ex(client_ctx, &conn); } return result; } int fcfs_vote_client_notify_next_leader_ex(FCFSVoteClientContext *client_ctx, const FCFSVoteClientJoinRequest *join_request, const unsigned char req_cmd) { int result; ConnectionInfo conn; if ((result=fcfs_vote_client_join_ex(client_ctx, &conn, join_request)) == 0) { result = vote_client_proto_notify_next_leader_ex( client_ctx, &conn, req_cmd); vote_client_proto_close_connection_ex(client_ctx, &conn); } return result; } ================================================ FILE: src/vote/client/client_proto.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_VOTE_CLIENT_PROTO_H #define _FCFS_VOTE_CLIENT_PROTO_H #include "fastcommon/fast_mpool.h" #include "fastcommon/connection_pool.h" #include "sf/sf_proto.h" #include "vote_proto.h" #include "client_types.h" typedef struct fcfs_vote_client_cluster_stat_ex_entry { int server_id; bool is_online; bool is_master; uint16_t port; char ip_addr[IP_ADDRESS_SIZE]; } FCFSVoteClientClusterStatEntry; typedef struct fcfs_vote_client_join_request { int server_id; int group_id; short response_size; short service_id; bool is_leader; bool persistent; } FCFSVoteClientJoinRequest; #ifdef __cplusplus extern "C" { #endif int vote_client_proto_get_master_connection_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn); int vote_client_proto_join_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn, const FCFSVoteClientJoinRequest *r); int vote_client_proto_get_vote_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn, const SFGetServerStatusRequest *r, char *in_buff, const int in_len); int vote_client_proto_notify_next_leader_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn, const unsigned char req_cmd); static inline int vote_client_proto_pre_set_next_leader_ex( FCFSVoteClientContext *client_ctx, ConnectionInfo *conn) { return vote_client_proto_notify_next_leader_ex(client_ctx, conn, FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER); } static inline int vote_client_proto_commit_next_leader_ex( FCFSVoteClientContext *client_ctx, ConnectionInfo *conn) { return vote_client_proto_notify_next_leader_ex(client_ctx, conn, FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER); } int vote_client_proto_active_check_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn); static inline void vote_client_proto_close_connection_ex( FCFSVoteClientContext *client_ctx, ConnectionInfo *conn) { conn_pool_disconnect_server(conn); } static inline int fcfs_vote_client_join_ex(FCFSVoteClientContext *client_ctx, ConnectionInfo *conn, const FCFSVoteClientJoinRequest *r) { int result; if ((result=vote_client_proto_get_master_connection_ex( client_ctx, conn)) != 0) { return result; } if ((result=vote_client_proto_join_ex(client_ctx, conn, r)) != 0) { vote_client_proto_close_connection_ex(client_ctx, conn); } return result; } int fcfs_vote_client_get_vote_ex(FCFSVoteClientContext *client_ctx, const FCFSVoteClientJoinRequest *join_request, const unsigned char *servers_sign, const unsigned char *cluster_sign, char *in_buff, const int in_len); int fcfs_vote_client_notify_next_leader_ex(FCFSVoteClientContext *client_ctx, const FCFSVoteClientJoinRequest *join_request, const unsigned char req_cmd); int fcfs_vote_client_cluster_stat_ex(FCFSVoteClientContext *client_ctx, FCFSVoteClientClusterStatEntry *stats, const int size, int *count); #define vote_client_proto_get_master_connection(conn) \ vote_client_proto_get_master_connection_ex(&g_fcfs_vote_client_vars. \ client_ctx, conn) #define vote_client_proto_join(conn, r) \ vote_client_proto_join_ex(&g_fcfs_vote_client_vars.client_ctx, conn, r) #define vote_client_proto_get_vote(conn, r, in_buff, in_len) \ vote_client_proto_get_vote_ex(&g_fcfs_vote_client_vars. \ client_ctx, conn, r, in_buff, in_len) #define vote_client_proto_notify_next_leader(conn, req_cmd) \ vote_client_proto_notify_next_leader_ex(&g_fcfs_vote_client_vars. \ client_ctx, conn, req_cmd) #define vote_client_proto_pre_set_next_leader(conn) \ vote_client_proto_pre_set_next_leader_ex(&g_fcfs_vote_client_vars. \ client_ctx, conn) #define vote_client_proto_commit_next_leader(conn) \ vote_client_proto_commit_next_leader_ex(&g_fcfs_vote_client_vars. \ client_ctx, conn) #define vote_client_proto_active_check(conn) \ vote_client_proto_active_check_ex(&g_fcfs_vote_client_vars. \ client_ctx, conn) #define vote_client_proto_close_connection(conn) \ vote_client_proto_close_connection_ex(&g_fcfs_vote_client_vars. \ client_ctx, conn) #define fcfs_vote_client_join(conn, r) \ fcfs_vote_client_join_ex(&g_fcfs_vote_client_vars.client_ctx, conn, r) #define fcfs_vote_client_get_vote(join_request, servers_sign, \ cluster_sign, in_buff, in_len) \ fcfs_vote_client_get_vote_ex(&g_fcfs_vote_client_vars.client_ctx, \ join_request, servers_sign, cluster_sign, in_buff, in_len) #define fcfs_vote_client_notify_next_leader(join_request, req_cmd) \ fcfs_vote_client_notify_next_leader_ex( \ &g_fcfs_vote_client_vars.client_ctx, \ join_request, req_cmd) #define fcfs_vote_client_pre_set_next_leader(join_request) \ fcfs_vote_client_notify_next_leader_ex( \ &g_fcfs_vote_client_vars.client_ctx, join_request, \ FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER) #define fcfs_vote_client_commit_next_leader(join_request) \ fcfs_vote_client_notify_next_leader_ex( \ &g_fcfs_vote_client_vars.client_ctx, join_request, \ FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER) #define fcfs_vote_client_cluster_stat(stats, size, count) \ fcfs_vote_client_cluster_stat_ex(&g_fcfs_vote_client_vars. \ client_ctx, stats, size, count) #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/client/client_types.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_VOTE_CLIENT_TYPES_H #define _FCFS_VOTE_CLIENT_TYPES_H #include "fastcommon/common_define.h" #include "sf/sf_configs.h" #include "sf/sf_connection_manager.h" #include "sf/sf_cluster_cfg.h" #include "vote_types.h" #define FCFS_VOTE_CLIENT_DEFAULT_CONFIG_FILENAME "/etc/fastcfs/vote/client.conf" typedef struct fcfs_vote_client_server_entry { int server_id; ConnectionInfo conn; char status; } FCFSVoteClientServerEntry; typedef struct fcfs_vote_client_context { SFClusterConfig cluster; int connect_timeout; int network_timeout; } FCFSVoteClientContext; #endif ================================================ FILE: src/vote/client/fcfs_vote_client.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_VOTE_CLIENT_H #define _FCFS_VOTE_CLIENT_H #include "client_types.h" #include "client_func.h" #include "client_global.h" #include "client_proto.h" #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/client/tools/Makefile.in ================================================ .SUFFIXES: .c .o .lo COMPILE = $(CC) $(CFLAGS) INC_PATH = -I../../common -I/usr/local/include LIB_PATH = -L.. $(LIBS) -lfcfsvoteclient -lfastcommon -lserverframe TARGET_PATH = $(TARGET_PREFIX)/bin STATIC_OBJS = ALL_PRGS = fvote_cluster_stat all: $(STATIC_OBJS) $(ALL_PRGS) .o: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c.o: $(COMPILE) -c -o $@ $< $(INC_PATH) install: mkdir -p $(TARGET_PATH) cp -f $(ALL_PRGS) $(TARGET_PATH) clean: rm -f $(STATIC_OBJS) $(ALL_PRGS) ================================================ FILE: src/vote/client/tools/fvote_cluster_stat.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "../fcfs_vote_client.h" static void usage(char *argv[]) { fprintf(stderr, "Usage: %s [-c config_filename=%s]\n", argv[0], FCFS_VOTE_CLIENT_DEFAULT_CONFIG_FILENAME); } static void output(FCFSVoteClientClusterStatEntry *stats, const int count) { FCFSVoteClientClusterStatEntry *stat; FCFSVoteClientClusterStatEntry *end; char formatted_ip[FORMATTED_IP_SIZE]; end = stats + count; for (stat=stats; statip_addr, formatted_ip); printf( "server_id: %d, host: %s:%u, " "is_online: %d, is_master: %d\n", stat->server_id, formatted_ip, stat->port, stat->is_online, stat->is_master); } printf("\nserver count: %d\n\n", count); } int main(int argc, char *argv[]) { #define CLUSTER_MAX_SERVER_COUNT 16 int ch; const char *config_filename = FCFS_VOTE_CLIENT_DEFAULT_CONFIG_FILENAME; int count; FCFSVoteClientClusterStatEntry stats[CLUSTER_MAX_SERVER_COUNT]; int result; while ((ch=getopt(argc, argv, "hc:")) != -1) { switch (ch) { case 'h': usage(argv); break; case 'c': config_filename = optarg; break; default: usage(argv); return 1; } } log_init(); //g_log_context.log_level = LOG_DEBUG; if ((result=fcfs_vote_client_init(config_filename)) != 0) { return result; } if ((result=fcfs_vote_client_cluster_stat(stats, CLUSTER_MAX_SERVER_COUNT, &count)) != 0) { fprintf(stderr, "fcfs_vote_client_cluster_stat fail, " "errno: %d, error info: %s\n", result, STRERROR(result)); return result; } output(stats, count); return 0; } ================================================ FILE: src/vote/common/vote_global.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "vote_global.h" FCFSVoteGlobalVars g_fcfs_vote_global_vars = { {5, 5, 0} }; ================================================ FILE: src/vote/common/vote_global.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_VOTE_GLOBAL_H #define _FCFS_VOTE_GLOBAL_H #include "fastcommon/common_define.h" typedef struct fcfs_vote_global_vars { Version version; } FCFSVoteGlobalVars; #ifdef __cplusplus extern "C" { #endif extern FCFSVoteGlobalVars g_fcfs_vote_global_vars; #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/common/vote_proto.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "vote_proto.h" void fcfs_vote_proto_init() { } const char *fcfs_vote_get_cmd_caption(const int cmd) { switch (cmd) { case FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ: return "GET_MASTER_REQ"; case FCFS_VOTE_SERVICE_PROTO_GET_MASTER_RESP: return "GET_MASTER_RESP"; case FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_REQ: return "CLIENT_JOIN_REQ"; case FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_RESP: return "CLIENT_JOIN_RESP"; case FCFS_VOTE_SERVICE_PROTO_GET_VOTE_REQ: return "GET_VOTE_REQ"; case FCFS_VOTE_SERVICE_PROTO_GET_VOTE_RESP: return "GET_VOTE_RESP"; case FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER: return "VOTE_PRE_SET_NEXT_LEADER"; case FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER: return "VOTE_COMMIT_NEXT_LEADER"; case FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_REQ: return "VOTE_ACTIVE_CHECK_REQ"; case FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_RESP: return "VOTE_ACTIVE_CHECK_RESP"; case FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_REQ: return "CLUSTER_STAT_REQ"; case FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_RESP: return "CLUSTER_STAT_RESP"; case FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_REQ: return "GET_SERVER_STATUS_REQ"; case FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_RESP: return "GET_SERVER_STATUS_RESP"; case FCFS_VOTE_CLUSTER_PROTO_JOIN_MASTER: return "JOIN_MASTER"; case FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_REQ: return "PING_MASTER_REQ"; case FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_RESP: return "PING_MASTER_RESP"; case FCFS_VOTE_CLUSTER_PROTO_PRE_SET_NEXT_MASTER: return "PRE_SET_NEXT_MASTER"; case FCFS_VOTE_CLUSTER_PROTO_COMMIT_NEXT_MASTER: return "COMMIT_NEXT_MASTER"; default: return sf_get_cmd_caption(cmd); } } ================================================ FILE: src/vote/common/vote_proto.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_VOTE_PROTO_H #define _FCFS_VOTE_PROTO_H #include "sf/sf_proto.h" #include "vote_types.h" //service commands #define FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ 61 #define FCFS_VOTE_SERVICE_PROTO_GET_MASTER_RESP 62 #define FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_REQ 63 #define FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_RESP 64 #define FCFS_VOTE_SERVICE_PROTO_GET_VOTE_REQ \ SF_CLUSTER_PROTO_GET_SERVER_STATUS_REQ #define FCFS_VOTE_SERVICE_PROTO_GET_VOTE_RESP \ SF_CLUSTER_PROTO_GET_SERVER_STATUS_RESP #define FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER 67 #define FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER 69 #define FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_REQ 71 #define FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_RESP 72 #define FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_REQ 77 #define FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_RESP 78 //cluster commands #define FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_REQ 81 #define FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_RESP 82 #define FCFS_VOTE_CLUSTER_PROTO_JOIN_MASTER 83 //slave -> master #define FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_REQ 85 #define FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_RESP 86 #define FCFS_VOTE_CLUSTER_PROTO_PRE_SET_NEXT_MASTER 87 //notify next master to other servers #define FCFS_VOTE_CLUSTER_PROTO_COMMIT_NEXT_MASTER 88 //commit next master to other servers typedef SFCommonProtoHeader FCFSVoteProtoHeader; typedef struct fcfs_vote_proto_client_join_req { char server_id[4]; char group_id[4]; char response_size[2]; char is_leader; unsigned char service_id; //which service: fauth/fdir/fstore char persistent; char padding[3]; char config_sign[SF_CLUSTER_CONFIG_SIGN_LEN]; } FCFSVoteProtoClientJoinReq; typedef SFProtoGetServerResp FCFSVoteProtoGetServerResp; typedef struct fcfs_vote_proto_get_server_status_req { char config_sign[SF_CLUSTER_CONFIG_SIGN_LEN]; char server_id[4]; char padding[4]; } FCFSVoteProtoGetServerStatusReq; typedef struct fcfs_vote_proto_get_server_status_resp { char server_id[4]; char is_master; char padding[3]; } FCFSVoteProtoGetServerStatusResp; typedef struct fcfs_vote_proto_join_master_req { char server_id[4]; //the slave server id char padding[4]; char config_sign[SF_CLUSTER_CONFIG_SIGN_LEN]; } FCFSVoteProtoJoinMasterReq; typedef struct fcfs_vote_proto_cluster_stat_resp_body_header { char count[4]; char padding[4]; } FCFSVoteProtoClusterStatRespBodyHeader; typedef struct fcfs_vote_proto_cluster_stat_resp_body_part { char server_id[4]; char is_online; char is_master; char padding[1]; char port[2]; char ip_addr[IP_ADDRESS_SIZE]; } FCFSVoteProtoClusterStatRespBodyPart; #define vote_log_network_error_ex(response, conn, result, log_level) \ sf_log_network_error_ex(response, conn, "fvote", result, log_level) #define vote_log_network_error(response, conn, result) \ sf_log_network_error(response, conn, "fvote", result) #ifdef __cplusplus extern "C" { #endif void fcfs_vote_proto_init(); const char *fcfs_vote_get_cmd_caption(const int cmd); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/common/vote_types.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_VOTE_TYPES_H #define _FCFS_VOTE_TYPES_H #include "fastcommon/common_define.h" #include "sf/sf_types.h" #define FCFS_VOTE_DEFAULT_CLUSTER_PORT 41011 #define FCFS_VOTE_DEFAULT_SERVICE_PORT 41012 #define FCFS_VOTE_SERVICE_ID_FAUTH 'A' #define FCFS_VOTE_SERVICE_ID_FDIR 'D' #define FCFS_VOTE_SERVICE_ID_FSTORE 'S' #ifdef __cplusplus extern "C" { #endif static inline const char *fcfs_vote_get_service_name(const int service_id) { switch (service_id) { case FCFS_VOTE_SERVICE_ID_FAUTH: return "fauth"; case FCFS_VOTE_SERVICE_ID_FDIR: return "fdir"; case FCFS_VOTE_SERVICE_ID_FSTORE: return "fstore"; default: return "unkown"; } } #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/conf/client.conf ================================================ # config the cluster servers cluster_config_filename = cluster.conf ================================================ FILE: src/vote/conf/cluster.conf ================================================ [group-cluster] # the default cluster port port = 41011 [group-service] # the default service port port = 41012 ## Important:server group mark, don't modify this line. # config a server # section format: [server-$id] # server id is a 32 bits natural number (1, 2, 3 etc.), [server-1] # format: host[:port] # host can be an IP address or a hostname, IPv6 is supported # IP address is recommended # can occur more than once host = 172.16.168.128 ================================================ FILE: src/vote/conf/full/client.conf ================================================ # connect timeout in seconds # default value is 10 # Note: in the intranet network (LAN), 10 seconds is enough. connect_timeout = 10 # network timeout in seconds # default value is 60 network_timeout = 60 # config the cluster servers cluster_config_filename = cluster.conf #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level = info ================================================ FILE: src/vote/conf/full/cluster.conf ================================================ [master-election] # the quorum for master election # set quorum to majority to avoid brain-split # value list: ## any: no requirement ## majority: more than half ## auto: set to majority when the number of nodes is odd, ## otherwise set to any # default value is auto quorum = auto # the timeout to determinate master lost # the default value is 3 seconds master_lost_timeout = 3 # the max wait time for master election # this parameter is for the master restart # the default value is 5 seconds max_wait_time = 5 [group-cluster] # the default cluster port port = 41011 [group-service] # the default service port port = 41012 ## Important:server group mark, don't modify this line. # config a server # section format: [server-$id] # server id is a 32 bits natural number (1, 2, 3 etc.), [server-1] # format: host[:port] # host can be an IP address or a hostname, IPv6 is supported # IP address is recommended # can occur more than once host = 172.16.168.128 ================================================ FILE: src/vote/conf/full/server.conf ================================================ # connect timeout in seconds # default value is 10 # Note: in the intranet network (LAN), 10 seconds is enough. # do NOT set to 1 second because of time accuracy! connect_timeout = 10 # network timeout in seconds for send and recv # default value is 60 network_timeout = 60 # the base path to store log files # this path must be exist base_path = /opt/fastcfs/vote # max concurrent connections this server support # you should set this parameter larger, eg. 10240 # default value is 256 max_connections = 10240 # the min network buff size # default value 8KB min_buff_size = 64KB # the max network buff size # default value 128KB max_buff_size = 256KB # max pkg size # default value is 16K max_pkg_size = 256KB # if io_uring send with zero copy when Linux kernel version >= 5.19 # set to true when the NIC support zero copy # can be overwritten in sections: [cluster] and [service] # default value is true use_send_zc = true # TCP quick ack for Linux (setsockopt with TCP_QUICKACK option) # default value is true tcp_quick_ack = true # config the cluster servers and groups cluster_config_filename = cluster.conf # the session config filename session_config_filename = session.conf #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level = info #unix group name to run this program, #not set (empty) means run by the group of current user run_by_group= #unix username to run this program, #not set (empty) means run by current user run_by_user = # thread stack size, should >= 64KB # default value is 256KB thread_stack_size = 256KB # NOTE: following global parameters for error log and slow log # which can be overwritten in [error-log] and [slow-log] sections # sync log buff to disk every interval seconds # default value is 1 seconds sync_log_buff_interval = 1 # if rotate the log file every day # set to true for rotate the log file anyway at the rotate time # default value is true log_file_rotate_everyday = true # the time to rotate the log file, format is Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # valid only when log_file_rotate_everyday is true # default value is 00:00 log_file_rotate_time = 00:00 # if compress the old log file by gzip # default value is false log_file_compress_old = false # compress the log file days before # default value is 1 log_file_compress_days_before = 7 # rotate the log file when the log file exceeds this size # 0 means never rotates log file by log file size # default value is 0 log_file_rotate_on_size = 0 # keep days of the log files # 0 means do not delete the old log files # default value is 15 log_file_keep_days = 15 # the time to delete the old log files, format is Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # valid only when log_file_keep_days > 0 # default value is 01:30 log_file_delete_old_time = 01:30 [error-log] # global log parameters can be overwritten here for error log [slow-log] # global log parameters can be overwritten here for slow log # if enable the slow log # default value is false enabled = true # the filename prefix of the slow log # default value is slow filename_prefix = slow # log the request to the slow log whose response time exceeds this parameter # default value is 100ms log_slower_than_ms = 50 [cluster] # bind an address of this host # empty for bind all addresses of this host bind_addr = # the listen port port = 41011 # the accept thread count # default value is 1 which is recommended accept_threads = 1 # the network thread count # these threads deal network io # dispatched by the incoming socket fd # default value is 4 work_threads = 4 # if io_uring send with zero copy when Linux kernel version >= 5.19 # set to true when the NIC support zero copy # default value set by global config use_send_zc = true [service] bind_addr = port = 41012 accept_threads = 1 work_threads = 4 use_send_zc = true ================================================ FILE: src/vote/conf/server.conf ================================================ # the base path to store log files # this path must be exist base_path = /opt/fastcfs/vote # max concurrent connections this server support # you should set this parameter larger, eg. 10240 # default value is 256 max_connections = 10240 # config the cluster servers and groups cluster_config_filename = cluster.conf [cluster] # the listen port port = 41011 # the network thread count # these threads deal network io # dispatched by the incoming socket fd # default value is 4 work_threads = 4 [service] # the listen port port = 41012 # the network thread count # these threads deal network io # dispatched by the incoming socket fd # default value is 4 work_threads = 4 ================================================ FILE: src/vote/server/Makefile.in ================================================ .SUFFIXES: .c .o .lo COMPILE = $(CC) $(CFLAGS) INC_PATH = -I.. -I../common LIB_PATH = $(LIBS) -lfastcommon -lserverframe TARGET_PATH = $(TARGET_PREFIX)/bin STATIC_OBJS = ../common/vote_global.o ../common/vote_proto.o \ server_global.o server_func.o common_handler.o service_handler.o \ cluster_handler.o cluster_relationship.o cluster_info.o \ service_group_htable.o ALL_PRGS = fcfs_voted all: $(STATIC_OBJS) $(ALL_PRGS) $(ALL_PRGS): $(STATIC_OBJS) .o: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c: $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH) .c.o: $(COMPILE) -c -o $@ $< $(INC_PATH) install: mkdir -p $(TARGET_PATH) cp -f $(ALL_PRGS) $(TARGET_PATH) clean: rm -f $(STATIC_OBJS) $(ALL_PRGS) ================================================ FILE: src/vote/server/cluster_handler.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //cluster_handler.c #include #include #include #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/shared_func.h" #include "fastcommon/pthread_func.h" #include "fastcommon/sched_thread.h" #include "fastcommon/ioevent_loop.h" #include "sf/sf_util.h" #include "sf/sf_func.h" #include "sf/sf_nio.h" #include "sf/sf_global.h" #include "common/vote_proto.h" #include "server_global.h" #include "server_func.h" #include "cluster_info.h" #include "cluster_relationship.h" #include "common_handler.h" #include "cluster_handler.h" int cluster_handler_init() { return 0; } int cluster_handler_destroy() { return 0; } int cluster_recv_timeout_callback(struct fast_task_info *task) { if (SERVER_TASK_TYPE == VOTE_SERVER_TASK_TYPE_RELATIONSHIP && CLUSTER_PEER != NULL) { logWarning("file: "__FILE__", line: %d, " "cluster client ip: %s, server id: %d, recv timeout", __LINE__, task->client_ip, CLUSTER_PEER->server->id); return ETIMEDOUT; } return 0; } void cluster_task_finish_cleanup(struct fast_task_info *task) { switch (SERVER_TASK_TYPE) { case VOTE_SERVER_TASK_TYPE_RELATIONSHIP: if (CLUSTER_PEER != NULL) { CLUSTER_PEER->is_online = false; cluster_relationship_add_to_inactive_sarray(CLUSTER_PEER); CLUSTER_PEER = NULL; } else { logError("file: "__FILE__", line: %d, " "mistake happen! task: %p, SERVER_TASK_TYPE: %d, " "CLUSTER_PEER is NULL", __LINE__, task, SERVER_TASK_TYPE); } SERVER_TASK_TYPE = SF_SERVER_TASK_TYPE_NONE; break; default: break; } sf_task_finish_clean_up(task); } static int cluster_check_config_sign(struct fast_task_info *task, const int server_id, const char *config_sign) { if (memcmp(config_sign, CLUSTER_CONFIG_SIGN_BUF, SF_CLUSTER_CONFIG_SIGN_LEN) != 0) { char peer_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1]; char my_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1]; bin2hex(config_sign, SF_CLUSTER_CONFIG_SIGN_LEN, peer_hex); bin2hex((const char *)CLUSTER_CONFIG_SIGN_BUF, SF_CLUSTER_CONFIG_SIGN_LEN, my_hex); RESPONSE.error.length = sprintf(RESPONSE.error.message, "server #%d 's cluster config md5: %s != mine: %s", server_id, peer_hex, my_hex); return EFAULT; } return 0; } static int cluster_deal_get_server_status(struct fast_task_info *task) { int result; int server_id; FCFSVoteProtoGetServerStatusReq *req; FCFSVoteProtoGetServerStatusResp *resp; if ((result=server_expect_body_length(sizeof( FCFSVoteProtoGetServerStatusReq))) != 0) { return result; } req = (FCFSVoteProtoGetServerStatusReq *)REQUEST.body; server_id = buff2int(req->server_id); if ((result=cluster_check_config_sign(task, server_id, req->config_sign)) != 0) { return result; } resp = (FCFSVoteProtoGetServerStatusResp *)SF_PROTO_SEND_BODY(task); resp->is_master = MYSELF_IS_MASTER; int2buff(CLUSTER_MY_SERVER_ID, resp->server_id); RESPONSE.header.body_len = sizeof(FCFSVoteProtoGetServerStatusResp); RESPONSE.header.cmd = FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_RESP; TASK_CTX.common.response_done = true; return 0; } static int cluster_deal_join_master(struct fast_task_info *task) { int result; int server_id; FCFSVoteProtoJoinMasterReq *req; FCFSVoteClusterServerInfo *peer; if ((result=server_expect_body_length(sizeof( FCFSVoteProtoJoinMasterReq))) != 0) { return result; } req = (FCFSVoteProtoJoinMasterReq *)REQUEST.body; server_id = buff2int(req->server_id); peer = fcfs_vote_get_server_by_id(server_id); if (peer == NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "peer server id: %d not exist", server_id); return ENOENT; } if ((result=cluster_check_config_sign(task, server_id, req->config_sign)) != 0) { return result; } if (CLUSTER_MYSELF_PTR != CLUSTER_MASTER_ATOM_PTR && CLUSTER_MYSELF_PTR != CLUSTER_NEXT_MASTER) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am not master"); return SF_RETRIABLE_ERROR_NOT_MASTER; } if (peer == CLUSTER_MYSELF_PTR) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "can't join self"); return EINVAL; } if (CLUSTER_PEER != NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "peer server id: %d already joined", server_id); return EEXIST; } SERVER_TASK_TYPE = VOTE_SERVER_TASK_TYPE_RELATIONSHIP; CLUSTER_PEER = peer; CLUSTER_PEER->is_online = true; cluster_relationship_remove_from_inactive_sarray(peer); return 0; } static int cluster_deal_ping_master(struct fast_task_info *task) { int result; if ((result=server_expect_body_length(0)) != 0) { return result; } if (CLUSTER_PEER == NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "please join first"); return EINVAL; } if (CLUSTER_MYSELF_PTR != CLUSTER_MASTER_ATOM_PTR && CLUSTER_MYSELF_PTR != CLUSTER_NEXT_MASTER) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am not master"); return SF_RETRIABLE_ERROR_NOT_MASTER; } RESPONSE.header.cmd = FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_RESP; return 0; } static int cluster_deal_next_master(struct fast_task_info *task) { int result; int master_id; FCFSVoteClusterServerInfo *master; if ((result=server_expect_body_length(4)) != 0) { return result; } if (CLUSTER_MYSELF_PTR == CLUSTER_MASTER_ATOM_PTR) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am already master"); cluster_relationship_trigger_reselect_master(); return EEXIST; } master_id = buff2int(REQUEST.body); master = fcfs_vote_get_server_by_id(master_id); if (master == NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "master server id: %d not exist", master_id); return ENOENT; } if (REQUEST.header.cmd == FCFS_VOTE_CLUSTER_PROTO_PRE_SET_NEXT_MASTER) { return cluster_relationship_pre_set_master(master); } else { return cluster_relationship_commit_master(master); } } static int cluster_process(struct fast_task_info *task) { int result; int cmd; cmd = REQUEST.header.cmd; if (!MYSELF_IS_MASTER) { } switch (cmd) { case SF_PROTO_ACTIVE_TEST_REQ: /* if (SERVER_TASK_TYPE == VOTE_SERVER_TASK_TYPE_SUBSCRIBE && !MYSELF_IS_MASTER) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am not master"); return SF_RETRIABLE_ERROR_NOT_MASTER; } */ RESPONSE.header.cmd = SF_PROTO_ACTIVE_TEST_RESP; return sf_proto_deal_active_test(task, &REQUEST, &RESPONSE); case FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_REQ: return cluster_deal_get_server_status(task); case FCFS_VOTE_CLUSTER_PROTO_PRE_SET_NEXT_MASTER: case FCFS_VOTE_CLUSTER_PROTO_COMMIT_NEXT_MASTER: return cluster_deal_next_master(task); case FCFS_VOTE_CLUSTER_PROTO_JOIN_MASTER: return cluster_deal_join_master(task); case FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_REQ: return cluster_deal_ping_master(task); case FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ: if ((result=fcfs_vote_deal_get_master(task, CLUSTER_GROUP_INDEX)) == 0) { RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_GET_MASTER_RESP; } return result; case SF_SERVICE_PROTO_GET_LEADER_REQ: if ((result=fcfs_vote_deal_get_master(task, CLUSTER_GROUP_INDEX)) == 0) { RESPONSE.header.cmd = SF_SERVICE_PROTO_GET_LEADER_RESP; } return result; default: RESPONSE.error.length = sprintf(RESPONSE.error.message, "unkown cluster cmd: %d", REQUEST.header.cmd); return -EINVAL; } } int cluster_deal_task(struct fast_task_info *task, const int stage) { int result; if (stage == SF_NIO_STAGE_CONTINUE) { if (task->continue_callback != NULL) { result = task->continue_callback(task); } else { result = RESPONSE_STATUS; if (result == TASK_STATUS_CONTINUE) { logError("file: "__FILE__", line: %d, " "unexpect status: %d", __LINE__, result); result = EBUSY; } } } else { sf_proto_init_task_context(task, &TASK_CTX.common); result = cluster_process(task); } if (result == TASK_STATUS_CONTINUE) { return 0; } else { RESPONSE_STATUS = result; return sf_proto_deal_task_done(task, "cluster", &TASK_CTX.common); } } ================================================ FILE: src/vote/server/cluster_handler.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //cluster_handler.h #ifndef FCFS_VOTE_CLUSTER_HANDLER_H #define FCFS_VOTE_CLUSTER_HANDLER_H #include #include #include #include "fastcommon/fast_task_queue.h" #include "server_types.h" #ifdef __cplusplus extern "C" { #endif int cluster_handler_init(); int cluster_handler_destroy(); int cluster_deal_task(struct fast_task_info *task, const int stage); void cluster_task_finish_cleanup(struct fast_task_info *task); int cluster_recv_timeout_callback(struct fast_task_info *task); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/server/cluster_info.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fastcommon/logger.h" #include "fastcommon/shared_func.h" #include "fastcommon/sched_thread.h" #include "fastcommon/local_ip_func.h" #include "server_global.h" #include "cluster_info.h" static int init_cluster_server_array() { int bytes; FCFSVoteClusterServerInfo *cs; FCServerInfo *server; FCServerInfo *end; bytes = sizeof(FCFSVoteClusterServerInfo) * FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG); if ((CLUSTER_SERVER_ARRAY.servers=(FCFSVoteClusterServerInfo *) fc_malloc(bytes)) == NULL) { return ENOMEM; } memset(CLUSTER_SERVER_ARRAY.servers, 0, bytes); end = FC_SID_SERVERS(CLUSTER_SERVER_CONFIG) + FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG); for (server=FC_SID_SERVERS(CLUSTER_SERVER_CONFIG), cs=CLUSTER_SERVER_ARRAY.servers; serverserver = server; } CLUSTER_SERVER_ARRAY.count = FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG); return 0; } static int find_myself_in_cluster_config(const char *filename) { const char *local_ip; struct { const char *ip_addr; int port; } found; FCServerInfo *server; FCFSVoteClusterServerInfo *myself; SFNetworkHandler *service_handler; SFNetworkHandler *cluster_handler; char formatted_found_ip[FORMATTED_IP_SIZE]; char formatted_local_ip[FORMATTED_IP_SIZE]; int ports[4]; int count; int i; service_handler = SERVICE_SF_CTX.handlers[SF_IPV4_ADDRESS_FAMILY_INDEX]. handlers + SF_SOCKET_NETWORK_HANDLER_INDEX; cluster_handler = CLUSTER_SF_CTX.handlers[SF_IPV4_ADDRESS_FAMILY_INDEX]. handlers + SF_SOCKET_NETWORK_HANDLER_INDEX; count = 0; ports[count++] = service_handler->inner.port; if (service_handler->outer.port != service_handler->inner.port) { ports[count++] = service_handler->outer.port; } ports[count++] = cluster_handler->inner.port; if (cluster_handler->outer.port != cluster_handler->inner.port) { ports[count++] = cluster_handler->outer.port; } found.ip_addr = NULL; found.port = 0; local_ip = get_first_local_ip(); while (local_ip != NULL) { for (i=0; iserver->id); return EEXIST; } found.ip_addr = local_ip; found.port = ports[i]; } } local_ip = get_next_local_ip(local_ip); } if (CLUSTER_MYSELF_PTR == NULL) { logError("file: "__FILE__", line: %d, " "cluster config file: %s, can't find myself " "by my local ip and listen port", __LINE__, filename); return ENOENT; } return 0; } FCFSVoteClusterServerInfo *fcfs_vote_get_server_by_id(const int server_id) { FCServerInfo *server; server = fc_server_get_by_id(&CLUSTER_SERVER_CONFIG, server_id); if (server == NULL) { return NULL; } return CLUSTER_SERVER_ARRAY.servers + (server - FC_SID_SERVERS(CLUSTER_SERVER_CONFIG)); } int cluster_info_init(const char *cluster_config_filename) { int result; if ((result=init_cluster_server_array()) != 0) { return result; } if ((result=find_myself_in_cluster_config(cluster_config_filename)) != 0) { return result; } return 0; } ================================================ FILE: src/vote/server/cluster_info.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //cluster_info.h #ifndef _CLUSTER_INFO_H_ #define _CLUSTER_INFO_H_ #include "server_global.h" #ifdef __cplusplus extern "C" { #endif int cluster_info_init(const char *cluster_config_filename); FCFSVoteClusterServerInfo *fcfs_vote_get_server_by_id(const int server_id); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/server/cluster_relationship.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/shared_func.h" #include "fastcommon/pthread_func.h" #include "fastcommon/sched_thread.h" #include "sf/sf_configs.h" #include "sf/sf_service.h" #include "sf/sf_func.h" #include "common/vote_proto.h" #include "server_global.h" #include "service_group_htable.h" #include "cluster_relationship.h" typedef struct fcfs_vote_cluster_server_status { FCFSVoteClusterServerInfo *cs; bool is_master; int server_id; } FCFSVoteClusterServerStatus; typedef struct fcfs_vote_cluster_server_detect_entry { FCFSVoteClusterServerInfo *cs; int next_time; } FCFSVoteClusterServerDetectEntry; typedef struct fcfs_vote_cluster_server_detect_array { FCFSVoteClusterServerDetectEntry *entries; int count; int alloc; pthread_mutex_t lock; } FCFSVoteClusterServerDetectArray; typedef struct fcfs_vote_cluster_relationship_context { FCFSVoteClusterServerDetectArray inactive_server_array; } FCFSVoteClusterRelationshipContext; #define INACTIVE_SERVER_ARRAY relationship_ctx.inactive_server_array static FCFSVoteClusterRelationshipContext relationship_ctx = { {NULL, 0, 0} }; #define SET_SERVER_DETECT_ENTRY(entry, server) \ do { \ entry->cs = server; \ entry->next_time = g_current_time + 1; \ } while (0) static int proto_get_server_status(ConnectionInfo *conn, const int network_timeout, FCFSVoteClusterServerStatus *server_status) { int result; FCFSVoteProtoHeader *header; FCFSVoteProtoGetServerStatusReq *req; FCFSVoteProtoGetServerStatusResp *resp; SFResponseInfo response; char out_buff[sizeof(FCFSVoteProtoHeader) + sizeof(FCFSVoteProtoGetServerStatusReq)]; char in_body[sizeof(FCFSVoteProtoGetServerStatusResp)]; char formatted_ip[FORMATTED_IP_SIZE]; header = (FCFSVoteProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_REQ, sizeof(out_buff) - sizeof(FCFSVoteProtoHeader)); req = (FCFSVoteProtoGetServerStatusReq *)(out_buff + sizeof(FCFSVoteProtoHeader)); int2buff(CLUSTER_MY_SERVER_ID, req->server_id); memcpy(req->config_sign, CLUSTER_CONFIG_SIGN_BUF, SF_CLUSTER_CONFIG_SIGN_LEN); response.error.length = 0; if ((result=sf_send_and_check_response_header(conn, out_buff, sizeof(out_buff), &response, network_timeout, FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_RESP)) != 0) { vote_log_network_error(&response, conn, result); return result; } if (response.header.body_len != sizeof(FCFSVoteProtoGetServerStatusResp)) { format_ip_address(conn->ip_addr, formatted_ip); logError("file: "__FILE__", line: %d, " "server %s:%u, recv body length: %d != %d", __LINE__, formatted_ip, conn->port, response.header.body_len, (int) sizeof(FCFSVoteProtoGetServerStatusResp)); return EINVAL; } if ((result=tcprecvdata_nb(conn->sock, in_body, response. header.body_len, network_timeout)) != 0) { format_ip_address(conn->ip_addr, formatted_ip); logError("file: "__FILE__", line: %d, " "recv from server %s:%u fail, " "errno: %d, error info: %s", __LINE__, formatted_ip, conn->port, result, STRERROR(result)); return result; } resp = (FCFSVoteProtoGetServerStatusResp *)in_body; server_status->is_master = resp->is_master; server_status->server_id = buff2int(resp->server_id); return 0; } static void init_inactive_server_array() { FCFSVoteClusterServerInfo *cs; FCFSVoteClusterServerInfo *end; FCFSVoteClusterServerDetectEntry *entry; PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock); entry = INACTIVE_SERVER_ARRAY.entries; end = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count; for (cs=CLUSTER_SERVER_ARRAY.servers; csserver_id); memcpy(req->config_sign, CLUSTER_CONFIG_SIGN_BUF, SF_CLUSTER_CONFIG_SIGN_LEN); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, out_buff, sizeof(out_buff), &response, network_timeout, SF_PROTO_ACK)) != 0) { vote_log_network_error(&response, conn, result); } return result; } static int proto_ping_master(ConnectionInfo *conn, const int network_timeout) { FCFSVoteProtoHeader header; SFResponseInfo response; int result; SF_PROTO_SET_HEADER(&header, FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_REQ, 0); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(conn, (char *)&header, sizeof(header), &response, network_timeout, FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_RESP)) != 0) { vote_log_network_error(&response, conn, result); } return result; } static int cluster_cmp_server_status(const void *p1, const void *p2) { FCFSVoteClusterServerStatus *status1; FCFSVoteClusterServerStatus *status2; int sub; status1 = (FCFSVoteClusterServerStatus *)p1; status2 = (FCFSVoteClusterServerStatus *)p2; sub = (int)status1->is_master - (int)status2->is_master; if (sub != 0) { return sub; } return (int)status1->server_id - (int)status2->server_id; } #define cluster_get_server_status(server_status) \ cluster_get_server_status_ex(server_status, true) static int cluster_get_server_status_ex(FCFSVoteClusterServerStatus *server_status, const bool log_connect_error) { const int connect_timeout = 2; const int network_timeout = 2; ConnectionInfo conn; int result; if (server_status->cs == CLUSTER_MYSELF_PTR) { server_status->is_master = MYSELF_IS_MASTER; server_status->server_id = CLUSTER_MY_SERVER_ID; return 0; } else { if ((result=fc_server_make_connection_ex(&CLUSTER_GROUP_ADDRESS_ARRAY( server_status->cs->server), &conn, "fvote", connect_timeout, NULL, log_connect_error)) != 0) { return result; } result = proto_get_server_status(&conn, network_timeout, server_status); conn_pool_disconnect_server(&conn); return result; } } static int do_check_brainsplit(FCFSVoteClusterServerInfo *cs) { int result; const bool log_connect_error = false; FCFSVoteClusterServerStatus server_status; char formatted_ip[FORMATTED_IP_SIZE]; server_status.cs = cs; server_status.is_master = false; server_status.server_id = 0; if ((result=cluster_get_server_status_ex(&server_status, log_connect_error)) != 0) { return result; } if (server_status.is_master) { format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( cs->server), formatted_ip); logWarning("file: "__FILE__", line: %d, " "two masters occurs, anonther master id: %d, %s:%u, " "trigger re-select master ...", __LINE__, cs->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(cs->server)); cluster_relationship_trigger_reselect_master(); return EEXIST; } return 0; } static int cluster_check_brainsplit(int *inactive_count) { FCFSVoteClusterServerDetectEntry *entry; FCFSVoteClusterServerDetectEntry *end; int result; end = INACTIVE_SERVER_ARRAY.entries + *inactive_count; for (entry=INACTIVE_SERVER_ARRAY.entries; entry= INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count) { break; } if (entry->next_time > g_current_time) { continue; } result = do_check_brainsplit(entry->cs); if (result == 0) { //success --(*inactive_count); } else if (result == EEXIST) { //brain-split occurs return result; } entry->next_time = g_current_time + 1; } return 0; } static int master_check() { int result; int active_count; int inactive_count; PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock); inactive_count = INACTIVE_SERVER_ARRAY.count; PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock); if (inactive_count > 0) { if ((result=cluster_check_brainsplit(&inactive_count)) != 0) { return result; } active_count = CLUSTER_SERVER_ARRAY.count - inactive_count; if (!sf_election_quorum_check(MASTER_ELECTION_QUORUM, false, CLUSTER_SERVER_ARRAY.count, active_count)) { logWarning("file: "__FILE__", line: %d, " "trigger re-select master because alive server " "count: %d < half of total server count: %d ...", __LINE__, active_count, CLUSTER_SERVER_ARRAY.count); cluster_relationship_trigger_reselect_master(); return EBUSY; } } return 0; } static int cluster_get_master(FCFSVoteClusterServerStatus *server_status, const bool log_connect_error, int *active_count) { #define STATUS_ARRAY_FIXED_COUNT 8 FCFSVoteClusterServerInfo *server; FCFSVoteClusterServerInfo *end; FCFSVoteClusterServerStatus *current_status; FCFSVoteClusterServerStatus *cs_status; FCFSVoteClusterServerStatus status_array[STATUS_ARRAY_FIXED_COUNT]; char formatted_ip[FORMATTED_IP_SIZE]; int result; int r; int i; memset(server_status, 0, sizeof(FCFSVoteClusterServerStatus)); if (CLUSTER_SERVER_ARRAY.count < STATUS_ARRAY_FIXED_COUNT) { cs_status = status_array; } else { int bytes; bytes = sizeof(FCFSVoteClusterServerStatus) * CLUSTER_SERVER_ARRAY.count; cs_status = (FCFSVoteClusterServerStatus *)fc_malloc(bytes); if (cs_status == NULL) { return ENOMEM; } } current_status = cs_status; result = 0; end = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count; for (server=CLUSTER_SERVER_ARRAY.servers; servercs = server; r = cluster_get_server_status_ex(current_status, log_connect_error); if (r == 0) { current_status++; } else if (r != ENOENT) { result = r; } } *active_count = current_status - cs_status; if (*active_count == 0) { logError("file: "__FILE__", line: %d, " "get server status fail, " "server count: %d", __LINE__, CLUSTER_SERVER_ARRAY.count); return result == 0 ? ENOENT : result; } qsort(cs_status, *active_count, sizeof(FCFSVoteClusterServerStatus), cluster_cmp_server_status); if (FC_LOG_BY_LEVEL(LOG_DEBUG)) { for (i=0; i<*active_count; i++) { format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( cs_status[i].cs->server), formatted_ip); logDebug("file: "__FILE__", line: %d, " "server_id: %d, ip and port %s:%u, is_master: %d", __LINE__, cs_status[i].server_id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(cs_status[i].cs->server), cs_status[i].is_master); } } memcpy(server_status, cs_status + (*active_count - 1), sizeof(FCFSVoteClusterServerStatus)); if (cs_status != status_array) { free(cs_status); } return 0; } static int do_notify_master_changed(FCFSVoteClusterServerInfo *cs, FCFSVoteClusterServerInfo *master, const unsigned char cmd, bool *bConnectFail) { int connect_timeout; char out_buff[sizeof(FCFSVoteProtoHeader) + 4]; ConnectionInfo conn; FCFSVoteProtoHeader *header; SFResponseInfo response; int result; connect_timeout = FC_MIN(CLUSTER_CONNECT_TIMEOUT, 2); if ((result=fc_server_make_connection(&CLUSTER_GROUP_ADDRESS_ARRAY( cs->server), &conn, "fvote", connect_timeout)) != 0) { *bConnectFail = true; return result; } *bConnectFail = false; header = (FCFSVoteProtoHeader *)out_buff; SF_PROTO_SET_HEADER(header, cmd, sizeof(out_buff) - sizeof(FCFSVoteProtoHeader)); int2buff(master->server->id, out_buff + sizeof(FCFSVoteProtoHeader)); response.error.length = 0; if ((result=sf_send_and_recv_none_body_response(&conn, out_buff, sizeof(out_buff), &response, CLUSTER_NETWORK_TIMEOUT, SF_PROTO_ACK)) != 0) { vote_log_network_error(&response, &conn, result); } conn_pool_disconnect_server(&conn); return result; } int cluster_relationship_pre_set_master(FCFSVoteClusterServerInfo *master) { FCFSVoteClusterServerInfo *next_master; next_master = CLUSTER_NEXT_MASTER; if (next_master == NULL) { CLUSTER_NEXT_MASTER = master; } else if (next_master != master) { logError("file: "__FILE__", line: %d, " "try to set next master id: %d, " "but next master: %d already exist", __LINE__, master->server->id, next_master->server->id); CLUSTER_NEXT_MASTER = NULL; return EEXIST; } return 0; } static int cluster_relationship_set_master(FCFSVoteClusterServerInfo *new_master, const time_t start_time) { FCFSVoteClusterServerInfo *old_master; char formatted_ip[FORMATTED_IP_SIZE]; old_master = CLUSTER_MASTER_ATOM_PTR; if (new_master == old_master) { format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( new_master->server), formatted_ip); logDebug("file: "__FILE__", line: %d, " "the server id: %d, %s:%u already is master", __LINE__, new_master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(new_master->server)); return 0; } if (CLUSTER_MYSELF_PTR == new_master) { } else { char time_used[128]; if (start_time > 0) { sprintf(time_used, ", election time used: %ds", (int)(g_current_time - start_time)); } else { *time_used = '\0'; } format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( new_master->server), formatted_ip); logInfo("file: "__FILE__", line: %d, " "the master server id: %d, %s:%u%s", __LINE__, new_master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(new_master->server), time_used); } do { if (__sync_bool_compare_and_swap(&CLUSTER_MASTER_PTR, old_master, new_master)) { break; } old_master = CLUSTER_MASTER_ATOM_PTR; } while (old_master != new_master); return 0; } int cluster_relationship_commit_master(FCFSVoteClusterServerInfo *master) { const time_t start_time = 0; FCFSVoteClusterServerInfo *next_master; int result; next_master = CLUSTER_NEXT_MASTER; if (next_master == NULL) { logError("file: "__FILE__", line: %d, " "next master is NULL", __LINE__); return EBUSY; } if (next_master != master) { logError("file: "__FILE__", line: %d, " "next master server id: %d != expected server id: %d", __LINE__, next_master->server->id, master->server->id); CLUSTER_NEXT_MASTER = NULL; return EBUSY; } result = cluster_relationship_set_master(master, start_time); CLUSTER_NEXT_MASTER = NULL; return result; } void cluster_relationship_trigger_reselect_master() { FCFSVoteClusterServerInfo *master; master = CLUSTER_MASTER_ATOM_PTR; if (CLUSTER_MYSELF_PTR != master) { return; } if (cluster_unset_master(master)) { service_group_htable_clear_tasks(); } } static int cluster_notify_next_master(FCFSVoteClusterServerInfo *cs, FCFSVoteClusterServerStatus *server_status, bool *bConnectFail) { FCFSVoteClusterServerInfo *master; int result; master = server_status->cs; if (cs == CLUSTER_MYSELF_PTR) { if ((result=cluster_relationship_pre_set_master(master)) == 0) { init_inactive_server_array(); } return result; } else { return do_notify_master_changed(cs, master, FCFS_VOTE_CLUSTER_PROTO_PRE_SET_NEXT_MASTER, bConnectFail); } } static int cluster_commit_next_master(FCFSVoteClusterServerInfo *cs, FCFSVoteClusterServerStatus *server_status, bool *bConnectFail) { FCFSVoteClusterServerInfo *master; master = server_status->cs; if (cs == CLUSTER_MYSELF_PTR) { return cluster_relationship_commit_master(master); } else { return do_notify_master_changed(cs, master, FCFS_VOTE_CLUSTER_PROTO_COMMIT_NEXT_MASTER, bConnectFail); } } typedef int (*cluster_notify_next_master_func)(FCFSVoteClusterServerInfo *cs, FCFSVoteClusterServerStatus *server_status, bool *bConnectFail); static int notify_next_master(cluster_notify_next_master_func notify_func, FCFSVoteClusterServerStatus *server_status) { FCFSVoteClusterServerInfo *server; FCFSVoteClusterServerInfo *send; int result; bool bConnectFail; int success_count; result = ENOENT; success_count = 0; send = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count; for (server=CLUSTER_SERVER_ARRAY.servers; server 1) { need_log = true; last_log_time = g_current_time; } if (g_current_time - last_log_time > 8) { need_log = ((i + 1) % 10 == 0); if (need_log) { last_log_time = g_current_time; } } else { need_log = false; } if ((result=cluster_get_master(&server_status, need_log, &active_count)) != 0) { return result; } ++i; if (!sf_election_quorum_check(MASTER_ELECTION_QUORUM, false, CLUSTER_SERVER_ARRAY.count, active_count)) { sleep_secs = 1; if (need_log) { logWarning("file: "__FILE__", line: %d, " "round %dth select master fail because alive server " "count: %d < half of total server count: %d, " "try again after %d seconds.", __LINE__, i, active_count, CLUSTER_SERVER_ARRAY.count, sleep_secs); } sleep(sleep_secs); continue; } if ((active_count == CLUSTER_SERVER_ARRAY.count) || (active_count >= 2 && server_status.is_master)) { break; } remain_time = ELECTION_MAX_WAIT_TIME - (g_current_time - start_time); if (remain_time <= 0) { break; } sleep_secs = FC_MIN(remain_time, max_sleep_secs); logWarning("file: "__FILE__", line: %d, " "round %dth select master, alive server count: %d " "< server count: %d, try again after %d seconds.", __LINE__, i, active_count, CLUSTER_SERVER_ARRAY.count, sleep_secs); sleep(sleep_secs); if ((i % 2 == 0) && (max_sleep_secs < 8)) { max_sleep_secs *= 2; } } next_master = CLUSTER_MASTER_ATOM_PTR; if (next_master != NULL) { format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( next_master->server), formatted_ip); logInfo("file: "__FILE__", line: %d, " "abort election because the master exists, " "master id: %d, %s:%u, election time used: %ds", __LINE__, next_master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(next_master->server), (int)(g_current_time - start_time)); return 0; } next_master = server_status.cs; if (CLUSTER_MYSELF_PTR == next_master) { if ((result=cluster_notify_master_changed( &server_status)) != 0) { return result; } format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( next_master->server), formatted_ip); logInfo("file: "__FILE__", line: %d, " "I am the new master, id: %d, %s:%u, election time used: " "%ds", __LINE__, next_master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(next_master->server), (int)(g_current_time - start_time)); } else { if (server_status.is_master) { cluster_relationship_set_master(next_master, start_time); } else if (CLUSTER_MASTER_ATOM_PTR == NULL) { format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( next_master->server), formatted_ip); logInfo("file: "__FILE__", line: %d, " "election time used: %ds, waiting for the candidate " "master server id: %d, %s:%u notify ...", __LINE__, (int)(g_current_time - start_time), next_master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT( next_master->server)); return ENOENT; } } return 0; } static int cluster_ping_master(FCFSVoteClusterServerInfo *master, ConnectionInfo *conn, const int timeout, bool *is_ping) { int connect_timeout; int network_timeout; int result; if (CLUSTER_MYSELF_PTR == master) { *is_ping = false; return master_check(); } network_timeout = FC_MIN(CLUSTER_NETWORK_TIMEOUT, timeout); *is_ping = true; if (conn->sock < 0) { connect_timeout = FC_MIN(CLUSTER_CONNECT_TIMEOUT, timeout); if ((result=fc_server_make_connection(&CLUSTER_GROUP_ADDRESS_ARRAY( master->server), conn, "fvote", connect_timeout)) != 0) { return result; } if ((result=proto_join_master(conn, network_timeout)) != 0) { conn_pool_disconnect_server(conn); return result; } } if ((result=proto_ping_master(conn, network_timeout)) != 0) { conn_pool_disconnect_server(conn); } return result; } static void *cluster_thread_entrance(void* arg) { #define MAX_SLEEP_SECONDS 10 int result; int fail_count; int sleep_seconds; int ping_remain_time; time_t ping_start_time; bool is_ping; FCFSVoteClusterServerInfo *master; ConnectionInfo mconn; //master connection char formatted_ip[FORMATTED_IP_SIZE]; #ifdef OS_LINUX prctl(PR_SET_NAME, "relationship"); #endif memset(&mconn, 0, sizeof(mconn)); mconn.sock = -1; fail_count = 0; sleep_seconds = 1; ping_start_time = g_current_time; while (SF_G_CONTINUE_FLAG) { master = CLUSTER_MASTER_ATOM_PTR; if (master == NULL) { if (cluster_select_master() != 0) { sleep_seconds = 1 + (int)((double)rand() * (double)MAX_SLEEP_SECONDS / RAND_MAX); } else { if (mconn.sock >= 0) { conn_pool_disconnect_server(&mconn); } ping_start_time = g_current_time; sleep_seconds = 1; } } else { ping_remain_time = ELECTION_MASTER_LOST_TIMEOUT - (g_current_time - ping_start_time); if (ping_remain_time < 2) { ping_remain_time = 2; } if ((result=cluster_ping_master(master, &mconn, ping_remain_time, &is_ping)) == 0) { fail_count = 0; ping_start_time = g_current_time; sleep_seconds = 1; } else if (is_ping) { ++fail_count; format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP( master->server), formatted_ip); logError("file: "__FILE__", line: %d, " "%dth ping master id: %d, %s:%u fail", __LINE__, fail_count, master->server->id, formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(master->server)); if (result == SF_RETRIABLE_ERROR_NOT_MASTER) { cluster_unset_master(master); fail_count = 0; sleep_seconds = 0; } else if (g_current_time - ping_start_time > ELECTION_MASTER_LOST_TIMEOUT) { if (fail_count > 1) { cluster_unset_master(master); fail_count = 0; } sleep_seconds = 0; } else { sleep_seconds = 1; } } else { sleep_seconds = 1; } } if (sleep_seconds > 0) { sleep(sleep_seconds); } } return NULL; } int cluster_relationship_init() { int result; int bytes; pthread_t tid; bytes = sizeof(FCFSVoteClusterServerDetectEntry) * CLUSTER_SERVER_ARRAY.count; INACTIVE_SERVER_ARRAY.entries = (FCFSVoteClusterServerDetectEntry *) fc_malloc(bytes); if (INACTIVE_SERVER_ARRAY.entries == NULL) { return ENOMEM; } if ((result=init_pthread_lock(&INACTIVE_SERVER_ARRAY.lock)) != 0) { logError("file: "__FILE__", line: %d, " "init_pthread_lock fail, errno: %d, error info: %s", __LINE__, result, STRERROR(result)); return result; } INACTIVE_SERVER_ARRAY.alloc = CLUSTER_SERVER_ARRAY.count; return fc_create_thread(&tid, cluster_thread_entrance, NULL, SF_G_THREAD_STACK_SIZE); } int cluster_relationship_destroy() { return 0; } void cluster_relationship_add_to_inactive_sarray(FCFSVoteClusterServerInfo *cs) { FCFSVoteClusterServerDetectEntry *entry; FCFSVoteClusterServerDetectEntry *end; bool found; found = false; PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock); if (INACTIVE_SERVER_ARRAY.count > 0) { end = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count; for (entry=INACTIVE_SERVER_ARRAY.entries; entrycs == cs) { found = true; break; } } } if (!found) { if (INACTIVE_SERVER_ARRAY.count < INACTIVE_SERVER_ARRAY.alloc) { entry = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count; SET_SERVER_DETECT_ENTRY(entry, cs); INACTIVE_SERVER_ARRAY.count++; } else { logError("file: "__FILE__", line: %d, " "server id: %d, add to inactive array fail " "because array is full", __LINE__, cs->server->id); } } else { logWarning("file: "__FILE__", line: %d, " "server id: %d, already in inactive array!", __LINE__, cs->server->id); } PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock); } void cluster_relationship_remove_from_inactive_sarray(FCFSVoteClusterServerInfo *cs) { FCFSVoteClusterServerDetectEntry *entry; FCFSVoteClusterServerDetectEntry *p; FCFSVoteClusterServerDetectEntry *end; PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock); end = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count; for (entry=INACTIVE_SERVER_ARRAY.entries; entrycs == cs) { break; } } if (entry < end) { for (p=entry+1; p * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //cluster_relationship.h #ifndef _CLUSTER_RELATIONSHIP_H_ #define _CLUSTER_RELATIONSHIP_H_ #include #include #include "server_types.h" #ifdef __cplusplus extern "C" { #endif int cluster_relationship_init(); int cluster_relationship_destroy(); int cluster_relationship_pre_set_master(FCFSVoteClusterServerInfo *master); int cluster_relationship_commit_master(FCFSVoteClusterServerInfo *master); void cluster_relationship_trigger_reselect_master(); void cluster_relationship_add_to_inactive_sarray(FCFSVoteClusterServerInfo *cs); void cluster_relationship_remove_from_inactive_sarray(FCFSVoteClusterServerInfo *cs); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/server/common_handler.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //common_handler.c #include #include #include #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/shared_func.h" #include "fastcommon/sched_thread.h" #include "common/vote_proto.h" #include "server_global.h" #include "common_handler.h" #define LOG_LEVEL_FOR_DEBUG LOG_DEBUG static int fcfs_vote_get_cmd_log_level(const int cmd) { switch (cmd) { case SF_PROTO_ACTIVE_TEST_REQ: case SF_PROTO_ACTIVE_TEST_RESP: return LOG_NOTHING; case SF_SERVICE_PROTO_REPORT_REQ_RECEIPT_REQ: return LOG_DEBUG; default: return LOG_LEVEL_FOR_DEBUG; } } void common_handler_init() { SFHandlerContext handler_ctx; fcfs_vote_proto_init(); handler_ctx.slow_log = &SLOW_LOG; handler_ctx.callbacks.get_cmd_caption = fcfs_vote_get_cmd_caption; if (FC_LOG_BY_LEVEL(LOG_LEVEL_FOR_DEBUG)) { handler_ctx.callbacks.get_cmd_log_level = fcfs_vote_get_cmd_log_level; } else { handler_ctx.callbacks.get_cmd_log_level = NULL; } sf_proto_set_handler_context(&handler_ctx); } int fcfs_vote_deal_get_master(struct fast_task_info *task, const int group_index) { int result; FCFSVoteProtoGetServerResp *resp; FCFSVoteClusterServerInfo *master; const FCAddressInfo *addr; if ((result=server_expect_body_length(0)) != 0) { return result; } master = (CLUSTER_MASTER_ATOM_PTR != NULL) ? CLUSTER_MASTER_ATOM_PTR : CLUSTER_NEXT_MASTER; if (master == NULL) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "the master NOT exist"); return SF_RETRIABLE_ERROR_NO_SERVER; } resp = (FCFSVoteProtoGetServerResp *)SF_PROTO_SEND_BODY(task); if (group_index == SERVICE_GROUP_INDEX) { addr = fc_server_get_address_by_peer(&SERVICE_GROUP_ADDRESS_ARRAY( master->server), task->client_ip); } else { addr = fc_server_get_address_by_peer(&CLUSTER_GROUP_ADDRESS_ARRAY( master->server), task->client_ip); } int2buff(master->server->id, resp->server_id); fc_safe_strcpy(resp->ip_addr, addr->conn.ip_addr); short2buff(addr->conn.port, resp->port); RESPONSE.header.body_len = sizeof(FCFSVoteProtoGetServerResp); TASK_CTX.common.response_done = true; return 0; } ================================================ FILE: src/vote/server/common_handler.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //common_handler.h #ifndef FCFS_VOTE_COMMON_HANDLER_H #define FCFS_VOTE_COMMON_HANDLER_H #include #include #include #include "server_types.h" #ifdef __cplusplus extern "C" { #endif void common_handler_init(); int fcfs_vote_deal_get_master(struct fast_task_info *task, const int group_index); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/server/fcfs_voted.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fastcommon/shared_func.h" #include "fastcommon/pthread_func.h" #include "fastcommon/process_ctrl.h" #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/sched_thread.h" #include "sf/sf_global.h" #include "sf/sf_func.h" #include "sf/sf_nio.h" #include "sf/sf_service.h" #include "sf/sf_util.h" #include "common/vote_proto.h" #include "server_global.h" #include "server_func.h" #include "service_group_htable.h" #include "cluster_relationship.h" #include "common_handler.h" #include "cluster_handler.h" #include "service_handler.h" static int setup_server_env(const char *config_filename); static bool daemon_mode = true; static const char *config_filename; static char g_pid_filename[MAX_PATH_SIZE]; static int process_cmdline(int argc, char *argv[], bool *continue_flag) { char *action; bool stop; int result; *continue_flag = false; if (argc < 2) { sf_usage(argv[0]); return 1; } config_filename = sf_parse_daemon_mode_and_action(argc, argv, &g_fcfs_vote_global_vars.version, &daemon_mode, &action); if (config_filename == NULL) { return 0; } log_init2(); //log_set_time_precision(&g_log_context, LOG_TIME_PRECISION_USECOND); result = sf_get_base_path_from_conf_file(config_filename); if (result != 0) { log_destroy(); return result; } fc_get_full_filename(SF_G_BASE_PATH_STR, SF_G_BASE_PATH_LEN, "voted.pid", sizeof("voted.pid") - 1, g_pid_filename); stop = false; result = process_action(g_pid_filename, action, &stop); if (result != 0) { if (result == EINVAL) { sf_usage(argv[0]); } log_destroy(); return result; } if (stop) { log_destroy(); return 0; } *continue_flag = true; return 0; } int main(int argc, char *argv[]) { pthread_t schedule_tid; int wait_count; int result; result = process_cmdline(argc, argv, (bool *)&SF_G_CONTINUE_FLAG); if (!SF_G_CONTINUE_FLAG) { return result; } sf_enable_exit_on_oom(); srand(time(NULL)); fast_mblock_manager_init(); //sched_set_delay_params(300, 1024); do { if ((result=setup_server_env(config_filename)) != 0) { break; } if ((result=sf_startup_schedule(&schedule_tid)) != 0) { break; } if ((result=sf_add_slow_log_schedule(&g_server_global_vars. slow_log)) != 0) { break; } if ((result=sf_socket_server()) != 0) { break; } if ((result=sf_socket_server_ex(&CLUSTER_SF_CTX)) != 0) { break; } if ((result=write_to_pid_file(g_pid_filename)) != 0) { break; } if ((result=service_group_htable_init()) != 0) { break; } if ((result=service_handler_init()) != 0) { break; } common_handler_init(); //sched_print_all_entries(); result = sf_service_init_ex(&CLUSTER_SF_CTX, "cluster", NULL, NULL, NULL, sf_proto_set_body_length, NULL, cluster_deal_task, cluster_task_finish_cleanup, cluster_recv_timeout_callback, 1000, sizeof(FCFSVoteProtoHeader), sizeof(VoteServerTaskArg)); if (result != 0) { break; } sf_enable_thread_notify_ex(&CLUSTER_SF_CTX, true); sf_accept_loop_ex(&CLUSTER_SF_CTX, false); result = sf_service_init_ex(&SERVICE_SF_CTX, "service", NULL, NULL, NULL, sf_proto_set_body_length, NULL, service_deal_task, service_task_finish_cleanup, service_recv_timeout_callback, 1000, sizeof(FCFSVoteProtoHeader), sizeof(VoteServerTaskArg)); if (result != 0) { break; } sf_enable_thread_notify(true); if ((result=cluster_relationship_init()) != 0) { break; } } while (0); if (result != 0) { lcrit("program exit abnomally"); log_destroy(); return result; } //sched_print_all_entries(); sf_accept_loop(); if (g_schedule_flag) { pthread_kill(schedule_tid, SIGINT); } wait_count = 0; while ((SF_G_ALIVE_THREAD_COUNT != 0) || g_schedule_flag) { fc_sleep_ms(10); if (++wait_count > 1000) { lwarning("waiting timeout, exit!"); break; } } sf_service_destroy(); delete_pid_file(g_pid_filename); logInfo("file: "__FILE__", line: %d, " "program exit normally.\n", __LINE__); log_destroy(); return 0; } static int setup_server_env(const char *config_filename) { int result; sf_set_current_time(); if ((result=server_load_config(config_filename)) != 0) { return result; } if (daemon_mode) { daemon_init(false); } umask(0); result = sf_setup_signal_handler(); log_set_cache(true); return result; } ================================================ FILE: src/vote/server/server_func.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include "fastcommon/ini_file_reader.h" #include "fastcommon/shared_func.h" #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/local_ip_func.h" #include "sf/sf_global.h" #include "sf/sf_configs.h" #include "sf/sf_service.h" #include "sf/sf_cluster_cfg.h" #include "common/vote_proto.h" #include "server_global.h" #include "cluster_info.h" #include "server_func.h" static void log_cluster_server_config() { FastBuffer buffer; if (fast_buffer_init1(&buffer, 1024) != 0) { return; } fc_server_to_config_string(&CLUSTER_SERVER_CONFIG, &buffer); log_it1(LOG_INFO, buffer.data, buffer.length); fast_buffer_destroy(&buffer); fc_server_to_log(&CLUSTER_SERVER_CONFIG); } static void server_log_configs() { char sz_server_config[512]; char sz_global_config[512]; char sz_slowlog_config[256]; char sz_service_config[256]; sf_global_config_to_string(sz_global_config, sizeof(sz_global_config)); sf_slow_log_config_to_string(&SLOW_LOG_CFG, "slow-log", sz_slowlog_config, sizeof(sz_slowlog_config)); sf_context_config_to_string(&SERVICE_SF_CTX, sz_service_config, sizeof(sz_service_config)); snprintf(sz_server_config, sizeof(sz_server_config), "master-election {quorum: %s, master_lost_timeout: %ds, " "max_wait_time: %ds}", sf_get_election_quorum_caption( MASTER_ELECTION_QUORUM), ELECTION_MASTER_LOST_TIMEOUT, ELECTION_MAX_WAIT_TIME); logInfo("FCFSVote V%d.%d.%d, %s, %s, service: {%s}, %s", g_fcfs_vote_global_vars.version.major, g_fcfs_vote_global_vars.version.minor, g_fcfs_vote_global_vars.version.patch, sz_global_config, sz_slowlog_config, sz_service_config, sz_server_config); log_local_host_ip_addrs(); log_cluster_server_config(); } static int load_master_election_config(const char *cluster_filename) { IniContext ini_context; IniFullContext ini_ctx; int result; if ((result=iniLoadFromFile(cluster_filename, &ini_context)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, cluster_filename, result); return result; } FAST_INI_SET_FULL_CTX_EX(ini_ctx, cluster_filename, "master-election", &ini_context); ELECTION_MASTER_LOST_TIMEOUT = iniGetIntCorrectValue( &ini_ctx, "master_lost_timeout", 3, 1, 30); ELECTION_MAX_WAIT_TIME = iniGetIntCorrectValue( &ini_ctx, "max_wait_time", 5, 1, 300); if ((result=sf_load_election_quorum_config(&MASTER_ELECTION_QUORUM, &ini_ctx)) != 0) { return result; } iniFreeContext(&ini_context); return 0; } static int load_cluster_config(IniFullContext *ini_ctx, char *full_cluster_filename) { int result; if ((result=sf_load_cluster_config_ex(&CLUSTER_CONFIG, ini_ctx, FCFS_VOTE_DEFAULT_CLUSTER_PORT, full_cluster_filename, PATH_MAX)) != 0) { return result; } if ((result=load_master_election_config(full_cluster_filename)) != 0) { return result; } if ((result=cluster_info_init(full_cluster_filename)) != 0) { return result; } sf_set_address_family_by_ip(&SERVICE_SF_CTX, &SERVICE_GROUP_ADDRESS_ARRAY( CLUSTER_MYSELF_PTR->server)); sf_set_address_family_by_ip(&CLUSTER_SF_CTX, &CLUSTER_GROUP_ADDRESS_ARRAY( CLUSTER_MYSELF_PTR->server)); return 0; } int server_load_config(const char *filename) { const int fixed_buffer_size = 0; const int task_buffer_extra_size = 0; IniContext ini_context; IniFullContext ini_ctx; char full_cluster_filename[PATH_MAX]; int result; if ((result=iniLoadFromFile(filename, &ini_context)) != 0) { logError("file: "__FILE__", line: %d, " "load conf file \"%s\" fail, ret code: %d", __LINE__, filename, result); return result; } if ((result=sf_load_config("fcfs_voted", fc_comm_type_sock, filename, &ini_context, "service", FCFS_VOTE_DEFAULT_SERVICE_PORT, FCFS_VOTE_DEFAULT_SERVICE_PORT, fixed_buffer_size, task_buffer_extra_size)) != 0) { return result; } if ((result=sf_load_context_from_config(&CLUSTER_SF_CTX, fc_comm_type_sock, filename, &ini_context, "cluster", FCFS_VOTE_DEFAULT_CLUSTER_PORT, FCFS_VOTE_DEFAULT_CLUSTER_PORT, fixed_buffer_size, task_buffer_extra_size)) != 0) { return result; } FAST_INI_SET_FULL_CTX_EX(ini_ctx, filename, NULL, &ini_context); if ((result=load_cluster_config(&ini_ctx, full_cluster_filename)) != 0) { return result; } if ((result=sf_load_slow_log_config(filename, &ini_context, &SLOW_LOG_CTX, &SLOW_LOG_CFG)) != 0) { return result; } iniFreeContext(&ini_context); load_local_host_ip_addrs(); server_log_configs(); return 0; } ================================================ FILE: src/vote/server/server_func.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _FCFS_VOTE_SERVER_FUNC_H #define _FCFS_VOTE_SERVER_FUNC_H #include "server_types.h" #ifdef __cplusplus extern "C" { #endif int server_load_config(const char *filename); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/server/server_global.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "server_global.h" VoteServerGlobalVars g_server_global_vars; ================================================ FILE: src/vote/server/server_global.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _SERVER_GLOBAL_H #define _SERVER_GLOBAL_H #include "fastcommon/common_define.h" #include "sf/sf_global.h" #include "../common/vote_global.h" #include "server_types.h" typedef struct server_global_vars { struct { FCFSVoteClusterServerInfo *master; FCFSVoteClusterServerInfo *myself; FCFSVoteClusterServerInfo *next_master; SFClusterConfig config; FCFSVoteClusterServerArray server_array; struct { SFElectionQuorum quorum; bool vote_node_enabled; int master_lost_timeout; int max_wait_time; } master_election; SFContext sf_context; //for cluster communication } cluster; SFSlowLogContext slow_log; } VoteServerGlobalVars; #define MASTER_ELECTION_QUORUM g_server_global_vars.cluster. \ master_election.quorum #define ELECTION_MASTER_LOST_TIMEOUT g_server_global_vars.cluster. \ master_election.master_lost_timeout #define ELECTION_MAX_WAIT_TIME g_server_global_vars.cluster. \ master_election.max_wait_time #define CLUSTER_CONFIG g_server_global_vars.cluster.config #define CLUSTER_SERVER_CONFIG CLUSTER_CONFIG.server_cfg #define CLUSTER_NEXT_MASTER g_server_global_vars.cluster.next_master #define CLUSTER_MYSELF_PTR g_server_global_vars.cluster.myself #define CLUSTER_MASTER_PTR g_server_global_vars.cluster.master #define CLUSTER_MASTER_ATOM_PTR ((FCFSVoteClusterServerInfo *) \ __sync_add_and_fetch(&CLUSTER_MASTER_PTR, 0)) #define MYSELF_IS_MASTER (CLUSTER_MASTER_ATOM_PTR == CLUSTER_MYSELF_PTR) #define CLUSTER_SERVER_ARRAY g_server_global_vars.cluster.server_array #define CLUSTER_MY_SERVER_ID CLUSTER_MYSELF_PTR->server->id #define SERVICE_SF_CTX g_sf_context #define CLUSTER_SF_CTX g_server_global_vars.cluster.sf_context #define CLUSTER_CONNECT_TIMEOUT CLUSTER_SF_CTX.net_buffer_cfg.connect_timeout #define CLUSTER_NETWORK_TIMEOUT CLUSTER_SF_CTX.net_buffer_cfg.network_timeout #define SLOW_LOG g_server_global_vars.slow_log #define SLOW_LOG_CFG SLOW_LOG.cfg #define SLOW_LOG_CTX SLOW_LOG.ctx #define CLUSTER_GROUP_INDEX g_server_global_vars.cluster.config.cluster_group_index #define SERVICE_GROUP_INDEX g_server_global_vars.cluster.config.service_group_index #define CLUSTER_GROUP_ADDRESS_ARRAY(server) \ (server)->group_addrs[CLUSTER_GROUP_INDEX].address_array #define SERVICE_GROUP_ADDRESS_ARRAY(server) \ (server)->group_addrs[SERVICE_GROUP_INDEX].address_array #define CLUSTER_GROUP_ADDRESS_FIRST_PTR(server) \ (*(server)->group_addrs[CLUSTER_GROUP_INDEX].address_array.addrs) #define SERVICE_GROUP_ADDRESS_FIRST_PTR(server) \ (*(server)->group_addrs[SERVICE_GROUP_INDEX].address_array.addrs) #define CLUSTER_GROUP_ADDRESS_FIRST_IP(server) \ CLUSTER_GROUP_ADDRESS_FIRST_PTR(server)->conn.ip_addr #define CLUSTER_GROUP_ADDRESS_FIRST_PORT(server) \ CLUSTER_GROUP_ADDRESS_FIRST_PTR(server)->conn.port #define SERVICE_GROUP_ADDRESS_FIRST_IP(server) \ SERVICE_GROUP_ADDRESS_FIRST_PTR(server)->conn.ip_addr #define SERVICE_GROUP_ADDRESS_FIRST_PORT(server) \ SERVICE_GROUP_ADDRESS_FIRST_PTR(server)->conn.port #define CLUSTER_CONFIG_SIGN_BUF g_server_global_vars.cluster.config.md5_digest #ifdef __cplusplus extern "C" { #endif extern VoteServerGlobalVars g_server_global_vars; #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/server/server_types.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef _SERVER_TYPES_H #define _SERVER_TYPES_H #include "fastcommon/uniq_skiplist.h" #include "fastcommon/fc_queue.h" #include "fastcommon/fast_mblock.h" #include "fastcommon/locked_list.h" #include "sf/sf_types.h" #include "common/vote_types.h" #define TASK_STATUS_CONTINUE 12345 #define TASK_ARG ((VoteServerTaskArg *)task->arg) #define TASK_CTX TASK_ARG->context #define REQUEST TASK_CTX.common.request #define RESPONSE TASK_CTX.common.response #define RESPONSE_STATUS RESPONSE.header.status #define REQUEST_STATUS REQUEST.header.status #define SERVER_TASK_TYPE TASK_CTX.task_type #define CLUSTER_PEER TASK_CTX.shared.cluster.peer #define SERVICE_PEER TASK_CTX.shared.service.peer #define VOTE_SERVER_TASK_TYPE_NONE 0 #define VOTE_SERVER_TASK_TYPE_VOTE_NODE 1 #define VOTE_SERVER_TASK_TYPE_RELATIONSHIP 2 #define SERVER_CTX ((VoteServerContext *)task->thread_data->arg) typedef struct fcfs_vote_cluster_server_info { FCServerInfo *server; volatile bool is_online; } FCFSVoteClusterServerInfo; typedef struct fcfs_vote_cluster_server_array { FCFSVoteClusterServerInfo *servers; int count; } FCFSVoteClusterServerArray; typedef struct fcfs_vote_service_peer_info { struct fcfs_vote_service_group_info *group; int server_id; bool persistent; } FCFSVoteServicePeerInfo; typedef struct server_task_arg { struct { SFCommonTaskContext common; int task_type; union { struct { FCFSVoteServicePeerInfo peer; } service; union { FCFSVoteClusterServerInfo *peer; //the peer server in the cluster } cluster; } shared; } context; } VoteServerTaskArg; #endif ================================================ FILE: src/vote/server/service_group_htable.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fastcommon/logger.h" #include "fastcommon/shared_func.h" #include "fastcommon/fc_atomic.h" #include "sf/sf_nio.h" #include "server_global.h" #include "service_group_htable.h" typedef struct fcfs_vote_shared_lock_array { pthread_mutex_t *locks; int count; } FCFSVoteSharedLockArray; typedef struct fcfs_vote_service_group_htable { FCFSVoteServiceGroupInfo **buckets; int capacity; } FCFSVoteServiceGroupHtable; typedef struct fcfs_vote_service_group_context { struct fast_mblock_man allocator; //element: FCFSVoteServiceGroupInfo FCFSVoteSharedLockArray lock_array; FCFSVoteServiceGroupHtable htable; } FCFSVoteServiceGroupContext; static FCFSVoteServiceGroupContext service_group_ctx; int service_group_htable_init() { int result; int bytes; pthread_mutex_t *lock; pthread_mutex_t *end; if ((result=fast_mblock_init_ex1(&service_group_ctx.allocator, "service_group", sizeof(FCFSVoteServiceGroupInfo), 4 * 1024, 0, NULL, NULL, true)) != 0) { return result; } service_group_ctx.lock_array.count = 17; bytes = sizeof(pthread_mutex_t) * service_group_ctx.lock_array.count; service_group_ctx.lock_array.locks = fc_malloc(bytes); if (service_group_ctx.lock_array.locks == NULL) { return ENOMEM; } end = service_group_ctx.lock_array.locks + service_group_ctx.lock_array.count; for (lock=service_group_ctx.lock_array.locks; lockhash_code) { break; } current = current->next; } if (current == NULL) { current = fast_mblock_alloc_object(&service_group_ctx.allocator); if (current != NULL) { current->hash_code = hash_code; current->service_id = service_id; current->response_size = response_size; current->group_id = group_id; current->leader_id = leader_id; current->task = (leader_id > 0 ? task : NULL); current->next = *bucket; *bucket = current; result = 0; } else { result = ENOMEM; } } else { if (leader_id > 0) { old_leader_id = FC_ATOMIC_GET(current->leader_id); if (old_leader_id == 0) { if (__sync_bool_compare_and_swap(¤t->leader_id, old_leader_id, leader_id)) { result = 0; } else { result = SF_CLUSTER_ERROR_LEADER_INCONSISTENT; } } else if (old_leader_id == leader_id) { result = 0; } else { result = SF_CLUSTER_ERROR_LEADER_INCONSISTENT; } if (result == 0 && task != NULL && task != current->task) { if (current->task != NULL) { logWarning("file: "__FILE__", line: %d, " "network task already exist, clean it!", __LINE__); sf_nio_notify(current->task, SF_NIO_STAGE_CLOSE); } current->task = task; } } else { result = 0; } } PTHREAD_MUTEX_UNLOCK(lock); *group = current; return result; } void service_group_htable_unset_task(FCFSVoteServiceGroupInfo *group) { int bucket_index; pthread_mutex_t *lock; bucket_index = group->hash_code % service_group_ctx.htable.capacity; lock = service_group_ctx.lock_array.locks + bucket_index % service_group_ctx.lock_array.count; PTHREAD_MUTEX_LOCK(lock); group->task = NULL; PTHREAD_MUTEX_UNLOCK(lock); } void service_group_htable_clear_tasks() { FCFSVoteServiceGroupInfo **bucket; FCFSVoteServiceGroupInfo **end; FCFSVoteServiceGroupInfo *current; int bucket_index; int group_count; int clear_count; pthread_mutex_t *lock; group_count = clear_count = 0; end = service_group_ctx.htable.buckets + service_group_ctx.htable.capacity; for (bucket=service_group_ctx.htable.buckets; buckettask != NULL) { ++clear_count; sf_nio_notify(current->task, SF_NIO_STAGE_CLOSE); } } while ((current=current->next) != NULL); } PTHREAD_MUTEX_UNLOCK(lock); } logInfo("file: "__FILE__", line: %d, " "service group count: %d, clear count: %d", __LINE__, group_count, clear_count); } ================================================ FILE: src/vote/server/service_group_htable.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //service_group_htable.h #ifndef _SERVICE_GROUP_HTABLE_H_ #define _SERVICE_GROUP_HTABLE_H_ #include #include "server_global.h" typedef struct fcfs_vote_service_group_info { uint64_t hash_code; short service_id; short response_size; int group_id; volatile int next_leader; volatile int leader_id; struct fast_task_info *task; struct fcfs_vote_service_group_info *next; //for htable } FCFSVoteServiceGroupInfo; #ifdef __cplusplus extern "C" { #endif int service_group_htable_init(); int service_group_htable_get(const short service_id, const int group_id, const int leader_id, const short response_size, struct fast_task_info *task, FCFSVoteServiceGroupInfo **group); void service_group_htable_unset_task(FCFSVoteServiceGroupInfo *group); void service_group_htable_clear_tasks(); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/vote/server/service_handler.c ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //service_handler.c #include #include #include #include #include #include #include #include #include #include #include #include "fastcommon/logger.h" #include "fastcommon/sockopt.h" #include "fastcommon/shared_func.h" #include "fastcommon/pthread_func.h" #include "fastcommon/sched_thread.h" #include "fastcommon/ioevent_loop.h" #include "sf/sf_util.h" #include "sf/sf_func.h" #include "sf/sf_nio.h" #include "sf/sf_service.h" #include "sf/sf_global.h" #include "common/vote_proto.h" #include "server_global.h" #include "server_func.h" #include "service_group_htable.h" #include "common_handler.h" #include "service_handler.h" int service_handler_init() { return 0; } int service_handler_destroy() { return 0; } int service_recv_timeout_callback(struct fast_task_info *task) { if (SERVER_TASK_TYPE == VOTE_SERVER_TASK_TYPE_VOTE_NODE && SERVICE_PEER.group != NULL) { logWarning("file: "__FILE__", line: %d, " "client ip: %s, service: %s, group_id: %d, server id: %d, " "recv timeout", __LINE__, task->client_ip, fcfs_vote_get_service_name(SERVICE_PEER.group->service_id), SERVICE_PEER.group->group_id, SERVICE_PEER.server_id); return ETIMEDOUT; } return 0; } void service_task_finish_cleanup(struct fast_task_info *task) { if (SERVER_TASK_TYPE == VOTE_SERVER_TASK_TYPE_VOTE_NODE) { if (__sync_bool_compare_and_swap(&SERVICE_PEER.group-> leader_id, SERVICE_PEER.server_id, 0)) { /* logInfo("service: %s, group_id: %d, server id: %d, " "persistent: %d, next_leader: %d, offline", fcfs_vote_get_service_name(SERVICE_PEER.group->service_id), SERVICE_PEER.group->group_id, SERVICE_PEER.server_id, SERVICE_PEER.persistent, FC_ATOMIC_GET(SERVICE_PEER.group->next_leader)); */ if (SERVICE_PEER.persistent) { service_group_htable_unset_task(SERVICE_PEER.group); SERVICE_PEER.persistent = false; } } SERVICE_PEER.server_id = 0; SERVICE_PEER.group = NULL; SERVER_TASK_TYPE = VOTE_SERVER_TASK_TYPE_NONE; } sf_task_finish_clean_up(task); } static int service_deal_cluster_stat(struct fast_task_info *task) { int result; FCFSVoteProtoClusterStatRespBodyHeader *body_header; FCFSVoteProtoClusterStatRespBodyPart *body_part; FCFSVoteClusterServerInfo *cs; FCFSVoteClusterServerInfo *send; if ((result=server_expect_body_length(0)) != 0) { return result; } body_header = (FCFSVoteProtoClusterStatRespBodyHeader *) SF_PROTO_SEND_BODY(task); body_part = (FCFSVoteProtoClusterStatRespBodyPart *)(body_header + 1); int2buff(CLUSTER_SERVER_ARRAY.count, body_header->count); send = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count; for (cs=CLUSTER_SERVER_ARRAY.servers; csserver->id, body_part->server_id); body_part->is_online = ((cs == CLUSTER_MASTER_ATOM_PTR || cs->is_online) ? 1 : 0); body_part->is_master = (cs == CLUSTER_MASTER_ATOM_PTR ? 1 : 0); fc_safe_strcpy(body_part->ip_addr, SERVICE_GROUP_ADDRESS_FIRST_IP( cs->server)); short2buff(SERVICE_GROUP_ADDRESS_FIRST_PORT(cs->server), body_part->port); } RESPONSE.header.body_len = (char *)body_part - SF_PROTO_SEND_BODY(task); RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_RESP; TASK_CTX.common.response_done = true; return 0; } static int service_check_config_sign(struct fast_task_info *task, const int service_id, const int server_id, const char *config_sign) { if (memcmp(config_sign, CLUSTER_CONFIG_SIGN_BUF, SF_CLUSTER_CONFIG_SIGN_LEN) != 0) { char peer_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1]; char my_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1]; bin2hex(config_sign, SF_CLUSTER_CONFIG_SIGN_LEN, peer_hex); bin2hex((const char *)CLUSTER_CONFIG_SIGN_BUF, SF_CLUSTER_CONFIG_SIGN_LEN, my_hex); RESPONSE.error.length = sprintf(RESPONSE.error.message, "service: %s, server_id: %d, client 's " "cluster config md5: %s != mine: %s", fcfs_vote_get_service_name(service_id), server_id, peer_hex, my_hex); return EFAULT; } return 0; } static int service_deal_client_join(struct fast_task_info *task) { int result; int server_id; int service_id; int group_id; int response_size; FCFSVoteProtoClientJoinReq *req; FCFSVoteServiceGroupInfo *group; if ((result=server_expect_body_length(sizeof( FCFSVoteProtoClientJoinReq))) != 0) { return result; } req = (FCFSVoteProtoClientJoinReq *)REQUEST.body; server_id = buff2int(req->server_id); group_id = buff2int(req->group_id); response_size = buff2short(req->response_size); service_id = req->service_id; switch (service_id) { case FCFS_VOTE_SERVICE_ID_FAUTH: case FCFS_VOTE_SERVICE_ID_FDIR: case FCFS_VOTE_SERVICE_ID_FSTORE: break; default: RESPONSE.error.length = sprintf(RESPONSE.error.message, "server_id: %d, unkown service id: %d", server_id, service_id); return -EINVAL; } if (server_id <= 0) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "service: %s, invalid server_id: %d", fcfs_vote_get_service_name(service_id), server_id); return -EINVAL; } if (response_size <= 0 || response_size > (task->send.ptr->size - sizeof(FCFSVoteProtoHeader))) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "service: %s, server_id: %d, invalid response_size: %d", fcfs_vote_get_service_name(service_id), server_id, response_size); return -EINVAL; } if (req->persistent && !req->is_leader) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "service: %s, server_id: %d, unexpected is_leader: %d", fcfs_vote_get_service_name(service_id), server_id, req->is_leader); return -EINVAL; } if ((result=service_check_config_sign(task, service_id, server_id, req->config_sign)) != 0) { return result; } if (SERVICE_PEER.group != NULL) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "service: %s, server_id: %d already joined", fcfs_vote_get_service_name(service_id), server_id); return EEXIST; } /* logInfo("service: %s, group_id: %d, server_id: %d, is_leader: %d, " "persistent: %d", fcfs_vote_get_service_name(service_id), group_id, server_id, req->is_leader, req->persistent); */ result = service_group_htable_get(service_id, group_id, (req->persistent && req->is_leader ? server_id : 0), response_size, (req->persistent ? task : NULL), &group); if (result != 0) { if (result == SF_CLUSTER_ERROR_LEADER_INCONSISTENT) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "service: %s, group_id: %d, target server_id: %d, " "current leader id: %d, leader inconsistent", fcfs_vote_get_service_name(service_id), group_id, server_id, group->leader_id); } return result; } SERVICE_PEER.server_id = server_id; SERVICE_PEER.group = group; SERVICE_PEER.persistent = (req->persistent ? true : false); SERVER_TASK_TYPE = VOTE_SERVER_TASK_TYPE_VOTE_NODE; RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_RESP; return 0; } static inline int service_check_login(struct fast_task_info *task) { if (!(SERVER_TASK_TYPE == VOTE_SERVER_TASK_TYPE_VOTE_NODE && SERVICE_PEER.group != NULL)) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "please login first"); return EPERM; } return 0; } static int service_deal_get_vote(struct fast_task_info *task) { int result; if ((result=server_expect_body_length(sizeof( SFProtoGetServerStatusReq))) != 0) { return result; } if ((result=service_check_login(task)) != 0) { return result; } memset(SF_PROTO_SEND_BODY(task), 0, SERVICE_PEER.group->response_size); RESPONSE.header.body_len = SERVICE_PEER.group->response_size; RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_GET_VOTE_RESP; TASK_CTX.common.response_done = true; return 0; } static int service_deal_active_check(struct fast_task_info *task) { int result; if ((result=server_expect_body_length(0)) != 0) { return result; } if ((result=service_check_login(task)) != 0) { return result; } if (FC_ATOMIC_GET(SERVICE_PEER.group->leader_id) != SERVICE_PEER.server_id) { RESPONSE.error.length = sprintf(RESPONSE.error.message, "service: %s, leader id: %d != peer server id: %d", fcfs_vote_get_service_name(SERVICE_PEER.group->service_id), FC_ATOMIC_GET(SERVICE_PEER.group->leader_id), SERVICE_PEER.server_id); return SF_CLUSTER_ERROR_LEADER_INCONSISTENT; } RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_RESP; return 0; } static int service_deal_next_leader(struct fast_task_info *task) { int result; int next_leader; int old_leader_id; int leader_id; if ((result=server_expect_body_length(0)) != 0) { return result; } if ((result=service_check_login(task)) != 0) { return result; } leader_id = SERVICE_PEER.server_id; old_leader_id = FC_ATOMIC_GET(SERVICE_PEER.group->leader_id); if (old_leader_id != 0) { if (old_leader_id == leader_id) { return 0; } else { RESPONSE.error.length = sprintf(RESPONSE.error.message, "service: %s, leader id: %d != peer server id: %d", fcfs_vote_get_service_name(SERVICE_PEER.group-> service_id), old_leader_id, leader_id); return SF_CLUSTER_ERROR_LEADER_INCONSISTENT; } } next_leader = FC_ATOMIC_GET(SERVICE_PEER.group->next_leader); if (REQUEST.header.cmd == FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER) { if (next_leader == 0) { if (__sync_bool_compare_and_swap(&SERVICE_PEER.group-> next_leader, 0, leader_id)) { return 0; } } else if (next_leader == leader_id) { return 0; } else { __sync_bool_compare_and_swap(&SERVICE_PEER.group-> next_leader, next_leader, 0); } } else { //commit leader __sync_bool_compare_and_swap(&SERVICE_PEER.group-> next_leader, next_leader, 0); if (next_leader != leader_id) { //error } else if (__sync_bool_compare_and_swap(&SERVICE_PEER. group->leader_id, 0, leader_id)) { return 0; } else if (FC_ATOMIC_GET(SERVICE_PEER.group-> leader_id) == leader_id) { return 0; } } RESPONSE.error.length = sprintf(RESPONSE.error.message, "service: %s, next leader id: %d != peer server id: %d", fcfs_vote_get_service_name(SERVICE_PEER.group-> service_id), next_leader, leader_id); return SF_CLUSTER_ERROR_LEADER_INCONSISTENT; } static int service_process(struct fast_task_info *task) { int result; if (!MYSELF_IS_MASTER) { if (!(REQUEST.header.cmd == FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ || REQUEST.header.cmd == SF_SERVICE_PROTO_GET_LEADER_REQ)) { RESPONSE.error.length = sprintf( RESPONSE.error.message, "i am not master"); return SF_RETRIABLE_ERROR_NOT_MASTER; } } switch (REQUEST.header.cmd) { case SF_PROTO_ACTIVE_TEST_REQ: RESPONSE.header.cmd = SF_PROTO_ACTIVE_TEST_RESP; return sf_proto_deal_active_test(task, &REQUEST, &RESPONSE); case FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_REQ: return service_deal_cluster_stat(task); case FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ: if ((result=fcfs_vote_deal_get_master(task, SERVICE_GROUP_INDEX)) == 0) { RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_GET_MASTER_RESP; } return result; case SF_SERVICE_PROTO_GET_LEADER_REQ: if ((result=fcfs_vote_deal_get_master(task, SERVICE_GROUP_INDEX)) == 0) { RESPONSE.header.cmd = SF_SERVICE_PROTO_GET_LEADER_RESP; } return result; case FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_REQ: return service_deal_client_join(task); case FCFS_VOTE_SERVICE_PROTO_GET_VOTE_REQ: return service_deal_get_vote(task); case FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER: case FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER: return service_deal_next_leader(task); case FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_REQ: return service_deal_active_check(task); default: RESPONSE.error.length = sprintf(RESPONSE.error.message, "unkown service cmd: %d", REQUEST.header.cmd); return -EINVAL; } } int service_deal_task(struct fast_task_info *task, const int stage) { int result; /* logInfo("file: "__FILE__", line: %d, " "task: %p, sock: %d, nio stage: %d, continue: %d, " "cmd: %d (%s)", __LINE__, task, task->event.fd, stage, stage == SF_NIO_STAGE_CONTINUE, ((FCFSVoteProtoHeader *)task->recv.ptr->data)->cmd, fdir_get_cmd_caption(((FCFSVoteProtoHeader *) task->recv.ptr->data)->cmd)); */ if (stage == SF_NIO_STAGE_CONTINUE) { if (task->continue_callback != NULL) { result = task->continue_callback(task); } else { result = RESPONSE_STATUS; if (result == TASK_STATUS_CONTINUE) { logError("file: "__FILE__", line: %d, " "unexpect status: %d", __LINE__, result); result = EBUSY; } } } else { sf_proto_init_task_context(task, &TASK_CTX.common); result = service_process(task); } if (result == TASK_STATUS_CONTINUE) { return 0; } else { RESPONSE_STATUS = result; return sf_proto_deal_task_done(task, "service", &TASK_CTX.common); } } ================================================ FILE: src/vote/server/service_handler.h ================================================ /* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //service_handler.h #ifndef FCFS_VOTE_SERVER_HANDLER_H #define FCFS_VOTE_SERVER_HANDLER_H #include #include #include #include "fastcommon/fast_task_queue.h" #include "server_types.h" #ifdef __cplusplus extern "C" { #endif int service_handler_init(); int service_handler_destroy(); int service_deal_task(struct fast_task_info *task, const int stage); void service_task_finish_cleanup(struct fast_task_info *task); int service_recv_timeout_callback(struct fast_task_info *task); #ifdef __cplusplus } #endif #endif ================================================ FILE: systemd/fastauth.service ================================================ [Unit] Description=fastcfs auth service After=network-online.target [Service] Type=forking PIDFile=/opt/fastcfs/auth/authd.pid ExecStart=/usr/bin/fcfs_authd /etc/fastcfs/auth/server.conf start ExecStartPost=/bin/sleep 0.1 ExecStop=/usr/bin/fcfs_authd /etc/fastcfs/auth/server.conf stop # start/stop timeout TimeoutSec=0 # Disable OOM kill by Linux kernel OOMScoreAdjust=-1000 [Install] WantedBy=multi-user.target ================================================ FILE: systemd/fastcfs.service ================================================ [Unit] Description=fastcfs service After=network-online.target [Service] Type=forking PIDFile=/opt/fastcfs/fcfs/fused.pid ExecStart=/usr/bin/fcfs_fused /etc/fastcfs/fcfs/fuse.conf start ExecStartPost=/bin/sleep 0.1 ExecStop=/usr/bin/fcfs_fused /etc/fastcfs/fcfs/fuse.conf stop # start/stop timeout TimeoutSec=60 # Disable OOM kill by Linux kernel OOMScoreAdjust=-1000 [Install] WantedBy=multi-user.target ================================================ FILE: systemd/fastvote.service ================================================ [Unit] Description=fastcfs vote service After=network-online.target [Service] Type=forking PIDFile=/opt/fastcfs/vote/voted.pid ExecStart=/usr/bin/fcfs_voted /etc/fastcfs/vote/server.conf start ExecStartPost=/bin/sleep 0.1 ExecStop=/usr/bin/fcfs_voted /etc/fastcfs/vote/server.conf stop # start/stop timeout TimeoutSec=0 # Disable OOM kill by Linux kernel OOMScoreAdjust=-1000 [Install] WantedBy=multi-user.target