[
  {
    "path": ".gitignore",
    "content": "# Makefile.in\nsrc/api/Makefile\nsrc/api/tests/Makefile\nsrc/preload/Makefile\nsrc/fuse/Makefile\nsrc/tools/Makefile\nsrc/auth/server/Makefile\nsrc/auth/client/Makefile\nsrc/auth/client/tools/Makefile\nsrc/vote/server/Makefile\nsrc/vote/client/Makefile\nsrc/vote/client/tools/Makefile\nsrc/java/jni/Makefile\n\n# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n*.class\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dSYM\n*.dll\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.out\nsrc/api/tests/fcfs_test_file_op\nsrc/api/tests/fcfs_test_file_copy\nsrc/api/tests/fcfs_test_papi_copy\nsrc/api/tests/fcfs_test_read_ahead\nsrc/api/tests/fcfs_beachmark\nsrc/fuse/fcfs_fused\nsrc/tools/fcfs_active_test\nsrc/tools/fcfs_pool_stat\nsrc/auth/server/fcfs_authd\nsrc/auth/client/tools/fcfs_user\nsrc/auth/client/tools/fcfs_pool\nsrc/auth/client/tools/fauth_list_servers\nsrc/auth/client/tools/fauth_cluster_stat\nsrc/vote/server/fcfs_voted\nsrc/vote/client/tools/fvote_cluster_stat\n\n# other\nlogs\ndata\n*.pid\n*.swp\n*.swo\n\n# Build tmp path after execute fastcfs.sh\nbuild\n\n.DS_Store\n.fcfs\n.idea\n"
  },
  {
    "path": "FastCFS-auth.spec.in",
    "content": "%define FastCFSAuthClient    FastCFS-auth-client\n%define FastCFSAuthDevel     FastCFS-auth-devel\n%define FastCFSAuthConfig    FastCFS-auth-config\n%define CommitVersion %(echo $COMMIT_VERSION)\n\nName: FastCFS-auth\nVersion: 5.5.0\nRelease: 1%{?dist}\nSummary: the auth client library and config files of FastCFS. FastCFS is a high performance cloud native distributed file system for databases, KVM and K8s\nLicense: AGPL v3.0\nGroup: Arch/Tech\nURL:  http://github.com/happyfish100/FastCFS/\nSource: http://github.com/happyfish100/FastCFS/%{name}-%{version}.tar.gz\n\nBuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) \n\nBuildRequires: libserverframe-devel >= 1.2.10\nRequires: %__cp %__mv %__chmod %__grep %__mkdir %__install %__id\nRequires: libserverframe >= 1.2.10\nRequires: FastCFS-auth-server = %{version}-%{release}\nRequires: %{FastCFSAuthClient} = %{version}-%{release}\nRequires: %{FastCFSAuthConfig} >= 2.0.0\n\n%description\nthe auth client library and config files of FastCFS.\nFastCFS is a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms.\ncommit version: %{CommitVersion}\n\n%package -n %{FastCFSAuthDevel}\nRequires: %{FastCFSAuthClient} = %{version}-%{release}\nSummary: header files of FastCFS auth client\n\n%package -n %{FastCFSAuthClient}\nRequires: libserverframe >= 1.2.10\nSummary: FastCFS auth client\n\n%package -n %{FastCFSAuthConfig}\nSummary: FastCFS auth config files for sample\n\n%description -n %{FastCFSAuthDevel}\nThis package provides the header files of libfcfsauthclient\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSAuthClient}\nFastCFS auth client\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSAuthConfig}\nFastCFS auth config files for sample\ncommit version: %{CommitVersion}\n\n\n%prep\n%setup -q\n\n%build\n./make.sh --module=authclient clean && ./make.sh --module=authclient\n\n%install\nrm -rf %{buildroot}\nDESTDIR=$RPM_BUILD_ROOT ./make.sh --module=authclient install\nAUTH_CONFDIR=%{buildroot}/etc/fastcfs/auth/\nmkdir -p $AUTH_CONFDIR\ncp src/auth/conf/*.conf $AUTH_CONFDIR\ncp -R src/auth/conf/keys $AUTH_CONFDIR\n\n%post\n\n%preun\n\n%postun\n\n%clean\nrm -rf %{buildroot}\n\n%files\n\n%files -n %{FastCFSAuthClient}\n/usr/lib64/libfcfsauthclient.so*\n/usr/bin/fcfs_user\n/usr/bin/fcfs_pool\n/usr/bin/fauth_list_servers\n/usr/bin/fauth_cluster_stat\n\n%files -n %{FastCFSAuthDevel}\n%defattr(-,root,root,-)\n/usr/include/fastcfs/auth/*\n\n%files -n %{FastCFSAuthConfig}\n%defattr(-,root,root,-)\n%config(noreplace) /etc/fastcfs/auth/*.conf\n%config(noreplace) /etc/fastcfs/auth/keys/*\n\n%changelog\n* Thu Apr 22 2021 YuQing <384681@qq.com>\n- first RPM release (1.0)\n"
  },
  {
    "path": "FastCFS-vote.spec.in",
    "content": "%define FastCFSVoteClient    FastCFS-vote-client\n%define FastCFSVoteDevel     FastCFS-vote-devel\n%define FastCFSVoteConfig    FastCFS-vote-config\n%define CommitVersion %(echo $COMMIT_VERSION)\n\nName: FastCFS-vote\nVersion: 5.5.0\nRelease: 1%{?dist}\nSummary: the vote client library and config files of FastCFS. FastCFS is a high performance cloud native distributed file system for databases, KVM and K8s\nLicense: AGPL v3.0\nGroup: Arch/Tech\nURL:  http://github.com/happyfish100/FastCFS/\nSource: http://github.com/happyfish100/FastCFS/%{name}-%{version}.tar.gz\n\nBuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) \n\nBuildRequires: libserverframe-devel >= 1.2.10\nRequires: %__cp %__mv %__chmod %__grep %__mkdir %__install %__id\nRequires: libserverframe >= 1.2.10\nRequires: FastCFS-vote-server = %{version}-%{release}\nRequires: %{FastCFSVoteClient} = %{version}-%{release}\nRequires: %{FastCFSVoteConfig} >= 3.5.0\n\n%description\nthe vote client library and config files of FastCFS.\nFastCFS is a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms.\ncommit version: %{CommitVersion}\n\n%package -n %{FastCFSVoteDevel}\nRequires: %{FastCFSVoteClient} = %{version}-%{release}\nSummary: header files of FastCFS vote client\n\n%package -n %{FastCFSVoteClient}\nRequires: libserverframe >= 1.2.10\nSummary: FastCFS vote client\n\n%package -n %{FastCFSVoteConfig}\nSummary: FastCFS vote config files for sample\n\n%description -n %{FastCFSVoteDevel}\nThis package provides the header files of libfcfsvoteclient\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSVoteClient}\nFastCFS vote client\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSVoteConfig}\nFastCFS vote config files for sample\ncommit version: %{CommitVersion}\n\n\n%prep\n%setup -q\n\n%build\n./make.sh --module=voteclient clean && ./make.sh --module=voteclient\n\n%install\nrm -rf %{buildroot}\nDESTDIR=$RPM_BUILD_ROOT ./make.sh --module=voteclient install\nVOTE_CONFDIR=%{buildroot}/etc/fastcfs/vote/\nmkdir -p $VOTE_CONFDIR\ncp src/vote/conf/*.conf $VOTE_CONFDIR\n\n%post\n\n%preun\n\n%postun\n\n%clean\nrm -rf %{buildroot}\n\n%files\n\n%files -n %{FastCFSVoteClient}\n/usr/lib64/libfcfsvoteclient.so*\n/usr/bin/fvote_cluster_stat\n\n%files -n %{FastCFSVoteDevel}\n%defattr(-,root,root,-)\n/usr/include/fastcfs/vote/*\n\n%files -n %{FastCFSVoteConfig}\n%defattr(-,root,root,-)\n%config(noreplace) /etc/fastcfs/vote/*.conf\n\n%changelog\n* Thu Apr 22 2021 YuQing <384681@qq.com>\n- first RPM release (1.0)\n"
  },
  {
    "path": "FastCFS.spec.in",
    "content": "%define FastCFSFused        FastCFS-fused\n%define FastCFSUtils        FastCFS-utils\n%define FastCFSAPI          FastCFS-api-libs\n%define FastCFSAPITests     FastCFS-api-tests\n%define FastCFSAuthServer   FastCFS-auth-server\n%define FastCFSVoteServer   FastCFS-vote-server\n%define FastCFSAPIDevel     FastCFS-api-devel\n%define FastCFSFuseConfig   FastCFS-fuse-config\n%define CommitVersion %(echo $COMMIT_VERSION)\n\nName: FastCFS\nVersion: 5.5.0\nRelease: 1%{?dist}\nSummary: a high performance cloud native distributed file system for databases, KVM and K8s\nLicense: AGPL v3.0\nGroup: Arch/Tech\nURL:  http://github.com/happyfish100/FastCFS/\nSource: http://github.com/happyfish100/FastCFS/%{name}-%{version}.tar.gz\n\nBuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)\n\nBuildRequires: fastDIR-devel >= 5.5.0\nBuildRequires: faststore-devel >= 5.5.0\nBuildRequires: fuse3-devel >= 3.16.2\nRequires: %__cp %__mv %__chmod %__grep %__mkdir %__install %__id\nRequires: %{FastCFSFused} = %{version}-%{release}\nRequires: %{FastCFSUtils} = %{version}-%{release}\nRequires: %{FastCFSAPITests} = %{version}-%{release}\n\n%description\na high performance distributed file system which can be used as the back-end storage of databases and cloud platforms.\ncommit version: %{CommitVersion}\n\n%package -n %{FastCFSFused}\nRequires: fuse3 >= 3.16.2\nRequires: %{FastCFSAPI} = %{version}-%{release}\nRequires: %{FastCFSFuseConfig} >= 1.0.0\nSummary: FastCFS fuse\n\n%package -n %{FastCFSUtils}\nRequires: %{FastCFSAPI} = %{version}-%{release}\nSummary: FastCFS utils \n\n%package -n %{FastCFSAPI}\nRequires: fastDIR-client >= 5.5.0\nRequires: faststore-client >= 5.5.0\nSummary: FastCFS api library\n\n%package -n %{FastCFSAPITests}\nRequires: %{FastCFSAPI} = %{version}-%{release}\nRequires: %{FastCFSFuseConfig} >= 1.0.0\nSummary: FastCFS api tests\n\n%package -n %{FastCFSAPIDevel}\nRequires: %{FastCFSAPI} = %{version}-%{release}\nSummary: header files of FastCFS api library\n\n%package -n %{FastCFSAuthServer}\nRequires: fastDIR-client >= 5.5.0\nRequires: FastCFS-auth-config >= 2.0.0\nSummary: FastCFS auth server\n\n%package -n %{FastCFSVoteServer}\nRequires: libserverframe >= 1.2.10\nRequires: FastCFS-vote-config >= 3.5.0\nSummary: FastCFS vote server\n\n%package -n %{FastCFSFuseConfig}\nRequires: faststore-config >= 1.0.0\nSummary: FastCFS fuse config files for sample\n\n%description -n %{FastCFSFused}\nFastCFS fuse\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSUtils}\nFastCFS utils\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSAPI}\nFastCFS api library\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSAPITests}\nFastCFS api tests\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSAPIDevel}\nThis package provides the header files of libfcfsapi\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSAuthServer}\nFastCFS auth server\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSVoteServer}\nFastCFS vote server\ncommit version: %{CommitVersion}\n\n%description -n %{FastCFSFuseConfig}\nFastCFS fuse config files for sample\ncommit version: %{CommitVersion}\n\n%prep\n%setup -q\n\n%build\n./make.sh clean && ./make.sh --exclude=client\n\n%install\nrm -rf %{buildroot}\nDESTDIR=$RPM_BUILD_ROOT ./make.sh --exclude=client install\nFUSE_CONFDIR=%{buildroot}/etc/fastcfs/fcfs/\nSYSTEMDIR=%{buildroot}/usr/lib/systemd/system/\nmkdir -p $FUSE_CONFDIR\nmkdir -p $SYSTEMDIR\ncp conf/*.conf $FUSE_CONFDIR\ncp systemd/fastcfs.service $SYSTEMDIR\ncp systemd/fastauth.service $SYSTEMDIR\ncp systemd/fastvote.service $SYSTEMDIR\n\n%post\n\n%preun\n\n%postun\n\n%clean\nrm -rf %{buildroot}\n\n%files\n\n%post -n %{FastCFSFused}\nmkdir -p /opt/fastcfs/fcfs\nmkdir -p /opt/fastcfs/fuse\nsystemctl enable fastcfs\n\n%files -n %{FastCFSFused}\n/usr/bin/fcfs_fused\n%config(noreplace) /usr/lib/systemd/system/fastcfs.service\n\n%files -n %{FastCFSUtils}\n/usr/bin/fcfs_active_test\n/usr/bin/fcfs_pool_stat\n\n%files -n %{FastCFSAPI}\n%defattr(-,root,root,-)\n/usr/lib64/libfcfsapi.so*\n/usr/lib64/libfcfspreload.so*\n\n%files -n %{FastCFSAPITests}\n/usr/bin/fcfs_beachmark\n/usr/bin/fcfs_test_file_op\n/usr/bin/fcfs_test_file_copy\n/usr/bin/fcfs_test_papi_copy\n/usr/bin/fcfs_test_read_ahead\n\n%files -n %{FastCFSAPIDevel}\n%defattr(-,root,root,-)\n/usr/include/fastcfs/api/*\n\n%post -n %{FastCFSAuthServer}\nmkdir -p /opt/fastcfs/auth\nsystemctl enable fastauth\n\n%post -n %{FastCFSVoteServer}\nmkdir -p /opt/fastcfs/vote\nsystemctl enable fastvote\n\n%files -n %{FastCFSAuthServer}\n/usr/bin/fcfs_authd\n%config(noreplace) /usr/lib/systemd/system/fastauth.service\n\n%files -n %{FastCFSVoteServer}\n/usr/bin/fcfs_voted\n%config(noreplace) /usr/lib/systemd/system/fastvote.service\n\n%files -n %{FastCFSFuseConfig}\n%defattr(-,root,root,-)\n%config(noreplace) /etc/fastcfs/fcfs/*.conf\n\n%changelog\n* Fri Jan 1 2021 YuQing <384681@qq.com>\n- first RPM release (1.0)\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "README-zh_CN.md",
    "content": "# FastCFS -- 可以跑数据库的高性能通用分布式文件系统\n\n[English](README.md) | 简体中文\n\n## 1. 简介\n\nFastCFS 是一款强一致性、高性能、高可用、支持百亿级海量文件的通用分布式文件系统，可以作为MySQL、PostgreSQL、Oracle等数据库，k8s、KVM、FTP、SMB和NFS等系统的后端存储。\n\n### FastCFS 主要特点\n\n* 保证数据强一致前提下实现了高性能：性能完胜Ceph；\n* 完全兼容POSIX文件接口，支持文件锁，64G内存即可支持百亿级海量文件；\n* 高可用：不存在单点，自动failover；\n* 简洁高效的架构和原生实现，不依赖第三方组件，内置运维工具，易用性较好；\n* 随机写性能强悍：FCFS基于trunk顺序分配空间，将客户端的随机写转换为顺序写。\n\n### FastCFS 典型应用场景\n\n* **数据库**：支持常规的独享数据和高阶的共享数据两种存储方式，支持数据库云化；\n* **文件存储**：如文档、图片、视频等等，FastCFS比对象存储更容易与通用软件集成；\n* **超融合存储**：数据库和文件存储共用一个存储集群，显著提升存储资源利用率；\n* **高性能计算**：高可靠和高性能的FastCFS，支持RDMA，天然适合高性能计算场景；\n* **视频监控**：FastCFS采用顺序写盘方式，使用SATA硬盘也可保证多路视频流畅写入。\n\n## 2. 当前版本：V5.5.0\n\n[FastCFS重大版本一览](docs/version-history-zh_CN.md)\n\n## 3. 支持的操作系统\n\n* Linux: Kernel version >= 3.10 （完全支持，推荐使用4.18及更高版本）\n* MacOS or FreeBSD  （仅支持服务端，不支持FUSE）\n\n## 4. 依赖\n\n* [libfuse](https://gitee.com/mirrors/libfuse) (版本 3.9.4 或更高版本，推荐3.16.2)\n    * [Python](https://python.org/) (版本 3.5 或更高版本)\n    * [Ninja](https://ninja-build.org/) (版本 1.7 或更高版本)\n    * [gcc](https://www.gnu.org/software/gcc/) (版本 4.7.0 或更高版本)\n* [libfastcommon](https://gitee.com/fastdfs100/libfastcommon) (tag: V1.0.83)\n* [libserverframe](https://gitee.com/fastdfs100/libserverframe) (tag: V1.2.11)\n* [libdiskallocator](https://gitee.com/fastdfs100/libdiskallocator) (tag: V1.1.13)\n* [fastDIR](https://gitee.com/fastdfs100/fastDIR) (tag: V5.5.0)\n* [faststore](https://gitee.com/fastdfs100/faststore) (tag: V5.5.0)\n* [FastCFS](https://gitee.com/fastdfs100/FastCFS) (tag: V5.5.0)\n\n## 5. 部署 & 运维\n\nFastCFS包含 libfastcommon、libserverframe、libdiskallocator、fastDIR、faststore和FastCFS 六个安装包。\n\n### 一键部署\n\n如果你打算快速体验一下FastCFS，可以一键搭建(包括部署和运行)单节点（需要root身份执行）：\n```\ngit clone https://gitee.com/fastdfs100/FastCFS.git; cd FastCFS/\n./helloWorld.sh\n\n# 注意：helloWorld.sh将更改FastCFS相关配置文件，请不要在多节点集群上执行！\n```\n\n上述操作完成后，执行命令：\n```\ndf -h /opt/fastcfs/fuse | grep fuse\n```\n可以看到FastCFS挂载的文件目录，你可以当作本地文件系统访问该目录。\n\n一键部署的详细说明，请参见这里[一键部署详细说明](docs/Easy-install-detail-zh_CN.md)\n\n### 集群部署工具\n\n推荐使用 [FastCFS集群运维工具](docs/fcfs-ops-tool-zh_CN.md)\n\n### DIY安装\n\n如果你要自己搭建FastCFS环境，可以采用如下三种安装方式之一：\n\n* yum安装（针对CentOS、Rocky、Fedora、RHEL等），参阅 [yum安装文档](docs/YUM-INSTALL-zh_CN.md)\n* apt安装（针对Ubuntu、Debian 和 Deepin），参阅 [apt 安装文档](docs/APT-INSTALL-zh_CN.md)\n* 源码编译安装，参阅 [安装文档](docs/INSTALL-zh_CN.md)\n\n### 配置指南\n\nFastCFS安装完成后，请参阅[配置指南](docs/CONFIGURE-zh_CN.md)\n\n### 集群扩容\n\n详情参见 [FastCFS集群扩容手册](docs/cluster-expansion-zh_CN.md)\n\n## 6. 性能测试\n\nFastCFS性能明显优于Ceph：顺序写是Ceph的6.x倍，顺序读是Ceph的2.x倍，随机写大约是Ceph的2倍。\n\n* [FastCFS与Ceph性能对比测试结果概要](docs/benchmark.md)\n\n* 详情参见 [完整PDF文档](docs/benchmark-20210621.pdf)\n\n* 如何进行性能测试点击 [这里](docs/benchmark-step-by-step.md)\n\n## 7. K8s CSI驱动\n\n参见项目 [fastcfs-csi](https://gitee.com/fastdfs100/fastcfs-csi)\n\n## 8. 技术文章\n\n参见 <a href=\"https://my.oschina.net/u/3334339\" target=\"_blank\">技术博客</a>\n\n## 9. 常见问题\n\n参见 [FastCFS常见问题](docs/FAQ-zh_CN.md)\n\n## 10. 待完成工作\n\n*  [fstore] 单盘故障恢复后，自动恢复数据（已完成）\n*  [fstore] 机器故障或网络短暂故障恢复后，master需重新均衡分配（已完成）\n*  [fauth & fdir & fstore] leader选举支持过半原则，防止脑裂（已完成）\n*  [fauth & fdir & fstore] 实现公用选举节点，双副本防脑裂（已完成）\n*  [fdir & fstore] binlog去重及历史数据清理（已完成）\n*  [fdir & fstore] 数据提交采用多数派确认机制保证数据一致性和可靠性（已完成）\n*  [fdir & fstore] 针对两副本，数据提交多数派机制智能化（已完成）\n*  [fstore] 文件读写性能优化（已完成）\n*  [fdir & api] POSIX兼容性测试及改进（已完成）\n*  [fstore] slice存储引擎插件，有限内存支持海量数据（已完成）\n*  [all] 适配RDMA网络，突破网络瓶颈（已完成）\n*  [fdir] 支持回收站功能，不用担心误删除文件（已完成）\n*  [all] 支持集群在线扩容\n\n参见更多 [TODO List](docs/TODO-zh_CN.md)，欢迎大家参与。\n\n## 11. 商业支持\n\n我们提供商业技术支持和定制化开发，欢迎微信或邮件洽谈。\n\nemail: 384681(at)qq(dot)com\n\n## 12. 联系我们\n\n查看FastCFS相关技术文章，请关注微信公众号：\n\n<img src=\"images/wechat_subscribe.jpg\" width=\"200\" alt=\"微信公众号\">\n\n微信交流群：\n\n<img src=\"images/wechat_group.jpg\" width=\"200\" alt=\"微信交流群\">\n"
  },
  {
    "path": "README.md",
    "content": "# FastCFS -- a high performance general distributed file system for databases, K8s and KVM etc.\n\nEnglish | [Chinese](README-zh_CN.md)\n\n## 1. About\n\nFastCFS is a general distributed file system with strong consistency, high performance, high availability and supporting 10 billion massive files.\nFastCFS can be used as the back-end storage of databases (MySQL, PostgreSQL, Oracle etc.), K8s, KVM, FTP, SMB and NFS.\n\n### Main Features\n\n* High performance on the premise of strong data consistency: performance is more better than ceph\n* Fully compatible with POSIX, supporting file lock and 10 billion massive files\n* High availability: no single point & automatic failover\n* Easy to use:\n    * simple and efficient architecture and native implementation\n    * independent of third-party components\n    * built-in operation and maintenance tools\n* Strong random write performance: FCFS allocates space based on the trunk file, converts random writes from the client to sequential writes\n\n### Classical Application Scene\n\n* **Database**: supports two storage methods (conventional exclusive data and high-level shared data) and database cloudification\n* **File Storage**: documents, pictures, videos, etc., FastCFS is easier to integrate with general software than the object storage\n* **Unified Storage**: database and file storage share a storage cluster, which significantly improves the utilization of storage resources\n* **High Performance Computing**: FastCFS with high reliability and high performance is naturally suitable for the HPC scene\n* **Video monitoring**: smooth writing for multi-channel videos with HDD such as SATA because FastCFS uses sequential writing approach\n\n\n## 2. Current Version: V5.5.0\n\n## 3. Supported Platforms\n\n* Linux: Kernel version >= 3.10  (Full support, >= 4.18 is recommended)\n* MacOS or FreeBSD (Only server side)\n\n## 4. Dependencies\n\n* [libfuse](https://github.com/libfuse/libfuse) (version 3.9.4 or newer, 3.16.2 is recommended)\n    * [Python](https://python.org/) (version 3.5 or newer)\n    * [Ninja](https://ninja-build.org/) (version 1.7 or newer)\n    * [gcc](https://www.gnu.org/software/gcc/) (version 4.7.0 or newer)\n* [libfastcommon](https://github.com/happyfish100/libfastcommon) (tag: V1.0.83)\n* [libserverframe](https://github.com/happyfish100/libserverframe) (tag: V1.2.11)\n* [libdiskallocator](https://github.com/happyfish100/libdiskallocator) (tag: V1.1.13)\n* [fastDIR](https://github.com/happyfish100/fastDIR) (tag: V5.5.0)\n* [faststore](https://github.com/happyfish100/faststore) (tag: V5.5.0)\n* [FastCFS](https://github.com/happyfish100/FastCFS) (tag: V5.5.0)\n\n## 5. Installation\n\n### 5.1 DIY installation\n\nyou can use [Cluster Operation Tool](docs/fcfs-ops-tool.md) to deploy FastCFS\n\nstep by step please see [INSTALL](docs/INSTALL.md)\n\nrecommend to execute libfuse_setup.sh for compiling and installing libfuse\n\n### 5.2 easy installation\n\nlibfastcommon, libserverframe, fastDIR, faststore and FastCFS can be compiled, installed and auto configurated by fastcfs.sh\n\nfastcfs.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.\n\n```\ngit clone https://github.com/happyfish100/FastCFS.git; cd FastCFS/\n```\n\nfastcfs.sh usage:\n\n```\n* install: pull/update codes from gitee, then make and install\n* config: copy config files and configure them with local ip\n* start | stop | restart: for service processes control\n```\n\none click to build (deploy and run) single node demo environment (MUST run by root):\n\n```\n./helloWorld.sh\n```\n\nor execute following commands (MUST run by root):\n\n```\n./fastcfs.sh install\n./fastcfs.sh config --force\n./fastcfs.sh restart\n```\n\nnow you can see the mounted path of FastCFS by the command:\n\n```\ndf -h /opt/fastcfs/fuse | grep fuse\n```\n\n## 6. Benchmark\n\nFastCFS 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.\n\n* [FastCFS vs. Ceph benchmark](docs/benchmark.md)\n\n## 7. K8s CSI Driver\n\n[fastcfs-csi](https://github.com/happyfish100/fastcfs-csi)\n\n## 8. Chinese Relative articles\n\n<a href=\"https://blog.csdn.net/happy_fish100/\" target=\"_blank\">CSDN blog</a>\n\n## 9. TODO List\n\n*  [fstore] data recovery after single disk fault (done)\n*  [fstore] after the machine recovery, the data masters should be rebalanced (done)\n*  [fauth & fdir & fstore] leader election uses  more than half principle to prevent brain-split (done)\n*  [fauth & fdir & fstore] import public vote node under 2 copies scene to prevent brain-split (done)\n*  [fdir & fstore] binlog deduplication and historical data deletion (done)\n*  [fdir & fstore] data submission by majority confirmation to ensure data consistency (done)\n*  [fdir & fstore] for two replicas, the data submission majority mechanism is intelligent (done)\n*  [fstore] file read & write performance optimization (done)\n*  [fdir & api] POSIX compatibility test and improvement (done)\n*  [fstore] slice storage engine plugin to support massive data with limited memory (done)\n*  [all] adapt to RDMA network to break through network bottlenecks (done)\n*  [fdir] support recycle bin, don't worry accidentally deleting files (done)\n*  [all] cluster online expansion\n\n## 10. Business Support\n\nWe provide technical support service and customized development. Welcome to use WeChat or email for discuss.\n\n## 11. Contact us\n\nemail: 384681(at)qq(dot)com\n\nWeChat subscription: search \"fastdfs\" for the related articles (Chinese Only)\n"
  },
  {
    "path": "conf/full/fuse.conf",
    "content": "# the base path to store log files\n# this path must exist\nbase_path = /opt/fastcfs/fcfs\n\n# the mount point (local path) for FUSE\n# the local path must exist\nmountpoint = /opt/fastcfs/fuse\n\n#standard log level as syslog, case insensitive, value list:\n### emerg for emergency\n### alert\n### crit for critical\n### error\n### warn for warning\n### notice\n### info\n### debug\nlog_level = info\n\n#unix group name to run this program, \n#not set (empty) means run by the group of current user\nrun_by_group =\n\n#unix username to run this program,\n#not set (empty) means run by current user\nrun_by_user =\n\n# sync log buff to disk every interval seconds\n# default value is 1 seconds\nsync_log_buff_interval = 1\n\n# if rotate the error log every day\n# default value is false\nrotate_error_log = false\n\n# rotate error log time base, time format: Hour:Minute\n# Hour from 0 to 23, Minute from 0 to 59\n# default value is 00:00\nerror_log_rotate_time = 00:00\n\n# if compress the old error log by gzip\n# default value is false\ncompress_old_error_log = false\n\n# compress the error log days before\n# default value is 1\ncompress_error_log_days_before = 7\n\n# rotate error log when the log file exceeds this size\n# 0 means never rotates log file by log file size\n# default value is 0\nrotate_error_log_size = 0\n\n# keep days of the log files\n# 0 means do not delete old log files\n# default value is 0\nlog_file_keep_days = 0\n\n\n# connect timeout in seconds\n# default value is 10\n# Note: in the intranet network (LAN), 10 seconds is enough.\nconnect_timeout = 10\n\n# network timeout in seconds\n# default value is 60\nnetwork_timeout = 60\n\n# TCP quick ack for Linux (setsockopt with TCP_QUICKACK option)\n# default value is true\ntcp_quick_ack = true\n\n# the rule of read data, value list:\n### any : any available server\n### slave : slave first, access master when all slaves down or offline\n### master : master only (default)\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nread_rule = master\n\n# the mode of retry interval, value list:\n### fixed for fixed interval\n### multiple for multiplication (default)\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nretry_interval_mode = multiple\n\n# the max retry interval in milliseconds\n# valid when retry_interval_mode set to multiple\n# default value is 3000 ms\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nretry_max_interval_ms = 3000\n\n# retry times when connect to server fail\n###  0 for never retry\n### < 0 for infinite retry\n# default value is 200\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nconnect_retry_times = 200\n\n# retry interval when connect to server fail\n# unit: milliseconds\n# default value is 100 ms\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nconnect_retry_interval_ms = 100\n\n# retry times when communicate with server fail\n###  0 for never retry\n### < 0 for infinite retry\n# default value is 200\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nnetwork_retry_times = 200\n\n# retry interval when communicate with server fail\n# unit: milliseconds\n# default value is 100 ms\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nnetwork_retry_interval_ms = 100\n\n# if use busy polling for RDMA network\n# should set to true for HPC\n# default value is false\nbusy_polling = false\n\n\n[idempotency]\n# if enable RPC idempotency for highest level consistency\n# default value is false\nenabled = true\n\n# the idempotency channel hashtable capacity\n# default value is 1361\nchannel_htable_capacity = 1361\n\n# the heartbeat interval for RPC idempotency channel\n# unit: seconds\n# default value is 3s\nchannel_heartbeat_interval = 3\n\n# close the idempotency channel when max idle time reachs\n# unit: seconds\n# default value is 300s\nchannel_max_idle_time  = 300\n\n# max connections for RPC idempotency report\n# you should set this parameter larger than the total server count of\n# FastDIR and FastStore\n# default value is 256\nmax_connections = 1024\n\n# work thread count for RPC idempotency report\n# default value is 1\nwork_threads = 1\n\n# max pkg size for RPC idempotency report\n# default value is 256KB\nmax_pkg_size = 256KB\n\n# thread stack size, should >= 320KB\nthread_stack_size = 512KB\n\n\n[FastDIR]\n# connect timeout in seconds\n# default value is 10\n# Note: in the intranet network (LAN), 10 seconds is enough.\nconnect_timeout = 10\n\n# network timeout in seconds\n# default value is 60\nnetwork_timeout = 60\n\n# the namespace for FastDIR\nnamespace = fs\n\n# config the cluster servers\ncluster_config_filename = ../fdir/cluster.conf\n\n# if use sys lock for file append and truncate to avoid conflict\n# set true when the files appended or truncated by many nodes (FUSE instances)\n# default value is false\nuse_sys_lock_for_append = false\n\n# if async report file attributes (size, modify time etc.) to the FastDIR server\n# default value is true\nasync_report_enabled = true\n\n# the interval in milliseconds for async report file attributes to the FastDIR server\n# default value is 100 ms\nasync_report_interval_ms = 100\n\n# the sharding count of hashtable\n# NO more than 1000 is recommended\n# default value is 17\nhashtable_sharding_count = 17\n\n# the capacity (bucket count) of all sharding hashtables\n# default value is 1403641\nhashtable_total_capacity = 1403641\n\n# the shared allocators for hashtabe entry, task etc.\n# NO more than the CPU cores is recommended\n# default value is 11\nshared_allocator_count = 11\n\n# set the owner (user and group) of new created files and directories\n# the values are:\n## caller: current user and group from the calling process (default)\n## fixed: fixed user and group\nowner_type = caller\n\n# set the user owner (username) of the new created files and directories\n# this parameter is valid only when owner_type set to fixed\n# empty for the user who runs the fcfs_fused program\nowner_user =\n\n# set the group owner (group name) of the new created files and directories\n# this parameter is valid only when owner_type set to fixed\n# empty for the group of the user who runs the fcfs_fused program\nowner_group =\n\n\n[FastStore]\n# connect timeout in seconds\n# default value is 10\n# Note: in the intranet network (LAN), 10 seconds is enough.\nconnect_timeout = 10\n\n# network timeout in seconds\n# default value is 60\nnetwork_timeout = 60\n\n# config the cluster servers and groups\ncluster_config_filename = ../fstore/cluster.conf\n\n# the sharding count of hashtable\n# NO more than 1000 is recommended\n# default value is 163\nhashtable_sharding_count = 163\n\n# the capacity (bucket count) of all sharding hashtables\n# default value is 1403641\nhashtable_total_capacity = 1403641\n\n# the shared allocators for hashtabe entry, task, slice etc.\n# NO more than the CPU cores is recommended\n# default value is 17\nshared_allocator_count = 17\n\n\n[write-combine]\n# if enable write combine feature for FastStore\n# default value is true\nenabled = true\n\n# the buffer size for write combine\n# the min value is 64KB and the max value is the data block size such as 4MB\n# default value is 256KB\nbuffer_size = 256KB\n\n# the min wait time in milliseconds for write combine\n# the min value is 10ms and the max value is 100ms\n# default value is 20ms\nmin_wait_time_ms = 50\n\n# the max wait time in milliseconds for write combine\n# the min value is 100ms and the max value is 10000ms\n# default value is 1000ms\nmax_wait_time_ms = 1000\n\n# the slice size for skipping write combine\n# skip combine when the slice size >= this parameter\n# the min value is 64KB and the max value is the data block size such as 4MB\n# default value is 256KB\nskip_combine_on_slice_size = 256KB\n\n# the merged slice count of last combine for skipping write combine\n# skip combine when the last combine is in progress and\n# it's merged slice count <= this parameter\n# default value is 1\nskip_combine_on_last_merged_slices = 1\n\n# the shared locks for timer (timeout manager)\n# default value is 163\ntimer_shared_lock_count = 163\n\n# the max waiting slice count in queue for flow control\n# default value is 16\nmax_waiting_slice_count = 16\n\n# the thread limit (max threads) for the thread pool\n# default value is 8\nthread_pool_max_threads = 8\n\n# the min idle thread count for the thread pool\n# default value is 2\nthread_pool_min_idle_count = 2\n\n# the max idle time in seconds for the thread pool\n# the running thread will exit on three conditions:\n##   1. this paramter > 0\n##   2. running thread count > thread_pool_min_idle_count\n##   3. the thread's idle time exceeds this parameter\n# default value is 300 (seconds)\nthread_pool_max_idle_time = 300\n\n\n[read-ahead]\n# if enable read ahead feature for FastStore\n# default value is true\nenabled = true\n\n# the TTL in miliseconds for preread cache\n# the min value is 100ms and the max value is 10000ms\n# default value is 1000ms\ncache_ttl_ms = 1000\n\n# the min buffer size for preread\n# the min value is 1KB and the max value is 16KB\n# default value is 4KB\nmin_buffer_size = 4KB\n\n# the max buffer size for preread\n# the min value is 16KB and the max value is 256KB\n# default value is 128KB\nmax_buffer_size = 128KB\n\n# the slice size for skipping read ahead\n# skip read ahead when the slice size >= this parameter\n# the min value is 8KB and the max value is 128KB\n# default value is half of max_buffer_size\nskip_preread_on_slice_size = 64KB\n\n# the shared locks for preread hashtable\n# default value is 1361\nshared_lock_count = 1361\n\n[FUSE]\n# if single thread mode\n# set true to disable multi-threaded operation\n# default value is false\nsinglethread = false\n\n# if use separate fuse device fd for each thread\n# set to true for more high performance\n# default value is false\nclone_fd = true\n\n# the max worker threads for FUSE\n# this parameter is valid when the version of libfuse >= 3.12\n# default value is 10\nmax_threads = 10\n\n# the max idle worker threads for FUSE\n# default value is 10\nmax_idle_threads = 10\n\n# access permissions for other users\n# the values are:\n##  all for all users\n##  root for root only\n##  empty or other values for none\nallow_others = all\n\n# mount the filesystem read-only as mount option: ro\n# default value is false\nread_only = false\n\n# auto unmount on process termination\n# default value is false\nauto_unmount = false\n\n# cache time for file entry in seconds\n# default value is 1.0s\nentry_timeout = 5.0\n\n# cache time for file attribute in seconds\n# default value is 1.0s\nattribute_timeout = 5.0\n\n# if enable kernel writeback cache\n# set to true for unshared data scene (private data for single node)\n# default value is true\nwriteback_cache = true\n\n# if keep kernel cache for read\n# set to true for unshared data scene (private data for single node)\n# should set to false on shared data scene for multi nodes\n# default value is true\nkernel_cache = true\n\n# if enable x-attribute\n# default value is false\n# IMPORTANT NOTE:\n### because Linux kernel get the xattr \"security.capability\" *EVERY* write,\n### do NOT change this parameter to true unless your application need\n### setxattr, getxattr or listxattr really!\nxattr_enabled = false\n\n# if enable additional groups for POSIX ACL\n# default value is true\ngroups_enabled = true\n\n\n[groups-cache]\n# if enable additional groups cache\n# default value is true\nenabled = true\n\n# the timeout of additional groups cache in seconds\n# default value is 300 seconds\ntimeout = 300\n\n# element limit for less memory usage\n# default value is 65536\nelement_limit = 65536\n\n# the sharding count of hashtable\n# NO more than 1000 is recommended\n# default value is 163\nhashtable_sharding_count = 163\n\n# the capacity (bucket count) of all sharding hashtables\n# default value is 175447\nhashtable_total_capacity = 175447\n\n# the shared allocators for hashtabe entry, task etc.\n# NO more than the CPU cores is recommended\n# default value is 7\nshared_allocator_count = 7\n"
  },
  {
    "path": "conf/full/papi.conf",
    "content": "# the base path to store log files\n# this path must exist\nbase_path = /opt/fastcfs/fcfs\n\n# the virtual mount point\nmountpoint = /opt/fastcfs/fuse\n\n#standard log level as syslog, case insensitive, value list:\n### emerg for emergency\n### alert\n### crit for critical\n### error\n### warn for warning\n### notice\n### info\n### debug\nlog_level = info\n\n#unix group name to run this program, \n#not set (empty) means run by the group of current user\nrun_by_group =\n\n#unix username to run this program,\n#not set (empty) means run by current user\nrun_by_user =\n\n# sync log buff to disk every interval seconds\n# default value is 1 seconds\nsync_log_buff_interval = 1\n\n# if rotate the error log every day\n# default value is false\nrotate_error_log = false\n\n# rotate error log time base, time format: Hour:Minute\n# Hour from 0 to 23, Minute from 0 to 59\n# default value is 00:00\nerror_log_rotate_time = 00:00\n\n# if compress the old error log by gzip\n# default value is false\ncompress_old_error_log = false\n\n# compress the error log days before\n# default value is 1\ncompress_error_log_days_before = 7\n\n# rotate error log when the log file exceeds this size\n# 0 means never rotates log file by log file size\n# default value is 0\nrotate_error_log_size = 0\n\n# keep days of the log files\n# 0 means do not delete old log files\n# default value is 0\nlog_file_keep_days = 0\n\n\n# connect timeout in seconds\n# default value is 10\n# Note: in the intranet network (LAN), 10 seconds is enough.\nconnect_timeout = 10\n\n# network timeout in seconds\n# default value is 60\nnetwork_timeout = 60\n\n# TCP quick ack for Linux (setsockopt with TCP_QUICKACK option)\n# default value is true\ntcp_quick_ack = true\n\n# the rule of read data, value list:\n### any : any available server\n### slave : slave first, access master when all slaves down or offline\n### master : master only (default)\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nread_rule = master\n\n# the mode of retry interval, value list:\n### fixed for fixed interval\n### multiple for multiplication (default)\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nretry_interval_mode = multiple\n\n# the max retry interval in milliseconds\n# valid when retry_interval_mode set to multiple\n# default value is 3000 ms\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nretry_max_interval_ms = 3000\n\n# retry times when connect to server fail\n###  0 for never retry\n### < 0 for infinite retry\n# default value is 200\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nconnect_retry_times = 200\n\n# retry interval when connect to server fail\n# unit: milliseconds\n# default value is 100 ms\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nconnect_retry_interval_ms = 100\n\n# retry times when communicate with server fail\n###  0 for never retry\n### < 0 for infinite retry\n# default value is 200\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nnetwork_retry_times = 200\n\n# retry interval when communicate with server fail\n# unit: milliseconds\n# default value is 100 ms\n# this parameter can be overriden / redefined in section [FastDIR] and [FastStore]\nnetwork_retry_interval_ms = 100\n\n[idempotency]\n# if enable RPC idempotency for highest level consistency\n# default value is false\nenabled = true\n\n# the idempotency channel hashtable capacity\n# default value is 1361\nchannel_htable_capacity = 1361\n\n# the heartbeat interval for RPC idempotency channel\n# unit: seconds\n# default value is 3s\nchannel_heartbeat_interval = 3\n\n# close the idempotency channel when max idle time reachs\n# unit: seconds\n# default value is 300s\nchannel_max_idle_time  = 300\n\n# max connections for RPC idempotency report\n# you should set this parameter larger than the total server count of\n# FastDIR and FastStore\n# default value is 256\nmax_connections = 1024\n\n# work thread count for RPC idempotency report\n# default value is 1\nwork_threads = 1\n\n# max pkg size for RPC idempotency report\n# default value is 256KB\nmax_pkg_size = 256KB\n\n# thread stack size, should >= 320KB\nthread_stack_size = 512KB\n\n\n[FastDIR]\n# connect timeout in seconds\n# default value is 10\n# Note: in the intranet network (LAN), 10 seconds is enough.\nconnect_timeout = 10\n\n# network timeout in seconds\n# default value is 60\nnetwork_timeout = 60\n\n# the namespace for FastDIR\nnamespace = fs\n\n# config the cluster servers\ncluster_config_filename = ../fdir/cluster.conf\n\n# if use sys lock for file append and truncate to avoid conflict\n# set true when the files appended or truncated by many nodes (FUSE instances)\n# default value is false\nuse_sys_lock_for_append = false\n\n# if async report file attributes (size, modify time etc.) to the FastDIR server\n# default value is true\nasync_report_enabled = true\n\n# the interval in milliseconds for async report file attributes to the FastDIR server\n# default value is 100 ms\nasync_report_interval_ms = 100\n\n# the sharding count of hashtable\n# NO more than 1000 is recommended\n# default value is 17\nhashtable_sharding_count = 17\n\n# the capacity (bucket count) of all sharding hashtables\n# default value is 1403641\nhashtable_total_capacity = 1403641\n\n# the shared allocators for hashtabe entry, task etc.\n# NO more than the CPU cores is recommended\n# default value is 11\nshared_allocator_count = 11\n\n# set the owner (user and group) of new created files and directories\n# the values are:\n## caller: current user and group from the calling process (default)\n## fixed: fixed user and group\nowner_type = caller\n\n# set the user owner (username) of the new created files and directories\n# this parameter is valid only when owner_type set to fixed\n# empty for the user who runs the fs_fused program\nowner_user =\n\n# set the group owner (group name) of the new created files and directories\n# this parameter is valid only when owner_type set to fixed\n# empty for the group of the user who runs the fs_fused program\nowner_group =\n\n\n[FastStore]\n# connect timeout in seconds\n# default value is 10\n# Note: in the intranet network (LAN), 10 seconds is enough.\nconnect_timeout = 10\n\n# network timeout in seconds\n# default value is 60\nnetwork_timeout = 60\n\n# config the cluster servers and groups\ncluster_config_filename = ../fstore/cluster.conf\n\n# the sharding count of hashtable\n# NO more than 1000 is recommended\n# default value is 163\nhashtable_sharding_count = 163\n\n# the capacity (bucket count) of all sharding hashtables\n# default value is 1403641\nhashtable_total_capacity = 1403641\n\n# the shared allocators for hashtabe entry, task, slice etc.\n# NO more than the CPU cores is recommended\n# default value is 17\nshared_allocator_count = 17\n\n\n[write-combine]\n# if enable write combine feature for FastStore\n# default value is true\nenabled = true\n\n# the buffer size for write combine\n# the min value is 64KB and the max value is the data block size such as 4MB\n# default value is 256KB\nbuffer_size = 256KB\n\n# the min wait time in milliseconds for write combine\n# the min value is 10ms and the max value is 100ms\n# default value is 20ms\nmin_wait_time_ms = 50\n\n# the max wait time in milliseconds for write combine\n# the min value is 100ms and the max value is 10000ms\n# default value is 1000ms\nmax_wait_time_ms = 1000\n\n# the slice size for skipping write combine\n# skip combine when the slice size >= this parameter\n# the min value is 64KB and the max value is the data block size such as 4MB\n# default value is 256KB\nskip_combine_on_slice_size = 256KB\n\n# the merged slice count of last combine for skipping write combine\n# skip combine when the last combine is in progress and\n# it's merged slice count <= this parameter\n# default value is 1\nskip_combine_on_last_merged_slices = 1\n\n# the shared locks for timer (timeout manager)\n# default value is 163\ntimer_shared_lock_count = 163\n\n# the max waiting slice count in queue for flow control\n# default value is 16\nmax_waiting_slice_count = 16\n\n# the thread limit (max threads) for the thread pool\n# default value is 8\nthread_pool_max_threads = 8\n\n# the min idle thread count for the thread pool\n# default value is 2\nthread_pool_min_idle_count = 2\n\n# the max idle time in seconds for the thread pool\n# the running thread will exit on three conditions:\n##   1. this paramter > 0\n##   2. running thread count > thread_pool_min_idle_count\n##   3. the thread's idle time exceeds this parameter\n# default value is 300 (seconds)\nthread_pool_max_idle_time = 300\n\n\n[read-ahead]\n# if enable read ahead feature for FastStore\n# default value is true\nenabled = true\n\n# the TTL in miliseconds for preread cache\n# the min value is 100ms and the max value is 10000ms\n# default value is 1000ms\ncache_ttl_ms = 1000\n\n# the min buffer size for preread\n# the min value is 1KB and the max value is 16KB\n# default value is 4KB\nmin_buffer_size = 4KB\n\n# the max buffer size for preread\n# the min value is 16KB and the max value is 256KB\n# default value is 128KB\nmax_buffer_size = 128KB\n\n# the slice size for skipping read ahead\n# skip read ahead when the slice size >= this parameter\n# the min value is 8KB and the max value is 128KB\n# default value is half of max_buffer_size\nskip_preread_on_slice_size = 64KB\n\n# the shared locks for preread hashtable\n# default value is 1361\nshared_lock_count = 1361\n"
  },
  {
    "path": "conf/fuse.conf",
    "content": "# the base path to store log files\n# this path must exist\nbase_path = /opt/fastcfs/fcfs\n\n# the mount point (local path) for FUSE\n# the local path must exist\nmountpoint = /opt/fastcfs/fuse\n\n# if use busy polling for RDMA network\n# should set to true for HPC\n# default value is false\nbusy_polling = false\n\n\n[idempotency]\n# if enable RPC idempotency for highest level consistency\n# default value is false\nenabled = true\n\n# thread stack size, should >= 320KB\nthread_stack_size = 512KB\n\n\n[FastDIR]\n# the namespace for FastDIR\nnamespace = fs\n\n# config the cluster servers\ncluster_config_filename = ../fdir/cluster.conf\n\n# if use sys lock for file append and truncate to avoid conflict\n# set true when the files appended or truncated by many nodes (FUSE instances)\n# default value is false\nuse_sys_lock_for_append = false\n\n# if async report file attributes (size, modify time etc.) to the FastDIR server\n# default value is true\nasync_report_enabled = true\n\n# the interval in milliseconds for async report file attributes to the FastDIR server\n# default value is 100 ms\nasync_report_interval_ms = 100\n\n\n[FastStore]\n# config the cluster servers and groups\ncluster_config_filename = ../fstore/cluster.conf\n\n\n[write-combine]\n# if enable write combine feature for FastStore\n# default value is true\nenabled = true\n\n\n[read-ahead]\n# if enable read ahead feature for FastStore\n# default value is true\nenabled = true\n\n\n[FUSE]\n# if use separate fuse device fd for each thread\n# set to true for more high performance\n# default value is false\nclone_fd = true\n\n# access permissions for other users\n# the values are:\n##  all for all users\n##  root for root only\n##  empty or other values for none\nallow_others = all\n\n# cache time for file attribute in seconds\n# default value is 1.0s\nattribute_timeout = 5.0\n\n# cache time for file entry in seconds\n# default value is 1.0s\nentry_timeout = 5.0\n\n# if enable kernel writeback cache\n# set to true for unshared data scene (private data for single node)\n# default value is true\nwriteback_cache = true\n\n# if keep kernel cache for read\n# set to true for unshared data scene (private data for single node)\n# should set to false on shared data scene for multi nodes\n# default value is true\nkernel_cache = true\n\n# if enable x-attribute\n# default value is false\n# IMPORTANT NOTE:\n### because Linux kernel get the xattr \"security.capability\" *EVERY* write,\n### do NOT change this parameter to true unless your application need\n### setxattr, getxattr or listxattr really!\nxattr_enabled = false\n"
  },
  {
    "path": "conf/papi.conf",
    "content": "# the base path to store log files\n# this path must exist\nbase_path = /opt/fastcfs/fcfs\n\n# the virtual mount point\nmountpoint = /\n\n\n[idempotency]\n# if enable RPC idempotency for highest level consistency\n# default value is false\nenabled = true\n\n# thread stack size, should >= 320KB\nthread_stack_size = 512KB\n\n\n[FastDIR]\n# the namespace for FastDIR\nnamespace = fs\n\n# config the cluster servers\ncluster_config_filename = ../fdir/cluster.conf\n\n# if use sys lock for file append and truncate to avoid conflict\n# set true when the files appended or truncated by many nodes (FUSE instances)\n# default value is false\nuse_sys_lock_for_append = false\n\n# if async report file attributes (size, modify time etc.) to the FastDIR server\n# default value is true\nasync_report_enabled = true\n\n# the interval in milliseconds for async report file attributes to the FastDIR server\n# default value is 100 ms\nasync_report_interval_ms = 100\n\n\n[FastStore]\n# config the cluster servers and groups\ncluster_config_filename = ../fstore/cluster.conf\n\n\n[write-combine]\n# if enable write combine feature for FastStore\n# default value is true\nenabled = true\n\n\n[read-ahead]\n# if enable read ahead feature for FastStore\n# default value is true\nenabled = true\n"
  },
  {
    "path": "debian/changelog",
    "content": "fastcfs (5.5.0-1) unstable; urgency=medium\n\n  * upgrade to 5.5.0-1\n\n -- YuQing <384681@qq.com>  Sun, 23 Nov 2025 10:53:21 +0000\n\nfastcfs (5.5.0-1) unstable; urgency=medium\n\n  * upgrade to 5.5.0-1\n\n -- YuQing <384681@qq.com>  Sun, 23 Nov 2025 10:06:42 +0000\n\nfastcfs (5.5.0-1) unstable; urgency=medium\n\n  * upgrade to 5.5.0-1\n\n -- YuQing <384681@qq.com>  Sun, 23 Nov 2025 09:11:49 +0000\n\nfastcfs (5.4.1-1) unstable; urgency=medium\n\n  * upgrade to 5.4.1-1\n\n -- YuQing <384681@qq.com>  Sat, 16 Aug 2025 16:37:13 +0000\n\nfastcfs (5.4.0-1) unstable; urgency=medium\n\n  * upgrade to 5.4.0-1\n\n -- YuQing <384681@qq.com>  Sun, 06 Apr 2025 17:01:49 +0000\n\nfastcfs (5.3.2-1) unstable; urgency=medium\n\n  * upgrade to 5.3.2-1\n\n -- YuQing <384681@qq.com>  Sun, 29 Sep 2024 15:29:40 +0000\n\nfastcfs (5.3.1-1) unstable; urgency=medium\n\n  * upgrade to 5.3.1-1\n\n -- YuQing <384681@qq.com>  Sat, 15 Jun 2024 14:50:36 +0000\n\nfastcfs (5.3.0-1) unstable; urgency=medium\n\n  * upgrade to 5.3.0-1\n\n -- YuQing <384681@qq.com>  Sun, 17 Mar 2024 15:16:01 +0000\n\nfastcfs (5.2.0-1) unstable; urgency=medium\n\n  * upgrade to 5.2.0-1\n\n -- YuQing <384681@qq.com>  Wed, 31 Jan 2024 12:05:19 +0000\n\nfastcfs (5.1.0-1) unstable; urgency=medium\n\n  * upgrade to 5.1.0-1\n\n -- YuQing <384681@qq.com>  Mon, 01 Jan 2024 11:30:03 +0000\n\nfastcfs (5.0.0-3) unstable; urgency=medium\n\n  * upgrade to 5.0.0-3\n\n -- YuQing <384681@qq.com>  Tue, 21 Nov 2023 14:41:40 +0000\n\nfastcfs (5.0.0-2) unstable; urgency=medium\n\n  * upgrade to 5.0.0-2\n\n -- YuQing <384681@qq.com>  Mon, 20 Nov 2023 13:29:08 +0000\n\nfastcfs (5.0.0-1) unstable; urgency=medium\n\n  * upgrade to 5.0.0-1\n\n -- YuQing <384681@qq.com>  Sun, 19 Nov 2023 14:50:41 +0000\n\nfastcfs (4.3.0-1) unstable; urgency=medium\n\n  * upgrade to 4.3.0-1\n\n -- YuQing <384681@qq.com>  Sun, 06 Aug 2023 07:28:20 +0000\n\nfastcfs (4.2.0-1) unstable; urgency=medium\n\n  * upgrade to 4.2.0-1\n\n -- YuQing <384681@qq.com>  Sun, 23 Jul 2023 14:33:49 +0000\n\nfastcfs (4.1.0-1) unstable; urgency=medium\n\n  * upgrade to 4.1.0-1\n\n -- YuQing <384681@qq.com>  Sat, 24 Jun 2023 06:57:02 +0000\n\nfastcfs (4.0.0-1) unstable; urgency=medium\n\n  * upgrade to 4.0.0-1\n\n -- YuQing <384681@qq.com>  Sun, 04 Jun 2023 10:59:03 +0000\n\nfastcfs (3.7.2-1) unstable; urgency=medium\n\n  * upgrade to 3.7.2-1\n\n -- YuQing <384681@qq.com>  Sat, 18 Feb 2023 05:50:44 +0000\n\nfastcfs (3.7.1-1) unstable; urgency=medium\n\n  * upgrade to 3.7.1-1\n\n -- YuQing <384681@qq.com>  Sun, 15 Jan 2023 13:56:32 +0000\n\nfastcfs (3.7.0-1) unstable; urgency=medium\n\n  * upgrade to 3.7.0-1\n\n -- YuQing <384681@qq.com>  Mon, 21 Nov 2022 15:01:45 +0000\n\nfastcfs (3.6.2-1) unstable; urgency=medium\n\n  * upgrade to 3.6.2-1\n\n -- YuQing <384681@qq.com>  Sat, 08 Oct 2022 13:30:32 +0000\n\nfastcfs (3.6.1-1) unstable; urgency=medium\n\n  * upgrade to 3.6.1-1\n\n -- YuQing <384681@qq.com>  Thu, 22 Sep 2022 12:24:49 +0000\n\nfastcfs (3.6.0-1) unstable; urgency=medium\n\n  * upgrade to 3.6.0-1\n\n -- YuQing <384681@qq.com>  Wed, 07 Sep 2022 13:38:59 +0000\n\nfastcfs (3.5.0-2) unstable; urgency=medium\n\n  * upgrade to 3.5.0-2\n\n -- YuQing <384681@qq.com>  Sat, 30 Jul 2022 12:58:43 +0000\n\nfastcfs (3.5.0-1) unstable; urgency=medium\n\n  * upgrade to 3.5.0-1\n\n -- YuQing <384681@qq.com>  Mon, 25 Jul 2022 13:54:45 +0000\n\nfastcfs (3.4.0-2) unstable; urgency=medium\n\n  * upgrade to 3.4.0-2\n\n -- YuQing <384681@qq.com>  Thu, 30 Jun 2022 15:20:18 +0000\n\nfastcfs (3.4.0-1) unstable; urgency=medium\n\n  * upgrade to 3.4.0-1\n\n -- YuQing <384681@qq.com>  Wed, 15 Jun 2022 14:28:17 +0000\n\nfastcfs (3.3.0-1) unstable; urgency=medium\n\n  * upgrade to 3.3.0-1\n\n -- YuQing <384681@qq.com>  Thu, 28 Apr 2022 11:56:12 +0000\n\nfastcfs (3.2.0-1) unstable; urgency=medium\n\n  [ sungness ]\n  * Update README.md\n  * Update AUTH-zh_CN.md\n  * Update docs style\n  * Update CONFIGURE-zh_CN.md\n  * update docs style\n  * Update fcfs.sh add status command\n  * Update fcfs-ops-tool.md add  description for status command\n  * Update fcfs.sh\n\n  [ YuQing ]\n  * CONFIGURE-zh_CN.md changed for storage engine\n\n  [ sungness ]\n  * Update fcfs.sh and fcfs_conf.sh add storage.conf for fdir\n\n  [ YuQing ]\n  * AUTH-zh_CN.md changed\n  * AUTH-zh_CN.md refined\n  * AUTH-zh_CN.md format\n  * fcfs_authd.service rename to fastauth.service\n  * README: description improved\n  * fix permission check for storage pool list\n  * fcfs_pool.c: beautify spool output\n  * fix list_spool and list_gpool return status\n  * [fauth] support regenerate secret key\n  * use -y option to confirm when the user is same with the admin\n  * beautify prompt for -y option\n  * change FUSEOwnerType to FCFSAPIOwnerType\n  * add files src/api/fcfs_posix_api.[hc]\n  * move fcfs_posix_api.[hc] to std/posix_api.[hc]\n  * add files: std/fd_manager.[hc]\n  * add files: std/papi_file.[hc] and std/papi_dir.[hc]\n  * fcfs_write and fcfs_pwrite impl.\n  * fcfs_writev and fcfs_pwritev impl.\n  * fcfs_read[v] and fcfs_pread[v] impl.\n  * impl. fcfs_fstat, fcfs_symlinkat, etc.\n  * rename papi_file.[hc] to papi.[hc]\n  * call stat_dentry_xxx with flags\n  * call link_dentry_xxx with flags\n  * fcfs_api_file.c: check_writable and check_readable\n  * impl. fcfs_utimes, fcfs_rename etc.\n  * call remove_dentry_xxx with flags\n  * impl. fcfs_setxattr, fcfs_getxattr etc.\n  * impl. opendir, readdir, closedir ...\n  * fcntl impl.\n  * chdir and getcwd impl.\n  * wrapper chroot, dup, dup2 etc.\n  * add src/api/tests/test_papi_copy.c and test passed\n  * test_papi_copy.c: support readv & writev\n  * add preload files: types.h and global.[hc]\n  * adapt dlsym in mac OS\n  * preload api wrapper done.\n  * set get size flags for getxattr and listxattr\n  * proxy api correctly (such as use syscall) when not inited\n  * __xstat, __lxstat etc. implement OK.\n\n  [ vazmin ]\n  * fcfs_authd service rename to fastauth\n\n  [ YuQing ]\n  * support posix_fallocate, dprintf etc.\n  * support euidaccess and eaccess\n  * preload support posix_fallocate, dprintf etc.\n  * C API fcfs_fopen impl.\n  * remove ctx parameter for posix functions with fd\n  * fdopen, freopen impl.\n  * impl. fputc, fgetc, fread, fwrite etc.\n  * impl. getdelim, getline etc.\n  * support function fcfs_readline\n  * preload for c APIs\n  * change function prototypes for __xmknod and __xmknodat\n  * compile passed in MacOS\n  * impl. __fprintf_chk and __vfprintf_chk\n  * impl. fsync, fdatasync and fcloseall\n  * preload use syscall to forward request\n  * papi.[hc] use fcfs_getcwd for current directory\n  * extract common codes for posix_api_init\n  * impl. fcfs_api_log_client_common_configs and fcfs_posix_api_log_configs\n  * add docs/cluster-expansion-zh_CN.md\n  * docs/cluster-expansion-zh_CN.md refined\n  * docs/cluster-expansion-zh_CN.md refined v2\n  * docs/cluster-expansion-zh_CN.md refined v3\n  * upgrade version to 3.2.0\n  * upgrade versions for RPM spec files\n  * compile passed in CentOS 7.4\n  * fuse.conf add parameter: xattr_enabled\n  * docs/cluster-expansion-zh_CN.md refined v4\n  * add link to docs/cluster-expansion-zh_CN.md\n\n  [ Jerry ]\n  * add mkdocs for documents build\n\n  [ YuQing ]\n  * add comments for posix_api.h\n  * cluster-expansion-zh_CN.md refined v5\n  * change version declare for README.md\n  * README refined\n\n -- YuQing <384681@qq.com>  Sun, 13 Mar 2022 17:07:08 +0800\n\nfastcfs (3.1.0-1) unstable; urgency=medium\n\n  * upgrade version to 3.1.0\n\n -- YuQing <384681@qq.com>  Sat, 15 Jan 2022 20:54:06 +0800\n\nfastcfs (3.0.0-1) unstable; urgency=medium\n\n  * upgrade version to 3.0.0\n\n -- YuQing <384681@qq.com>  Sun, 26 Dec 2021 21:55:51 +0800\n \nfastcfs (2.3.0-1) unstable; urgency=medium\n\n  * Initial release.\n\n -- YuQing <384681@qq.com>  Tue, 20 Jul 2021 00:55:33 +0800\n"
  },
  {
    "path": "debian/compat",
    "content": "11\n"
  },
  {
    "path": "debian/control",
    "content": "Source: fastcfs\nSection: admin\nPriority: optional\nMaintainer: YuQing <384681@qq.com>\nBuild-Depends: debhelper (>=11~),\n               fastdir-dev (>= 3.2.0) <pkg.fastcfs.core>,\n               faststore-dev (>= 3.2.0) <pkg.fastcfs.core>,\n               libfuse3-dev (>= 3.10.1) <pkg.fastcfs.core>,\n               fastcfs-auth-dev (>=3.6.0) <pkg.fastcfs.core>,\n               fastcfs-vote-dev (>=3.6.0) <pkg.fastcfs.core>,\n               libfastcommon-dev (>= 1.0.56),\n               libserverframe-dev (>= 1.1.13)\nStandards-Version: 4.1.4\nHomepage: http://github.com/happyfish100/FastCFS/\n\n\nPackage: fastcfs\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.fastcfs.core>\nDepends: fastcfs-fused (= ${binary:Version}),\n         fastcfs-utils (= ${binary:Version}),\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: the auth client library and config files of FastCFS.\n a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms.\n\n\nPackage: fastcfs-fused\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.fastcfs.core>\nDepends: fastcfs-api-libs (= ${binary:Version}),\n         fuse3 (>= 3.10.1),\n         fastcfs-fuse-config (>= ${fastcfs-fuse-config:Version}),\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: FastCFS fuse\n\nPackage: fastcfs-utils\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.fastcfs.core>\nDepends: libserverframe (>= ${libserverframe:Version}),\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: FastCFS utils\n\nPackage: fastcfs-api-libs\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.fastcfs.core>\nDepends: fastdir-client (>= ${fastdir-client:Version}),\n         faststore-client (>= ${faststore-client:Version}),\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: FastCFS api library\n\nPackage: fastcfs-api-tests\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.fastcfs.core>\nDepends: fastcfs-fuse-config (>= ${fastcfs-fuse-config:Version}),\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: FastCFS api tests\n\nPackage: fastcfs-api-dev\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.fastcfs.core>\nDepends: fastcfs-api-libs (= ${binary:Version}),\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: header files of FastCFS api library\n\nPackage: fastcfs-auth-server\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.fastcfs.core>\nDepends: fastdir-client (>= ${fastdir-client:Version}),\n         fastcfs-auth-config (>= ${fastcfs-auth-config:Version}),\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: FastCFS auth server\n\nPackage: fastcfs-fuse-config\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.fastcfs.core>\nDepends: faststore-config (>= ${faststore-config:Version})\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: FastCFS fuse config files for sample\n\nPackage: fastcfs-auth\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.auth.client>\nDepends: libfastcommon (>= ${libfastcommon:Version}),\n         libserverframe (>= ${libserverframe:Version}),\n         fastcfs-auth-server (= ${binary:Version}),\n         fastcfs-auth-client (= ${binary:Version}),\n         fastcfs-auth-config (>= ${fastcfs-auth-config:Version}),\n         ${misc:Depends}\nDescription: the auth client library and config files of FastCFS.\n FastCFS is a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms.\n\n\nPackage: fastcfs-auth-dev\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.auth.client>\nDepends: fastcfs-auth-client (= ${binary:Version}),\n         ${misc:Depends}\nDescription: header files of FastCFS auth client\n This package provides the header files of libfcfsauthclient\n\n \nPackage: fastcfs-auth-client\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.auth.client>\nDepends: libfastcommon (>= ${libfastcommon:Version}),\n         libserverframe (>= ${libserverframe:Version}),\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: FastCFS auth client\n FastCFS auth client\n\nPackage: fastcfs-auth-config\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.auth.client>\nDepends: ${misc:Depends}\nDescription: FastCFS auth config files for sample\n FastCFS auth config files for sample\n\nPackage: fastcfs-vote-server\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.fastcfs.core>\nDepends: libserverframe (>= ${libserverframe:Version}),\n         fastcfs-vote-config (>= ${fastcfs-vote-config:Version}),\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: FastCFS vote server\n\nPackage: fastcfs-vote\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.vote.client>\nDepends: libfastcommon (>= ${libfastcommon:Version}),\n         libserverframe (>= ${libserverframe:Version}),\n         fastcfs-vote-server (= ${binary:Version}),\n         fastcfs-vote-client (= ${binary:Version}),\n         fastcfs-vote-config (>= ${fastcfs-vote-config:Version}),\n         ${misc:Depends}\nDescription: the vote client library and config files of FastCFS.\n FastCFS is a high performance distributed file system which can be used as the back-end storage of databases and cloud platforms.\n\nPackage: fastcfs-vote-dev\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.vote.client>\nDepends: fastcfs-vote-client (= ${binary:Version}),\n         ${misc:Depends}\nDescription: header files of FastCFS vote client\n This package provides the header files of libfcfsauthclient\n \nPackage: fastcfs-vote-client\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.vote.client>\nDepends: libfastcommon (>= ${libfastcommon:Version}),\n         libserverframe (>= ${libserverframe:Version}),\n         ${misc:Depends}, ${shlibs:Depends}\nDescription: FastCFS vote client\n FastCFS vote client\n\nPackage: fastcfs-vote-config\nArchitecture: any\nMulti-Arch: foreign\nBuild-Profiles: <pkg.vote.client>\nDepends: ${misc:Depends}\nDescription: FastCFS vote config files for sample\n FastCFS vote config files for sample"
  },
  {
    "path": "debian/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: fastcfs\nSource: <url://example.com>\n#\n# Please double check copyright with the licensecheck(1) command.\n\nFiles:     *\nCopyright: 2020 YuQing <384681@qq.com>\nLicense:   AGPL-3.0+\n This program is free software: you can use, redistribute, and/or modify\n it under the terms of the GNU Affero General Public License, version 3\n or later (\"AGPL\"), as published by the Free Software Foundation.\n .\n This program is distributed in the hope that it will be useful, but WITHOUT\n ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n FITNESS FOR A PARTICULAR PURPOSE.\n .\n You should have received a copy of the GNU Affero General Public License\n along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n# License file: LICENSE\n                     GNU AFFERO GENERAL PUBLIC LICENSE\n                        Version 3, 19 November 2007\n .\n  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n  Everyone is permitted to copy and distribute verbatim copies\n  of this license document, but changing it is not allowed.\n .\n                             Preamble\n .\n   The GNU Affero General Public License is a free, copyleft license for\n software and other kinds of works, specifically designed to ensure\n cooperation with the community in the case of network server software.\n .\n   The licenses for most software and other practical works are designed\n to take away your freedom to share and change the works.  By contrast,\n our General Public Licenses are intended to guarantee your freedom to\n share and change all versions of a program--to make sure it remains free\n software for all its users.\n .\n   When we speak of free software, we are referring to freedom, not\n price.  Our General Public Licenses are designed to make sure that you\n have the freedom to distribute copies of free software (and charge for\n them if you wish), that you receive source code or can get it if you\n want it, that you can change the software or use pieces of it in new\n free programs, and that you know you can do these things.\n .\n   Developers that use our General Public Licenses protect your rights\n with two steps: (1) assert copyright on the software, and (2) offer\n you this License which gives you legal permission to copy, distribute\n and/or modify the software.\n .\n   A secondary benefit of defending all users' freedom is that\n improvements made in alternate versions of the program, if they\n receive widespread use, become available for other developers to\n incorporate.  Many developers of free software are heartened and\n encouraged by the resulting cooperation.  However, in the case of\n software used on network servers, this result may fail to come about.\n The GNU General Public License permits making a modified version and\n letting the public access it on a server without ever releasing its\n source code to the public.\n .\n   The GNU Affero General Public License is designed specifically to\n ensure that, in such cases, the modified source code becomes available\n to the community.  It requires the operator of a network server to\n provide the source code of the modified version running there to the\n users of that server.  Therefore, public use of a modified version, on\n a publicly accessible server, gives the public access to the source\n code of the modified version.\n .\n   An older license, called the Affero General Public License and\n published by Affero, was designed to accomplish similar goals.  This is\n a different license, not a version of the Affero GPL, but Affero has\n released a new version of the Affero GPL which permits relicensing under\n this license.\n .\n   The precise terms and conditions for copying, distribution and\n modification follow.\n .\n                        TERMS AND CONDITIONS\n .\n   0. Definitions.\n .\n   \"This License\" refers to version 3 of the GNU Affero General Public License.\n .\n   \"Copyright\" also means copyright-like laws that apply to other kinds of\n works, such as semiconductor masks.\n .\n   \"The Program\" refers to any copyrightable work licensed under this\n License.  Each licensee is addressed as \"you\".  \"Licensees\" and\n \"recipients\" may be individuals or organizations.\n .\n   To \"modify\" a work means to copy from or adapt all or part of the work\n in a fashion requiring copyright permission, other than the making of an\n exact copy.  The resulting work is called a \"modified version\" of the\n earlier work or a work \"based on\" the earlier work.\n .\n   A \"covered work\" means either the unmodified Program or a work based\n on the Program.\n .\n   To \"propagate\" a work means to do anything with it that, without\n permission, would make you directly or secondarily liable for\n infringement under applicable copyright law, except executing it on a\n computer or modifying a private copy.  Propagation includes copying,\n distribution (with or without modification), making available to the\n public, and in some countries other activities as well.\n .\n   To \"convey\" a work means any kind of propagation that enables other\n parties to make or receive copies.  Mere interaction with a user through\n a computer network, with no transfer of a copy, is not conveying.\n .\n   An interactive user interface displays \"Appropriate Legal Notices\"\n to the extent that it includes a convenient and prominently visible\n feature that (1) displays an appropriate copyright notice, and (2)\n tells the user that there is no warranty for the work (except to the\n extent that warranties are provided), that licensees may convey the\n work under this License, and how to view a copy of this License.  If\n the interface presents a list of user commands or options, such as a\n menu, a prominent item in the list meets this criterion.\n .\n   1. Source Code.\n .\n   The \"source code\" for a work means the preferred form of the work\n for making modifications to it.  \"Object code\" means any non-source\n form of a work.\n .\n   A \"Standard Interface\" means an interface that either is an official\n standard defined by a recognized standards body, or, in the case of\n interfaces specified for a particular programming language, one that\n is widely used among developers working in that language.\n .\n   The \"System Libraries\" of an executable work include anything, other\n than the work as a whole, that (a) is included in the normal form of\n packaging a Major Component, but which is not part of that Major\n Component, and (b) serves only to enable use of the work with that\n Major Component, or to implement a Standard Interface for which an\n implementation is available to the public in source code form.  A\n \"Major Component\", in this context, means a major essential component\n (kernel, window system, and so on) of the specific operating system\n (if any) on which the executable work runs, or a compiler used to\n produce the work, or an object code interpreter used to run it.\n .\n   The \"Corresponding Source\" for a work in object code form means all\n the source code needed to generate, install, and (for an executable\n work) run the object code and to modify the work, including scripts to\n control those activities.  However, it does not include the work's\n System Libraries, or general-purpose tools or generally available free\n programs which are used unmodified in performing those activities but\n which are not part of the work.  For example, Corresponding Source\n includes interface definition files associated with source files for\n the work, and the source code for shared libraries and dynamically\n linked subprograms that the work is specifically designed to require,\n such as by intimate data communication or control flow between those\n subprograms and other parts of the work.\n .\n   The Corresponding Source need not include anything that users\n can regenerate automatically from other parts of the Corresponding\n Source.\n .\n   The Corresponding Source for a work in source code form is that\n same work.\n .\n   2. Basic Permissions.\n .\n   All rights granted under this License are granted for the term of\n copyright on the Program, and are irrevocable provided the stated\n conditions are met.  This License explicitly affirms your unlimited\n permission to run the unmodified Program.  The output from running a\n covered work is covered by this License only if the output, given its\n content, constitutes a covered work.  This License acknowledges your\n rights of fair use or other equivalent, as provided by copyright law.\n .\n   You may make, run and propagate covered works that you do not\n convey, without conditions so long as your license otherwise remains\n in force.  You may convey covered works to others for the sole purpose\n of having them make modifications exclusively for you, or provide you\n with facilities for running those works, provided that you comply with\n the terms of this License in conveying all material for which you do\n not control copyright.  Those thus making or running the covered works\n for you must do so exclusively on your behalf, under your direction\n and control, on terms that prohibit them from making any copies of\n your copyrighted material outside their relationship with you.\n .\n   Conveying under any other circumstances is permitted solely under\n the conditions stated below.  Sublicensing is not allowed; section 10\n makes it unnecessary.\n .\n   3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n .\n   No covered work shall be deemed part of an effective technological\n measure under any applicable law fulfilling obligations under article\n 11 of the WIPO copyright treaty adopted on 20 December 1996, or\n similar laws prohibiting or restricting circumvention of such\n measures.\n .\n   When you convey a covered work, you waive any legal power to forbid\n circumvention of technological measures to the extent such circumvention\n is effected by exercising rights under this License with respect to\n the covered work, and you disclaim any intention to limit operation or\n modification of the work as a means of enforcing, against the work's\n users, your or third parties' legal rights to forbid circumvention of\n technological measures.\n .\n   4. Conveying Verbatim Copies.\n .\n   You may convey verbatim copies of the Program's source code as you\n receive it, in any medium, provided that you conspicuously and\n appropriately publish on each copy an appropriate copyright notice;\n keep intact all notices stating that this License and any\n non-permissive terms added in accord with section 7 apply to the code;\n keep intact all notices of the absence of any warranty; and give all\n recipients a copy of this License along with the Program.\n .\n   You may charge any price or no price for each copy that you convey,\n and you may offer support or warranty protection for a fee.\n .\n   5. Conveying Modified Source Versions.\n .\n   You may convey a work based on the Program, or the modifications to\n produce it from the Program, in the form of source code under the\n terms of section 4, provided that you also meet all of these conditions:\n .\n     a) The work must carry prominent notices stating that you modified\n     it, and giving a relevant date.\n .\n     b) The work must carry prominent notices stating that it is\n     released under this License and any conditions added under section\n     7.  This requirement modifies the requirement in section 4 to\n     \"keep intact all notices\".\n .\n     c) You must license the entire work, as a whole, under this\n     License to anyone who comes into possession of a copy.  This\n     License will therefore apply, along with any applicable section 7\n     additional terms, to the whole of the work, and all its parts,\n     regardless of how they are packaged.  This License gives no\n     permission to license the work in any other way, but it does not\n     invalidate such permission if you have separately received it.\n .\n     d) If the work has interactive user interfaces, each must display\n     Appropriate Legal Notices; however, if the Program has interactive\n     interfaces that do not display Appropriate Legal Notices, your\n     work need not make them do so.\n .\n   A compilation of a covered work with other separate and independent\n works, which are not by their nature extensions of the covered work,\n and which are not combined with it such as to form a larger program,\n in or on a volume of a storage or distribution medium, is called an\n \"aggregate\" if the compilation and its resulting copyright are not\n used to limit the access or legal rights of the compilation's users\n beyond what the individual works permit.  Inclusion of a covered work\n in an aggregate does not cause this License to apply to the other\n parts of the aggregate.\n .\n   6. Conveying Non-Source Forms.\n .\n   You may convey a covered work in object code form under the terms\n of sections 4 and 5, provided that you also convey the\n machine-readable Corresponding Source under the terms of this License,\n in one of these ways:\n .\n     a) Convey the object code in, or embodied in, a physical product\n     (including a physical distribution medium), accompanied by the\n     Corresponding Source fixed on a durable physical medium\n     customarily used for software interchange.\n .\n     b) Convey the object code in, or embodied in, a physical product\n     (including a physical distribution medium), accompanied by a\n     written offer, valid for at least three years and valid for as\n     long as you offer spare parts or customer support for that product\n     model, to give anyone who possesses the object code either (1) a\n     copy of the Corresponding Source for all the software in the\n     product that is covered by this License, on a durable physical\n     medium customarily used for software interchange, for a price no\n     more than your reasonable cost of physically performing this\n     conveying of source, or (2) access to copy the\n     Corresponding Source from a network server at no charge.\n .\n     c) Convey individual copies of the object code with a copy of the\n     written offer to provide the Corresponding Source.  This\n     alternative is allowed only occasionally and noncommercially, and\n     only if you received the object code with such an offer, in accord\n     with subsection 6b.\n .\n     d) Convey the object code by offering access from a designated\n     place (gratis or for a charge), and offer equivalent access to the\n     Corresponding Source in the same way through the same place at no\n     further charge.  You need not require recipients to copy the\n     Corresponding Source along with the object code.  If the place to\n     copy the object code is a network server, the Corresponding Source\n     may be on a different server (operated by you or a third party)\n     that supports equivalent copying facilities, provided you maintain\n     clear directions next to the object code saying where to find the\n     Corresponding Source.  Regardless of what server hosts the\n     Corresponding Source, you remain obligated to ensure that it is\n     available for as long as needed to satisfy these requirements.\n .\n     e) Convey the object code using peer-to-peer transmission, provided\n     you inform other peers where the object code and Corresponding\n     Source of the work are being offered to the general public at no\n     charge under subsection 6d.\n .\n   A separable portion of the object code, whose source code is excluded\n from the Corresponding Source as a System Library, need not be\n included in conveying the object code work.\n .\n   A \"User Product\" is either (1) a \"consumer product\", which means any\n tangible personal property which is normally used for personal, family,\n or household purposes, or (2) anything designed or sold for incorporation\n into a dwelling.  In determining whether a product is a consumer product,\n doubtful cases shall be resolved in favor of coverage.  For a particular\n product received by a particular user, \"normally used\" refers to a\n typical or common use of that class of product, regardless of the status\n of the particular user or of the way in which the particular user\n actually uses, or expects or is expected to use, the product.  A product\n is a consumer product regardless of whether the product has substantial\n commercial, industrial or non-consumer uses, unless such uses represent\n the only significant mode of use of the product.\n .\n   \"Installation Information\" for a User Product means any methods,\n procedures, authorization keys, or other information required to install\n and execute modified versions of a covered work in that User Product from\n a modified version of its Corresponding Source.  The information must\n suffice to ensure that the continued functioning of the modified object\n code is in no case prevented or interfered with solely because\n modification has been made.\n .\n   If you convey an object code work under this section in, or with, or\n specifically for use in, a User Product, and the conveying occurs as\n part of a transaction in which the right of possession and use of the\n User Product is transferred to the recipient in perpetuity or for a\n fixed term (regardless of how the transaction is characterized), the\n Corresponding Source conveyed under this section must be accompanied\n by the Installation Information.  But this requirement does not apply\n if neither you nor any third party retains the ability to install\n modified object code on the User Product (for example, the work has\n been installed in ROM).\n .\n   The requirement to provide Installation Information does not include a\n requirement to continue to provide support service, warranty, or updates\n for a work that has been modified or installed by the recipient, or for\n the User Product in which it has been modified or installed.  Access to a\n network may be denied when the modification itself materially and\n adversely affects the operation of the network or violates the rules and\n protocols for communication across the network.\n .\n   Corresponding Source conveyed, and Installation Information provided,\n in accord with this section must be in a format that is publicly\n documented (and with an implementation available to the public in\n source code form), and must require no special password or key for\n unpacking, reading or copying.\n .\n   7. Additional Terms.\n .\n   \"Additional permissions\" are terms that supplement the terms of this\n License by making exceptions from one or more of its conditions.\n Additional permissions that are applicable to the entire Program shall\n be treated as though they were included in this License, to the extent\n that they are valid under applicable law.  If additional permissions\n apply only to part of the Program, that part may be used separately\n under those permissions, but the entire Program remains governed by\n this License without regard to the additional permissions.\n .\n   When you convey a copy of a covered work, you may at your option\n remove any additional permissions from that copy, or from any part of\n it.  (Additional permissions may be written to require their own\n removal in certain cases when you modify the work.)  You may place\n additional permissions on material, added by you to a covered work,\n for which you have or can give appropriate copyright permission.\n .\n   Notwithstanding any other provision of this License, for material you\n add to a covered work, you may (if authorized by the copyright holders of\n that material) supplement the terms of this License with terms:\n .\n     a) Disclaiming warranty or limiting liability differently from the\n     terms of sections 15 and 16 of this License; or\n .\n     b) Requiring preservation of specified reasonable legal notices or\n     author attributions in that material or in the Appropriate Legal\n     Notices displayed by works containing it; or\n .\n     c) Prohibiting misrepresentation of the origin of that material, or\n     requiring that modified versions of such material be marked in\n     reasonable ways as different from the original version; or\n .\n     d) Limiting the use for publicity purposes of names of licensors or\n     authors of the material; or\n .\n     e) Declining to grant rights under trademark law for use of some\n     trade names, trademarks, or service marks; or\n .\n     f) Requiring indemnification of licensors and authors of that\n     material by anyone who conveys the material (or modified versions of\n     it) with contractual assumptions of liability to the recipient, for\n     any liability that these contractual assumptions directly impose on\n     those licensors and authors.\n .\n   All other non-permissive additional terms are considered \"further\n restrictions\" within the meaning of section 10.  If the Program as you\n received it, or any part of it, contains a notice stating that it is\n governed by this License along with a term that is a further\n restriction, you may remove that term.  If a license document contains\n a further restriction but permits relicensing or conveying under this\n License, you may add to a covered work material governed by the terms\n of that license document, provided that the further restriction does\n not survive such relicensing or conveying.\n .\n   If you add terms to a covered work in accord with this section, you\n must place, in the relevant source files, a statement of the\n additional terms that apply to those files, or a notice indicating\n where to find the applicable terms.\n .\n   Additional terms, permissive or non-permissive, may be stated in the\n form of a separately written license, or stated as exceptions;\n the above requirements apply either way.\n .\n   8. Termination.\n .\n   You may not propagate or modify a covered work except as expressly\n provided under this License.  Any attempt otherwise to propagate or\n modify it is void, and will automatically terminate your rights under\n this License (including any patent licenses granted under the third\n paragraph of section 11).\n .\n   However, if you cease all violation of this License, then your\n license from a particular copyright holder is reinstated (a)\n provisionally, unless and until the copyright holder explicitly and\n finally terminates your license, and (b) permanently, if the copyright\n holder fails to notify you of the violation by some reasonable means\n prior to 60 days after the cessation.\n .\n   Moreover, your license from a particular copyright holder is\n reinstated permanently if the copyright holder notifies you of the\n violation by some reasonable means, this is the first time you have\n received notice of violation of this License (for any work) from that\n copyright holder, and you cure the violation prior to 30 days after\n your receipt of the notice.\n .\n   Termination of your rights under this section does not terminate the\n licenses of parties who have received copies or rights from you under\n this License.  If your rights have been terminated and not permanently\n reinstated, you do not qualify to receive new licenses for the same\n material under section 10.\n .\n   9. Acceptance Not Required for Having Copies.\n .\n   You are not required to accept this License in order to receive or\n run a copy of the Program.  Ancillary propagation of a covered work\n occurring solely as a consequence of using peer-to-peer transmission\n to receive a copy likewise does not require acceptance.  However,\n nothing other than this License grants you permission to propagate or\n modify any covered work.  These actions infringe copyright if you do\n not accept this License.  Therefore, by modifying or propagating a\n covered work, you indicate your acceptance of this License to do so.\n .\n   10. Automatic Licensing of Downstream Recipients.\n .\n   Each time you convey a covered work, the recipient automatically\n receives a license from the original licensors, to run, modify and\n propagate that work, subject to this License.  You are not responsible\n for enforcing compliance by third parties with this License.\n .\n   An \"entity transaction\" is a transaction transferring control of an\n organization, or substantially all assets of one, or subdividing an\n organization, or merging organizations.  If propagation of a covered\n work results from an entity transaction, each party to that\n transaction who receives a copy of the work also receives whatever\n licenses to the work the party's predecessor in interest had or could\n give under the previous paragraph, plus a right to possession of the\n Corresponding Source of the work from the predecessor in interest, if\n the predecessor has it or can get it with reasonable efforts.\n .\n   You may not impose any further restrictions on the exercise of the\n rights granted or affirmed under this License.  For example, you may\n not impose a license fee, royalty, or other charge for exercise of\n rights granted under this License, and you may not initiate litigation\n (including a cross-claim or counterclaim in a lawsuit) alleging that\n any patent claim is infringed by making, using, selling, offering for\n sale, or importing the Program or any portion of it.\n .\n   11. Patents.\n .\n   A \"contributor\" is a copyright holder who authorizes use under this\n License of the Program or a work on which the Program is based.  The\n work thus licensed is called the contributor's \"contributor version\".\n .\n   A contributor's \"essential patent claims\" are all patent claims\n owned or controlled by the contributor, whether already acquired or\n hereafter acquired, that would be infringed by some manner, permitted\n by this License, of making, using, or selling its contributor version,\n but do not include claims that would be infringed only as a\n consequence of further modification of the contributor version.  For\n purposes of this definition, \"control\" includes the right to grant\n patent sublicenses in a manner consistent with the requirements of\n this License.\n .\n   Each contributor grants you a non-exclusive, worldwide, royalty-free\n patent license under the contributor's essential patent claims, to\n make, use, sell, offer for sale, import and otherwise run, modify and\n propagate the contents of its contributor version.\n .\n   In the following three paragraphs, a \"patent license\" is any express\n agreement or commitment, however denominated, not to enforce a patent\n (such as an express permission to practice a patent or covenant not to\n sue for patent infringement).  To \"grant\" such a patent license to a\n party means to make such an agreement or commitment not to enforce a\n patent against the party.\n .\n   If you convey a covered work, knowingly relying on a patent license,\n and the Corresponding Source of the work is not available for anyone\n to copy, free of charge and under the terms of this License, through a\n publicly available network server or other readily accessible means,\n then you must either (1) cause the Corresponding Source to be so\n available, or (2) arrange to deprive yourself of the benefit of the\n patent license for this particular work, or (3) arrange, in a manner\n consistent with the requirements of this License, to extend the patent\n license to downstream recipients.  \"Knowingly relying\" means you have\n actual knowledge that, but for the patent license, your conveying the\n covered work in a country, or your recipient's use of the covered work\n in a country, would infringe one or more identifiable patents in that\n country that you have reason to believe are valid.\n .\n   If, pursuant to or in connection with a single transaction or\n arrangement, you convey, or propagate by procuring conveyance of, a\n covered work, and grant a patent license to some of the parties\n receiving the covered work authorizing them to use, propagate, modify\n or convey a specific copy of the covered work, then the patent license\n you grant is automatically extended to all recipients of the covered\n work and works based on it.\n .\n   A patent license is \"discriminatory\" if it does not include within\n the scope of its coverage, prohibits the exercise of, or is\n conditioned on the non-exercise of one or more of the rights that are\n specifically granted under this License.  You may not convey a covered\n work if you are a party to an arrangement with a third party that is\n in the business of distributing software, under which you make payment\n to the third party based on the extent of your activity of conveying\n the work, and under which the third party grants, to any of the\n parties who would receive the covered work from you, a discriminatory\n patent license (a) in connection with copies of the covered work\n conveyed by you (or copies made from those copies), or (b) primarily\n for and in connection with specific products or compilations that\n contain the covered work, unless you entered into that arrangement,\n or that patent license was granted, prior to 28 March 2007.\n .\n   Nothing in this License shall be construed as excluding or limiting\n any implied license or other defenses to infringement that may\n otherwise be available to you under applicable patent law.\n .\n   12. No Surrender of Others' Freedom.\n .\n   If conditions are imposed on you (whether by court order, agreement or\n otherwise) that contradict the conditions of this License, they do not\n excuse you from the conditions of this License.  If you cannot convey a\n covered work so as to satisfy simultaneously your obligations under this\n License and any other pertinent obligations, then as a consequence you may\n not convey it at all.  For example, if you agree to terms that obligate you\n to collect a royalty for further conveying from those to whom you convey\n the Program, the only way you could satisfy both those terms and this\n License would be to refrain entirely from conveying the Program.\n .\n   13. Remote Network Interaction; Use with the GNU General Public License.\n .\n   Notwithstanding any other provision of this License, if you modify the\n Program, your modified version must prominently offer all users\n interacting with it remotely through a computer network (if your version\n supports such interaction) an opportunity to receive the Corresponding\n Source of your version by providing access to the Corresponding Source\n from a network server at no charge, through some standard or customary\n means of facilitating copying of software.  This Corresponding Source\n shall include the Corresponding Source for any work covered by version 3\n of the GNU General Public License that is incorporated pursuant to the\n following paragraph.\n .\n   Notwithstanding any other provision of this License, you have\n permission to link or combine any covered work with a work licensed\n under version 3 of the GNU General Public License into a single\n combined work, and to convey the resulting work.  The terms of this\n License will continue to apply to the part which is the covered work,\n but the work with which it is combined will remain governed by version\n 3 of the GNU General Public License.\n .\n   14. Revised Versions of this License.\n .\n   The Free Software Foundation may publish revised and/or new versions of\n the GNU Affero General Public License from time to time.  Such new versions\n will be similar in spirit to the present version, but may differ in detail to\n address new problems or concerns.\n .\n   Each version is given a distinguishing version number.  If the\n Program specifies that a certain numbered version of the GNU Affero General\n Public License \"or any later version\" applies to it, you have the\n option of following the terms and conditions either of that numbered\n version or of any later version published by the Free Software\n Foundation.  If the Program does not specify a version number of the\n GNU Affero General Public License, you may choose any version ever published\n by the Free Software Foundation.\n .\n   If the Program specifies that a proxy can decide which future\n versions of the GNU Affero General Public License can be used, that proxy's\n public statement of acceptance of a version permanently authorizes you\n to choose that version for the Program.\n .\n   Later license versions may give you additional or different\n permissions.  However, no additional obligations are imposed on any\n author or copyright holder as a result of your choosing to follow a\n later version.\n .\n   15. Disclaimer of Warranty.\n .\n   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\n APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\n HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\n OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\n THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\n IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\n ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n .\n   16. Limitation of Liability.\n .\n   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\n THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\n GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\n USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\n DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\n PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\n EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\n SUCH DAMAGES.\n .\n   17. Interpretation of Sections 15 and 16.\n .\n   If the disclaimer of warranty and limitation of liability provided\n above cannot be given local legal effect according to their terms,\n reviewing courts shall apply local law that most closely approximates\n an absolute waiver of all civil liability in connection with the\n Program, unless a warranty or assumption of liability accompanies a\n copy of the Program in return for a fee.\n .\n                      END OF TERMS AND CONDITIONS\n .\n             How to Apply These Terms to Your New Programs\n .\n   If you develop a new program, and you want it to be of the greatest\n possible use to the public, the best way to achieve this is to make it\n free software which everyone can redistribute and change under these terms.\n .\n   To do so, attach the following notices to the program.  It is safest\n to attach them to the start of each source file to most effectively\n state the exclusion of warranty; and each file should have at least\n the \"copyright\" line and a pointer to where the full notice is found.\n .\n     <one line to give the program's name and a brief idea of what it does.>\n     Copyright (C) <year>  <name of author>\n .\n     This program is free software: you can redistribute it and/or modify\n     it under the terms of the GNU Affero General Public License as published\n     by the Free Software Foundation, either version 3 of the License, or\n     (at your option) any later version.\n .\n     This program is distributed in the hope that it will be useful,\n     but WITHOUT ANY WARRANTY; without even the implied warranty of\n     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n     GNU Affero General Public License for more details.\n .\n     You should have received a copy of the GNU Affero General Public License\n     along with this program.  If not, see <https://www.gnu.org/licenses/>.\n .\n Also add information on how to contact you by electronic and paper mail.\n .\n   If your software can interact with users remotely through a computer\n network, you should also make sure that it provides a way for users to\n get its source.  For example, if your program is a web application, its\n interface could display a \"Source\" link that leads users to an archive\n of the code.  There are many ways you could offer source, and different\n solutions will be better for different programs; see section 13 for the\n specific requirements.\n .\n   You should also get your employer (if you work as a programmer) or school,\n if any, to sign a \"copyright disclaimer\" for the program, if necessary.\n For more information on this, and how to apply and follow the GNU AGPL, see\n <https://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "debian/fastcfs-api-dev.install",
    "content": "usr/include/fastcfs/api/*"
  },
  {
    "path": "debian/fastcfs-api-libs.install",
    "content": "usr/lib/libfcfsapi.so*"
  },
  {
    "path": "debian/fastcfs-api-tests.install",
    "content": "usr/bin/fcfs_beachmark\nusr/bin/fcfs_test_file_op\nusr/bin/fcfs_test_file_copy\nusr/bin/fcfs_test_papi_copy\nusr/bin/fcfs_test_read_ahead"
  },
  {
    "path": "debian/fastcfs-auth-client.install",
    "content": "usr/lib/libfcfsauthclient.so*\nusr/bin/fcfs_user\nusr/bin/fcfs_pool\nusr/bin/fauth_list_servers\nusr/bin/fauth_cluster_stat\n"
  },
  {
    "path": "debian/fastcfs-auth-config.install",
    "content": "etc/fastcfs/auth/*.conf\netc/fastcfs/auth/keys/*\n"
  },
  {
    "path": "debian/fastcfs-auth-dev.install",
    "content": "usr/include/fastcfs/auth/*\n"
  },
  {
    "path": "debian/fastcfs-auth-server-config.install",
    "content": "usr/lib/systemd/system/fastauth.service"
  },
  {
    "path": "debian/fastcfs-auth-server.dirs",
    "content": "opt/fastcfs/auth"
  },
  {
    "path": "debian/fastcfs-auth-server.install",
    "content": "usr/bin/fcfs_authd"
  },
  {
    "path": "debian/fastcfs-fuse-config.install",
    "content": "etc/fastcfs/fcfs/*.conf"
  },
  {
    "path": "debian/fastcfs-fused.dirs",
    "content": "opt/fastcfs/fuse\nopt/fastcfs/fcfs"
  },
  {
    "path": "debian/fastcfs-fused.install",
    "content": "usr/bin/fcfs_fused"
  },
  {
    "path": "debian/fastcfs-utils.install",
    "content": "usr/bin/fcfs_active_test\nusr/bin/fcfs_pool_stat"
  },
  {
    "path": "debian/fastcfs-vote-client.install",
    "content": "usr/lib/libfcfsvoteclient.so*\nusr/bin/fvote_cluster_stat\n"
  },
  {
    "path": "debian/fastcfs-vote-config.install",
    "content": "etc/fastcfs/vote/*.conf\n"
  },
  {
    "path": "debian/fastcfs-vote-dev.install",
    "content": "usr/include/fastcfs/vote/*\n"
  },
  {
    "path": "debian/fastcfs-vote-server.dirs",
    "content": "opt/fastcfs/vote"
  },
  {
    "path": "debian/fastcfs-vote-server.install",
    "content": "usr/bin/fcfs_voted"
  },
  {
    "path": "debian/rules",
    "content": "#!/usr/bin/make -f\n\nexport DESTDIR = $(CURDIR)/debian/tmp\nexport FUSE_CONFDIR=$(DESTDIR)/etc/fastcfs/fcfs/\nexport AUTH_CONFDIR=$(DESTDIR)/etc/fastcfs/auth/\nexport VOTE_CONFDIR=$(DESTDIR)/etc/fastcfs/vote/\n\n%:\n\tdh $@\n\noverride_dh_auto_build:\nifneq ($(filter pkg.fastcfs.core,$(DEB_BUILD_PROFILES)),)\n\t./make.sh --exclude=client clean && ./make.sh --exclude=client\nendif\nifneq ($(filter pkg.auth.client,$(DEB_BUILD_PROFILES)),)\n\t./make.sh --module=auth_client clean && ./make.sh --module=auth_client\nendif\nifneq ($(filter pkg.vote.client,$(DEB_BUILD_PROFILES)),)\n\t./make.sh --module=vote_client clean && ./make.sh --module=vote_client\nendif\n\t\n\noverride_dh_auto_install:\nifneq ($(filter pkg.fastcfs.core,$(DEB_BUILD_PROFILES)),)\n\t./make.sh --exclude=client install\n\tmkdir -p $(FUSE_CONFDIR)\n\tcp conf/*.conf $(FUSE_CONFDIR)\n\tcp systemd/fastcfs.service debian/fastcfs-fused.fastcfs.service\n\tcp systemd/fastauth.service debian/fastcfs-auth-server.fastauth.service\n\tcp systemd/fastvote.service debian/fastcfs-vote-server.fastvote.service\nendif\nifneq ($(filter pkg.auth.client,$(DEB_BUILD_PROFILES)),)\n\t./make.sh --module=auth_client install\n\tAUTH_CONFDIR=$(DESTDIR)/etc/fastcfs/auth/\n\tmkdir -p $(AUTH_CONFDIR)\n\tcp src/auth/conf/*.conf $(AUTH_CONFDIR)\n\tcp -R src/auth/conf/keys $(AUTH_CONFDIR)\nendif\nifneq ($(filter pkg.vote.client,$(DEB_BUILD_PROFILES)),)\n\t./make.sh --module=vote_client install\n\tVOTE_CONFDIR=%{buildroot}/etc/fastcfs/vote/\n\tmkdir -p $(VOTE_CONFDIR)\n\tcp src/vote/conf/*.conf $(VOTE_CONFDIR)\nendif\n\tdh_auto_install\n\noverride_dh_installsystemd:\n\tdh_installsystemd --package=fastcfs-fused --name=fastcfs --no-start --no-restart-on-upgrade\n\tdh_installsystemd --package=fastcfs-auth-server --name=fastauth --no-start --no-restart-on-upgrade\n\tdh_installsystemd --package=fastcfs-vote-server --name=fastvote --no-start --no-restart-on-upgrade\n\n.PHONY: override_dh_gencontrol\noverride_dh_gencontrol:\n\tdh_gencontrol -- -Tdebian/substvars\n"
  },
  {
    "path": "debian/source/format",
    "content": "3.0 (quilt)\n"
  },
  {
    "path": "debian/substvars",
    "content": "libfastcommon:Version=1.0.83\nlibserverframe:Version=1.2.11\nfastcfs-auth-config:Version=2.0.0\nfastcfs-vote-config:Version=3.4.0\nfastdir-client:Version=5.5.0\nfaststore-config:Version=1.0.0\nfaststore-client:Version=5.5.0\nfastcfs-fuse-config:Version=1.0.0\n"
  },
  {
    "path": "debian/watch",
    "content": "version=3\nopts=\"mode=git\" https://github.com/happyfish100/FastCFS.git \\\n   refs/tags/v([\\d\\.]+) debian uupdate\n"
  },
  {
    "path": "docs/APT-INSTALL-zh_CN.md",
    "content": "\n## 支持Debian 10及以上版本，Ubuntu 18.04及以上版本 和 Deepin 20及以上版本，amd64 和arm64 两种架构。\n\n\n### 1. 配置 apt 存储库\n\n配置 apt 存储库和签名密钥，以使系统的包管理器启用自动更新。\n\n```shell\nsudo apt-get install curl gpg\ncurl http://www.fastken.cn/aptrepo/packages.fastos.pub | gpg --dearmor > fastos-archive-keyring.gpg\nsudo install -D -o root -g root -m 644 fastos-archive-keyring.gpg /usr/share/keyrings/fastos-archive-keyring.gpg\nsudo 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'\nrm -f fastos-archive-keyring.gpg\n```\n\n如果需要安装调试包，执行：\n```shell\nsudo 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'\n```\n\n\n然后更新包缓存\n```\nsudo apt update\n```\n\n### 2. fastDIR server安装\n\n在需要运行 fastDIR server的服务器上执行：\n```shell\nsudo apt install fastdir-server -y\n```\n\n### 3. faststore server安装\n\n在需要运行 faststore server的服务器上执行：\n```shell\nsudo apt install faststore-server -y\n```\n\n### 4. FastCFS客户端安装\n\n在需要使用FastCFS存储服务的机器（即FastCFS客户端）上执行：\n```shell\n# 注：fastcfs-fused依赖fuse3\nsudo apt install fastcfs-fused -y\n```\n\n### 5. Vote server安装（可选）\n\nVote server作为FastCFS多个服务模块共用的选举节点，主要作用是实现双副本防脑裂（即双活互备防脑裂）。\n\n在需要运行选举节点的服务器上执行：\n```shell\nsudo apt install fastcfs-vote-server -y\n```\n\n### 6. Auth server安装（可选）\n\n如果需要存储池或访问控制，则需要安装本模块。\n\n在需要运行 Auth server的服务器上执行：\n```shell\nsudo apt install fastcfs-auth-server -y\n```\n\n### 7. 集群配置（必须）\n\n安装完成后，需要修改对应的配置文件，服务才可以正常启动。请参阅[配置指南](CONFIGURE-zh_CN.md)\n\n\n### 8. 启动\n\nFastCFS后台程序可通过systemd管理。systemd服务名称如下：\n\n  * fastdir： 目录服务，对应程序为 fdir_serverd\n  * faststore：存储服务，对应程序为 fs_serverd\n  * fastcfs： FUSE后台服务，对应程序为 fcfs_fused\n  * fastvote： 选举节点，对应程序为 fcfs_voted\n  * fastauth： 认证服务，对应程序为 fcfs_authd\n\n可以使用标准的systemd命令对上述5个服务进行管理，例如：\n```shell\nsudo systemctl restart fastdir\nsudo systemctl restart faststore\nsudo systemctl restart fastcfs\nsudo systemctl restart fastvote\nsudo systemctl restart fastauth\n```\n"
  },
  {
    "path": "docs/AUTH-zh_CN.md",
    "content": "\n# Auth (认证模块)配置及运行\n\n如果你不需要使用存储池或访问权限控制，可以跳过本文档。\n\n本文档以FastCFS RPM包设定的路径（配置文件目录和程序工作目录等）进行说明，如果你采用自助编译安装方式的话，请自行对应。\n\n为了防脑裂，建议配置3个节点（服务器），因认证服务占用资源较少，可以和其他服务共用服务器。\n\n## 1. Auth配置文件目录结构\n\n```\n/etc/fastcfs/\n        |\n        |__ auth: 认证中心\n             |__ keys: 存放用户密钥文件，每个用户对应一个密钥文件，例如 admin.key\n             |    |__ session_validate.key: 用于FastDIR和FastStore请求auth服务验证session和权限\n             |__ cluster.conf: 服务器列表，配置服务器ID、IP和端口\n             |__ server.conf: fcfs_authd对应的配置文件\n             |__ client.conf: 客户端配置文件\n             |__ auth.conf: 认证相关的公共配置文件，在FastDIR和FastStore的cluster.conf中引用\n             |__ session.conf: session相关配置文件，在Auth、FastDIR和FastStore的server.conf中引用\n```\n\n\n## 2. authd程序工作目录\n\n```\n/opt/fastcfs/\n        |\n        |__ auth\n             |__ authd.pid: 服务进程fcfs_authd的pid文件\n             |__ logs: 日志文件目录\n                  |__ fcfs_authd.log: 错误日志\n                  |__ slow.log: 慢查询日志\n```\n\n开启认证功能需要设置认证中心、FastDIR server、FastStore server和FastCFS客户端。\n\n## 3. 认证中心（authd server）配置\n\n配置文件路径：/etc/fastcfs/auth\n\nAuth集群内各个server配置的cluster.conf必须完全一样。\n\n建议配置一次，分发到其他服务器即可。\n\n### 3.1 把Auth集群中的所有服务实例配置到cluster.conf中\n\n每个Auth服务实例包含2个服务端口：cluster 和 service\n\n一个Auth服务实例需要配置一个[server-$id]的section，其中$id为实例ID。\n\n### 3.2 配置 server.conf\n\n* [cluster] 和 [service] 配置的端口（port）必须与cluster.conf中本机的一致，否则启动会报错\n\n### 3.3 配置 auth.conf\n\n将 auth_enabled 设置为 true 以开启认证功能\n\n### 3.4 复制FastDIR server上的如下配置文件到 /etc/fastcfs/fdir/\n\n```\n/etc/fastcfs/fdir/client.conf\n/etc/fastcfs/fdir/cluster.conf\n```\n\n### 3.5 启动authd\n\nauthd命令直接重启：\n\n```\n/usr/bin/fcfs_authd /etc/fastcfs/auth/server.conf restart\n```\n\n或者系统服务命令启动：\n\n```\nsudo systemctl restart fastauth\n```\n\n查看日志：\n\n```\ntail /opt/fastcfs/auth/logs/fcfs_authd.log\n```\n\n* 第一次运行将自动创建 admin 用户，默认生成的密钥文件名为 **/etc/fastcfs/auth/keys/admin.key**\n* 友情提示：只在master上生成该密钥文件，请在master上分发密钥文件。\n\n### 3.6 创建存储池\n\n创建名为 fs 的存储池，配额无限制：\n\n```\nfcfs_pool create fs unlimited\n```\n\n**_注：存储池名称必须和FastCFS fuse客户端配置文件fuse.conf中的命名空间一致（缺省配置为fs，可按需修改）_**\n\n## 4. FastDIR server\n\n### 4.1 复制auth server上的如下配置文件到 /etc/fastcfs/auth/\n\n```\n/etc/fastcfs/auth/auth.conf\n/etc/fastcfs/auth/session.conf\n/etc/fastcfs/auth/cluster.conf\n/etc/fastcfs/auth/client.conf\n```\n\n### 4.2 复制auth server上的如下密钥文件到 /etc/fastcfs/auth/keys/\n\n```\n/etc/fastcfs/auth/keys/admin.key\n/etc/fastcfs/auth/keys/session_validate.key\n```\n\n拷贝完成后重启FastDIR服务（fdir_serverd）\n\n## 5. FastStore server\n\n* 参见 4. FastDIR server 部分\n\n\n拷贝完成后重启FastStore服务（fs_serverd）\n\n## 6. FastCFS客户端（fused）\n\n* 参见 4. FastDIR server 部分\n\n拷贝完成后重启fuse服务（fcfs_fused）\n\n## 友情提示\n\n* 4 ~ 6部分的配置及启动参见 [配置指南](CONFIGURE-zh_CN.md)\n\n\n## 7. 命令行工具\n\n* fcfs_user：用户管理，主要包括创建用户、删除用户、设置用户权限（权限包括用户管理、创建存储池等等）\n\n* fcfs_pool：储存池管理，主要包括创建pool、删除pool、把pool读写权限授权给其他用户\n\n友情提示：直接执行上述命令可以查看使用帮助。\n\n## 8. 注意事项\n\n* Auth server依赖FastDIR server，需要先启动FastDIR server，然后启动Auth server。\n"
  },
  {
    "path": "docs/CONFIGURE-zh_CN.md",
    "content": "\n# 配置及运行\n\n本文档以FastCFS RPM包设定的路径（配置文件目录和程序工作目录等）进行说明，如果你采用自助编译安装方式的话，请自行对应。\n\nFastCFS集群配置包含如下五部分：\n\n* fastDIR server（服务实例）配置\n* faststore server（服务实例）配置\n* FastCFS客户端配置\n* 选举节点配置（可选）\n* 认证配置（可选）\n\n\n## 1. 配置文件目录结构\n\n```\n/etc/fastcfs/\n        |\n        |__ fcfs: fused服务\n        |    |__ fuse.conf: fcfs_fused对应的配置文件\n        |\n        |__ fdir: FastDIR目录服务\n        |    |__ cluster.conf: 服务器列表，配置服务器ID、IP和端口\n        |    |__ server.conf: fdir_serverd对应的配置文件\n        |    |__ storage.conf: 持久化存储配置文件\n        |    |__ client.conf: 客户端配置文件\n        |\n        |__ fstore: faststore存储服务\n             |__ cluster.conf: 服务器列表(配置服务器ID、IP和端口)，并配置服务器分组及数据分组之间的对应关系\n             |__ storage.conf: 存储路径及空间分配和回收配置\n             |__ server.conf: fs_serverd对应的配置文件\n             |__ client.conf: 客户端配置文件\n```\n\n## 2. 程序工作目录\n\n```\n/opt/fastcfs/\n        |\n        |__ fcfs\n        |    |__ fused.pid: 服务进程fcfs_fused的pid文件\n        |    |__ logs: 日志文件目录\n        |         |__ fcfs_fused.log: 错误日志\n        |\n        |__ fdir\n        |    |__ serverd.pid: 服务进程fdir_serverd的pid文件\n        |    |__ data: 系统数据文件目录，包含集群拓扑结构和binlog\n        |    |    |__ cluster.info: 集群拓扑信息\n        |    |    |__ .inode.sn: 当前inode顺序号\n        |    |    |__ binlog: 存放binlog文件\n        |    |\n        |    |__ db: 存储引擎默认数据目录\n        |    |    |__ trunk: trunk binlog\n        |    |    |__ inode: segment索引（一个segment包含多个inode，最多65536个，segment id顺序递增）\n        |    |    |__ ####(如0001、0002等等): 以trunk file方式存放inode数据\n        |    |\n        |    |__ logs: 日志文件目录\n        |         |__ fdir_serverd.log: 错误日志\n        |         |__ slow.log: 慢查询日志\n        |\n        |__ fstore\n              |__ serverd.pid: 服务进程fs_serverd的pid文件\n              |__ data: 系统数据文件目录，包含集群拓扑结构和binlog\n              |    |__ data_group.info: 集群数据分组信息\n              |    |__ .store_path_index.dat: 存储路径索引\n              |    |__ .trunk_id.dat: 当前trunk id信息\n              |    |__ replica: replica binlog，一个DG对应一个子目录\n              |    |__ slice: slice binlog\n              |    |__ trunk: trunk binlog\n              |    |__ recovery: slave追加master历史数据，一个DG对应一个子目录\n              |    |__ migrate: 迁移出去的DG数据清理\n              |    |__ rebuild: 单盘数据恢复\n              |\n              |__ db: slice索引存储引擎默认数据目录\n              |    |__ trunk: trunk binlog\n              |    |__ block: segment索引（一个segment包含多个block，segment id哈希分布）\n              |    |__ ####(如0001、0002等等): 以trunk file方式存放block索引数据\n              |\n              |__ logs: 日志文件目录\n                   |__ fs_serverd.log: 错误日志\n                   |__ slow.log: 慢查询日志\n```\n\n## 3. fastDIR server（服务实例）配置\n\n配置文件路径：\n\n> /etc/fastcfs/fdir\n\nfastDIR集群内各个server配置的cluster.conf必须完全一样，建议配置一次，分发到集群中的所有服务器和客户端即可。\n\n### 3.1 把fastDIR集群中的所有服务实例配置到cluster.conf中\n\n每个 **fastDIR** 服务实例包含2个服务端口：**cluster** 和 **service**；\n\n**cluster.conf** 中配置集群所有实例，一个实例由IP + 端口（包括 cluster和service 2个端口 ）组成；\n\n一个fastDIR服务实例需要配置一个[server-$id]的section，其中$id为实例ID（从1开始编号）；\n\n如果一台服务器上启动了多个实例，因端口与全局配置的不一致，此时必须指定端口。\n\n一个服务实例的配置示例如下：\n\n```\n[server-3]\ncluster-port = 11015\nservice-port = 11016\nhost = 172.16.168.128\n```\n\n### 3.2 配置 server.conf\n\n* 如果需要修改数据存放路径，修改配置项 data_path 为绝对路径\n* 本机端口包含cluster和service 2个端口，分配在[cluster] 和 [service] 中配置\n* 本机IP + 本机端口必须配置在cluster.conf的一个实例中，否则启动时会出现下面类似的出错信息\n```\nERROR - file: cluster_info.c, line: 119, cluster config file: /etc/fastcfs/fdir/cluster.conf,\n      can't find myself by my local ip and listen port\n```\n\n* V3.0支持的持久化特性默认是关闭的，在 [storage-engine] 中设置，详情参见源码的conf目录下的配置示例。\n启用存储引擎配置示例：\n\n```\n[storage-engine]\n# if enable the storage engine\n### false: use binlog directly\n### true: use storage engine for massive files\n# default value is false\nenabled = true\n\n# the config filename for storage\nstorage_config_filename = storage.conf\n\n# the path to store the data files\n# can be an absolute path or a relative path\n# the relative path for sub directory under the base_path\n# this path will be created auto when not exist\n# default value is db\ndata_path = db\n\n# the memory limit ratio for dentry\n# the valid limit range is [1%, 99%]\n# default value is 80%\nmemory_limit = 80%\n```\n\n### 3.3 配置 storage.conf\n\n* 开启持久化存储特性的情况下，才需要配置storage.conf\n* 通常采用默认设置即可\n\nfastDIR重启：\n\n```\n/usr/bin/fdir_serverd /etc/fastcfs/fdir/server.conf restart\n```\n或者：\n\n```\nsudo systemctl restart fastdir\n```\n\n查看日志：\n\n```\ntail /opt/fastcfs/fdir/logs/fdir_serverd.log\n```\n\n## 4. faststore server（服务实例）配置\n\n配置文件路径：\n\n> /etc/fastcfs/fstore\n\nfaststore集群各个服务实例配置的cluster.conf必须完全一样，建议把cluster.conf一次性配置好，然后分发到集群中的所有服务器和客户端。\n\n### 4.1 把 faststore 集群中的所有服务实例配置到 cluster.conf 中\n\n每个 **faststore** 服务实例包含3个服务端口：cluster、replica 和 service，和 **fastDIR** 的 cluster.conf 相比，多了一个replica端口，二者配置方式完全相同。\n\n### 4.2 在 cluster.conf 中配置服务器分组和数据分组对应关系\n\n对于生产环境，为了便于今后扩容，建议数据分组数目至少为64，最好不要超过1024（视业务未来5年发展规模而定）。\n\n### 4.3 在storage.conf 中配置存储路径等参数\n\n支持配置多个存储路径。为了充分发挥出硬盘性能，建议挂载单盘，每块盘作为一个存储路径。\n\n配置示例：\n\n```\nstore_path_count = 1\n\n[store-path-1]\npath = /opt/faststore/data\n```\n\n### 4.4 配置 server.conf\n\n* 如果需要修改binlog存放路径，修改配置项 data_path 为绝对路径\n* 本机端口包含cluster、replica和service 3个端口，分配在[cluster]、[replica] 和 [service] 中配置\n* 本机IP + 本机端口必须配置在cluster.conf的一个实例中，否则启动时会出现类似下面的出错信息\n\n```\nERROR - file: server_group_info.c, line: 347, cluster config file: /etc/fastcfs/fstore/cluster.conf,\n      can't find myself by my local ip and listen port\n```\n\n* V4.0支持的slice索引持久化特性默认是关闭的，在 [storage-engine] 中设置，详情参见源码的conf目录下的配置示例。\n启用存储引擎配置示例：\n\n```\n[storage-engine]\n# if enable the storage engine\n### false: use binlog directly\n### true: use storage engine for huge storage\n# default value is false\nenabled = true\n\n# the config filename for storage\nstorage_config_filename = dbstore.conf\n\n# the path to store the data files\n# can be an absolute path or a relative path\n# the relative path for sub directory under the base_path\n# this path will be created auto when not exist\n# default value is db\ndata_path = db\n\n# the memory limit ratio for slice index\n# the valid limit range is [1%, 99%]\n# default value is 60%\nmemory_limit = 60%\n```\n\n### 4.5 配置 dbstore.conf\n\n* slice索引开启持久化存储特性的情况下，才需要配置dbstore.conf\n* 通常采用默认设置即可\n\n\nfaststore重启：\n\n```\n/usr/bin/fs_serverd /etc/fastcfs/fstore/server.conf restart\n```\n\n或者：\n\n```\nsudo systemctl restart faststore\n```\n\n查看日志：\n\n```\ntail /opt/fastcfs/fstore/logs/fs_serverd.log\n```\n\n## 5. FastCFS客户端配置\n\nfused 配置文件路径：\n\n> /etc/fastcfs/fcfs\n\n_注：如果fused客户端与faststore和fastDIR部署在同一个服务器上，可跳过5.1、5.2节，否则需要将faststore和fastDIR的cluster.conf配置文件复制到fused客户端所在服务器上。_\n\n### 5.1 复制faststore server上的如下配置文件到 /etc/fastcfs/fstore/\n\n```\n/etc/fastcfs/fstore/cluster.conf\n```\n\n### 5.2 复制fastDIR server上的如下配置文件到 /etc/fastcfs/fdir/\n\n```\n/etc/fastcfs/fdir/cluster.conf\n```\n\n### 5.3 修改fuse挂载点（可选）\n\n如有必要，可修改配置文件 **fuse.conf** 中的 **mountpoint** 参数来指定挂载点：\n\n> mountpoint = /opt/fastcfs/fuse\n\nfused 重启：\n\n> /usr/bin/fcfs_fused /etc/fastcfs/fcfs/fuse.conf restart\n\n或者：\n\n> sudo systemctl restart fastcfs\n\n查看日志：\n\n> tail /opt/fastcfs/fcfs/logs/fcfs_fused.log\n\n查看fastDIR集群状态：\n\n> fdir_cluster_stat\n\n查看单台fastDIR 服务状态：\n\n> fdir_service_stat $IP:$port\n\n**_友情提示：可以拷贝fdir_cluster_stat 输出的IP和服务端口_**\n\n查看faststore集群状态：\n\n> fs_cluster_stat\n\n查看非ACTIVE的服务列表，使用：\n\n> fs_cluster_stat -N\n\n使用 -h 查看更多选项\n\n查看FastCFS磁盘使用情况：\n\n> df -h /opt/fastcfs/fuse | grep fuse\n\n至此，mountpoint（如：/opt/fastcfs/fuse）可以作为本地文件目录访问了。\n\n## 6. 选举节点配置（可选）\n\n如果需要实现双副本防脑裂（即双活互备防脑裂），请参阅 [选举节点配置文档](VoteNode-zh_CN.md)\n\n## 7. 认证配置（可选）\n\n如果需要开启存储池或访问权限控制，请参阅 [认证配置文档](AUTH-zh_CN.md)\n\n## 8. 共享数据配置（可选）\n\n将FastCFS作为Oracle RAC等系统的共享存储，请参阅 [共享数据配置指南](shared-storage-guide-zh_CN.md)\n\n祝顺利！ have a nice day!\n"
  },
  {
    "path": "docs/Easy-install-detail-zh_CN.md",
    "content": "# 快速体验FastCFS\n\n以CentOS 8为例，用单个服务节点，以一键安装的方式体验下FastCFS。 此方式仅供体验使用，不适合在生产环境使用. \n\n## 1. 获得最新的代码\n\n```\ngit clone https://gitee.com/fastdfs100/FastCFS.git\n```\n\n## 2. 安装\n\n涉及到libfuse的安装更新以及在/usr/local下创建目录，请务必使用sudo或者root的方式安装。\n\n```\ncd FastCFS/\nsudo ./helloWorld.sh\n```\n\n安装过程中会有进度提示信息，全自动进行，当看到以下提示时说明安装成功了。\n\n```\nINFO: Copy file fuse.conf to /etc/fastcfs/fcfs/\nwaiting services ready ...\n/dev/fuse        38G     0   38G   0% /opt/fastcfs/fuse\n\nthe mounted path is: /opt/fastcfs/fuse\nhave a nice day!\n```\n\n## 3. 验证：查看安装的文件目录\n\n安装成功后，通过df -h 可以看到新增一个/dev/fuse的设备，默认容量与你的磁盘容量有关，非固定值。比如我的是38G. \n\n```\nFilesystem           Size  Used Avail Use% Mounted on\ndevtmpfs             189M     0  189M   0% /dev\ntmpfs                219M     0  219M   0% /dev/shm\ntmpfs                219M  6.6M  212M   4% /run\ntmpfs                219M     0  219M   0% /sys/fs/cgroup\n/dev/mapper/cs-root   47G  5.9G   42G  13% /\n/dev/sda1           1014M  242M  773M  24% /boot\ntmpfs                 44M  4.0K   44M   1% /run/user/1000\n/dev/fuse             38G     0   38G   0% /opt/fastcfs/fuse\n```\n\n## 4. 使用：在新挂载的目录/opt/fastcfs/fuse 中，可以进行标准的文件创建/编辑/删除操作\n\n```\n[first@192.168.126.50 /opt/fastcfs/fuse]\n$ pwd\n/opt/fastcfs/fuse\n[first@192.168.126.50 /opt/fastcfs/fuse]\n$ touch abc  // 创建文件\n[first@192.168.126.50 /opt/fastcfs/fuse]\n$ ll\ntotal 0\n-rw-rw-r--. 1 first first 0 May 14 17:06 abc\n[first@192.168.126.50 /opt/fastcfs/fuse]\n$ rm abc  // 删除文件\n[first@192.168.126.50 /opt/fastcfs/fuse]\n$ mkdir -p d1/d2/d3  // 创建目录\n[first@192.168.126.50 /opt/fastcfs/fuse]\n$ ll\ntotal 0\ndrwxrwxr-x. 2 first first 0 May 14 17:06 d1\n[first@192.168.126.50 /opt/fastcfs/fuse]\n$ touch d1/d2/d3/123 // 创建文件\n[first@192.168.126.50 /opt/fastcfs/fuse]\n$ ll d1/d2/d3/123 \n-rw-rw-r--. 1 first first 0 May 14 17:06 d1/d2/d3/123\n[first@192.168.126.50 /opt/fastcfs/fuse]\n$ rm -rf d1 // 删除目录\n[first@192.168.126.50 /opt/fastcfs/fuse]\n$ ll\ntotal 0\n```\n \n## 5. 停止服务\n\n使用fashcfs.sh为管理脚本，可以停止、重启服务:\n\n```\n[first@192.168.126.50 ~/FastCFS]\n$ sudo ./fastcfs.sh stop\n[sudo] password for first:\nwaiting for pid [2364] exit ...\npid [2364] exit.\nwaiting for pid [2379] exit ...\npid [2379] exit.\nwaiting for pid [2383] exit ...\npid [2383] exit.\nwaiting for pid [2400] exit ...\npid [2400] exit.\n```\n\n## 6. 移除安装包恢复初始环境\n\n```\nsudo yum remove FastCFS-auth-server FastCFS-fused fastDIR-server faststore-server -y\nsudo yum remove fastDIR-config faststore-config FastCFS-auth-config FastCFS-fuse-config -y\nsudo rm -rf /etc/fastcfs /opt/fastcfs /opt/faststore\n```\n"
  },
  {
    "path": "docs/FAQ-zh_CN.md",
    "content": "# FAQ\n\n## 1. 服务器最低配置要求\n\n2核CPU、4G内存、10G硬盘\n\n## 2. 网络连接异常\n\n当日志出现`Connection refused`或者`Transport endpoint is not connected`之类的异常记录。\n请检查防火墙设置。FastCFS 共有9个服务端口,这些对应的端口应该打开：\n\n* fdir\n  * 默认集群端口 `11011`\n  * 默认服务端口 `11012`\n* fvote\n  * 默认集群端口 `41011`\n  * 默认服务端口 `41012`\n* fauth\n  * 默认集群端口 `31011`\n  * 默认服务端口 `31012`\n* fstore\n  * 默认集群端口 `21014`\n  * 默认副本端口 `21015`\n  * 默认服务端口 `21016`\n\n\n## 3. 服务有没有启动顺序？\n\n有。应按顺序启动`fvote`（可选），`fdir`， `fauth`（可选），`fstore`，`fuseclient`。\n\n## 4. FastCFS支持单盘数据恢复吗？\n\n```\n支持。\n实现机制：从本组的其他服务器上获取数据，恢复指定硬盘（即存储路径）上的所有数据。\n使用场景：硬盘坏掉后更换为新盘，或者因某种原因重新格式化硬盘。\n单盘故障恢复后，重启fs_serverd时，加上命令行参数 --data-rebuild <store_path>，\n其中 store_path为要恢复数据硬盘对应的存储路径。\n```\n\n## 5. FastCFS是如何防脑裂的？\n\n```\nleader/master选举时采用过半数机制，一个分组的节点数最好为奇数（比如3个）。\n配置项为 quorum，默认值为auto，表示节点数为奇数时开启，为偶数时关闭。\n详情参见源码目录下的conf/full/cluster.conf\n\nv3.4引入公用选举节点，在偶数节点数（比如双副本）时也可以防脑裂，详情参见配置文档。\n```\n\n## 6. fdir和fstore多副本（如3副本）情况下，停机后重启不能写入数据\n\n```\n为了保证数据一致性，当停机时间超过配置参数max_shutdown_duration（默认配置300秒），\n需要等待本组的其他服务器在线才可以选出master/leader。\n\n以3副本为例，如果3台服务器停机均超过max_shutdown_duration，而有的服务器没有启动，\n那么master/leader选举不出来，导致不能写入数据。此时启动fdir_serverd需要加上命令行\n参数--force-master-election，启动fs_serverd需要加上命令行参数--force-leader-election\n```\n\n## 7. 如何验证多个副本的数据一致性？\n\n```\nfaststore需要源码编译，修改make.sh，将\nCFLAGS='-Wall'\n修改为：\nCFLAGS='-Wall -DFS_DUMP_SLICE_FOR_DEBUG=32'\n启动fs_serverd，数据将导出到 $base_path/data/dump/slice.index，例如：\n/opt/fastcfs/fstore/data/dump/slice.index\n\n文件格式：\n类型 文件ID block偏移量 slice偏移量 slice长度 数据长度 数据CRC32\n\n可以通过 diff 命令比较同一组服务器下的两个节点导出的slice.index是否相同。\n\n注意：-DFS_DUMP_SLICE_FOR_DEBUG=32 这个编译选项仅供测试环境进行数据一致性验证，请不要在生产环境使用。\n\n```\n\n## 8. fuse客户端通过df看到的空间明显大于存储池配额\n\n这是因为客户端没有启用auth，需要将配置文件/etc/fastcfs/auth/auth.conf中的auth_enabled设置为true，修改后重启fcfs_fused生效。\n\n友情提示：如何配置auth服务请参阅 [认证配置文档](AUTH-zh_CN.md)\n\n## 9. FastCFS作为NFS后端存储时NFS client mount失败\n\n出错信息：reason given by server: No such file or directory\n\n解决方法：\n\nNFS v3直接使用服务端配置的目录如：/opt/fastcfs/fuse，而v4将服务端配置的路径作为基路径，mount要使用/\n\nNFS挂载默认会使用最新的NFS协议，挂载命令示例（支持v4前提下使用）：\n```\nmount -t nfs -onolock 172.16.168.131:/ /mnt/nfs\n```\n\nNFS v3挂载命令示例：\n```\nmount -t nfs -onolock 172.16.168.131:/opt/fastcfs/fuse /mnt/nfs\n```\n\n指定NFS v4挂载命令示例：\n```\nmount -t nfs -onolock,nfsvers=4 172.16.168.131:/ /mnt/nfs\n```\n\n友情提示：\n   * CentOS 6需要使用NFS v3挂载\n   * /etc/exports中需要设置fsid=0，例如：/opt/fastcfs/fuse 172.16.168.130(fsid=0,rw,sync,no_root_squash,no_all_squash)\n\n## 10. 为何删除了足够多的数据，df看到磁盘占用空间不见降低呢？\n\nFastCFS基于trunk file进行空间分配，目前trunk file只会增加而不会释放。文件数据删除后空间会回收利用，通过fuse client上df命令可以看到FastCFS的可用空间将增加。\n磁盘空间使用率达到阈值后才会启动空间回收，默认阈值为50%，可以根据实际需要调整。配置示例参见faststore源码目录下的conf/full/storage.conf。\n\n## 11. 如何将fastore的数据清除干净？\n\n一些特殊情况下，需要清除掉faststore的数据，此时要删除faststore的系统目录（server.conf中配置的base_path，默认为/opt/fastcfs/fstore）和用户数据存放目录（默认为/opt/faststore/data，配置多盘的情况要逐一删除数据目录），删除命令示例（文件删除后不可恢复，请再三确认后执行）：\n```\nrm -rf /opt/fastcfs/fstore /opt/faststore/data\n```\n\n## 12. 日志中报没有访问权限，如何进行排查？\n\n/opt/fastcfs/fcfs/logs/fcfs_fused.log 中的错误示例：\n\n[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\n\n问题排查：\n\n依次查看各台fdir server上的日志文件 /opt/fastcfs/fdir/logs/fdir_serverd.log，找到对应的出错信息，然后使用最新版本的fdir_stat 查看对应的完整路径（文件名），根据文件或目录owner、权限、访问用户及其用户组进行排查。\n\n按文件或目录inode查询示例：\n\n```\nfdir_stat -Fn fs 9007199660325177\n```\n\n按父目录inode + 子目录名或者文件名查询示例：\n```\nfdir_stat -Fn fs 9007199667318991 test\n```\n\n友情提示：如果采用的是装包方式，程序fdir_stat 所在的rpm包名为fastDIR-client，deb包名为fastdir-client\n"
  },
  {
    "path": "docs/INSTALL-zh_CN.md",
    "content": "# FastCFS安装手册\n\n## 1、fastcfs.sh 脚本统一安装\n\n通过执行fastcfs.sh脚本，可自动安装软件包，并能根据配置文件模版自动生成集群相关配置文件。\n\nfastcfs.sh 命令参数说明：\n\n```\n* install: 安装程序包\n* config: 复制配置文件并自动配置为单节点\n* start | stop | restart: 服务进程控制\n```\n\n或执行如下命令（需要root身份执行）：\n\n```\n./fastcfs.sh install\n./fastcfs.sh config\n./fastcfs.sh restart\n```\n\n通过 df 命令查看FastCFS挂载的文件目录：\n\n```\ndf -h /opt/fastcfs/fuse | grep fuse\n```\n\n以上命令执行成功，你就可以通过本地目录 /opt/fastcfs/fuse 访问FastCFS了。\n\n## 2、DIY编译和安装\n\n### 2.1 安装libaio devel包\n\n对于 CentOS或REHL，执行：\n\n```\nyum install libaio-devel -y\n```\n\n对于 Unbuntu或Debian，执行：\n\n```\napt install libaio-dev -y\n```\n\n其他Linux系统，需要编译安装 **libaio** 库。\n\n### 2.2 安装存储插件包和RDMA通信包【可选】\n\nfastDIR 和faststore 的存储插件以及RDMA通信库都是闭源的，可以通过 yum 或 apt 安装使用。\n\n配置我们的yum源和apt源：\n\n* yum安装方式（针对CentOS、Rocky、Fedora、RHEL等），参阅 [yum安装文档](YUM-INSTALL-zh_CN.md)\n* apt安装方式（针对Ubuntu、Debian 和 Deepin），参阅 [apt 安装文档](APT-INSTALL-zh_CN.md)\n\n如果使用存储插件特性，需要通过 yum 或 apt 安装相应的存储插件。\n\n* fastDIR 存储插件包名：libfdirstorage\n* faststore 存储插件包名：libfsstorage\n\n具备RDMA网络环境，要启动 RDMA 通信方式，需要安装RDMA通信包：libfastrdma\n\n\n### 2.3 libfastcommon 编译安装\n\n```\ngit clone https://gitee.com/fastdfs100/libfastcommon.git; cd libfastcommon/\ngit checkout master\n./make.sh clean && ./make.sh && ./make.sh install\n```\n\n默认安装目录：\n```\n/usr/lib64\n/usr/lib\n/usr/include/fastcommon\n```\n\n### 2.4 libserverframe 编译安装\n\n```\ngit clone https://gitee.com/fastdfs100/libserverframe.git; cd libserverframe/\n./make.sh clean && ./make.sh && ./make.sh install\n```\n\n### 2.5 libdiskallocator 编译安装\n\n```\ngit clone https://gitee.com/fastdfs100/libdiskallocator.git; cd libdiskallocator/\n./make.sh clean && ./make.sh && ./make.sh install\n```\n\n### 2.6 Vote Node client 编译安装\n\n```\ngit clone https://gitee.com/fastdfs100/FastCFS.git; cd FastCFS/\n./make.sh clean && ./make.sh --module=voteclient && ./make.sh --module=voteclient install\n```\n\n### 2.7 Auth client 编译安装\n\n```\ngit clone https://gitee.com/fastdfs100/FastCFS.git; cd FastCFS/\n./make.sh clean && ./make.sh --module=authclient && ./make.sh --module=authclient install\n```\n\n### 2.8 fastDIR 编译安装\n\n```\ngit clone https://gitee.com/fastdfs100/fastDIR.git; cd fastDIR/\n./make.sh clean && ./make.sh && ./make.sh install\nmkdir -p /etc/fastcfs/fdir/\ncp conf/*.conf /etc/fastcfs/fdir/\n```\n\n### 2.9 faststore 编译安装\n\n```\ngit clone https://gitee.com/fastdfs100/faststore.git; cd faststore/\n./make.sh clean && ./make.sh && ./make.sh install\nmkdir -p /etc/fastcfs/fstore/\ncp conf/*.conf /etc/fastcfs/fstore/\n```\n\n### 2.10 fuse客户端编译安装\n\n#### 2.10.1 libfuse 编译安装\n\nlibfuse 编译依赖比较复杂，建议使用脚本libfuse_setup.sh一键编译和安装。或者执行如下步骤DIY：\n\n构建libfuse需要先安装meson和ninja。安装meson和ninja需要python3.5及更高版本。\n\n##### 2.10.1.1 gcc 安装\n\n友情提示：gcc版本必须 >= 4.7.0\n\nUbuntu下安装命令：\n\n```\napt install gcc g++ -y\n```\n\nCentOS下安装命令：\n\n```\nyum install gcc gcc-c++ -y\n```\n\n##### 2.10.1.2 pkg-config 和 python安装\n\n需要安装的包名：\n* pkg-config\n* python3\n* python3-pip\n\nUbuntu下安装命令：\n\n```\napt install pkg-config python3 python3-pip -y\n```\n\nCentOS下安装命令：\n\n```\nyum install pkgconfig python3 python3-pip -y\n```\n\n##### 2.10.1.3 meson 和 ninja 安装\n\n友情提示： ninja 版本必须 >= 1.7\n\n```\npip3 install meson\npip3 install ninja\n```\n\n##### 2.10.1.4 libfuse 安装\n\n```\ngit clone https://gitee.com/mirrors/libfuse.git; cd libfuse/\ngit checkout fuse-3.16.2\nmkdir build/; cd build/\nmeson ..\nmeson configure -D prefix=/usr\nmeson configure -D examples=false\nninja && ninja install\nsed -i 's/#user_allow_other/user_allow_other/g' /etc/fuse.conf\nsed -i \"s#prefix=.*#prefix=/usr#g\" /usr/lib64/pkgconfig/fuse3.pc\n```\n\n#### 2.10.2 依赖的项目编译和安装\n\n参见上面的2.3 ～2.8，按照步骤逐一编译和安装\n\n\n#### 2.10.3 fcfs_fused 编译安装\n\n进入之前clone下来的FastCFS目录，然后执行：\n```\n./make.sh --module=fuseclient clean && ./make.sh --module=fuseclient && ./make.sh --module=fuseclient install\nmkdir -p /etc/fastcfs/fcfs/\nmkdir -p /etc/fastcfs/vote/\nmkdir -p /etc/fastcfs/auth/\ncp conf/*.conf /etc/fastcfs/fcfs/\ncp -R src/vote/conf/* /etc/fastcfs/vote/\ncp -R src/auth/conf/* /etc/fastcfs/auth/\n```\n\n配置参见 [配置文档](CONFIGURE-zh_CN.md)\n"
  },
  {
    "path": "docs/INSTALL.md",
    "content": "### 1. install libaio devel\n\nfor CentOS or REHL:\n```\nyum install libaio-devel -y\n```\n\nfor Unbuntu or Debian:\n```\napt install libaio-dev -y\n```\n\n### 2. libfastcommon\n\n```\ngit clone git@github.com:happyfish100/libfastcommon.git || git clone https://github.com/happyfish100/libfastcommon.git\ncd libfastcommon/\ngit checkout master\n./make.sh clean && ./make.sh && ./make.sh install\n```\n\ndefault install directories:\n```\n/usr/lib64\n/usr/lib\n/usr/include/fastcommon\n```\n\n### 3. libserverframe\n\n```\ngit clone git@github.com:happyfish100/libserverframe.git || git clone https://github.com/happyfish100/libserverframe.git\ncd libserverframe/\n./make.sh clean && ./make.sh && ./make.sh install\n```\n\n### 4. libdiskallocator\n\n```\ngit clone https://gitee.com/fastdfs100/libdiskallocator.git  || git clone https://github.com/happyfish100/libdiskallocator.git\ncd libdiskallocator/\n./make.sh clean && ./make.sh && ./make.sh install\n```\n\n### 5. Vote Node client\n\n```\ngit clone git@github.com:happyfish100/FastCFS.git || git clone https://github.com/happyfish100/FastCFS.git\ncd FastCFS/\n./make.sh clean && ./make.sh --module=voteclient && ./make.sh --module=voteclient install\n```\n\n### 6. Auth client\n\n```\ngit clone git@github.com:happyfish100/FastCFS.git || git clone https://github.com/happyfish100/FastCFS.git\ncd FastCFS/\n./make.sh clean && ./make.sh --module=authclient && ./make.sh --module=authclient install\n```\n\n### 7. fastDIR\n\n```\ngit clone git@github.com:happyfish100/fastDIR.git || git clone https://github.com/happyfish100/fastDIR.git\ncd fastDIR/\n./make.sh clean && ./make.sh && ./make.sh install\nmkdir -p /etc/fastcfs/fdir/\ncp conf/*.conf /etc/fastcfs/fdir/\n```\n\n### 8. faststore\n\n```\ngit clone git@github.com:happyfish100/faststore.git || git clone https://github.com/happyfish100/faststore.git\ncd faststore/\n./make.sh clean && ./make.sh && ./make.sh install\nmkdir -p /etc/fastcfs/fstore/\ncp conf/*.conf /etc/fastcfs/fstore/\n```\n\n### 9. fuse client\n#### 9.1. libfuse\n\nlibfuse requires meson and ninja which require python3.5 or higher version\n\n##### 9.1.1. pkg-config and python\n\npackages: pkg-config  python3  python3-pip\n\nUbuntu:\n```\napt install pkg-config python3 python3-pip -y\n```\n\nCentOS:\n```\nyum install pkgconfig python3 python3-pip -y\n```\n\n##### 9.1.2. meson and ninja\n\n```\npip3 install meson\npip3 install ninja\n```\n\n##### 9.1.3. gcc\n\nUbuntu:\n```\napt install gcc g++ -y\n```\n\nCentOS:\n```\nyum install gcc gcc-c++ -y\n```\n\n##### 9.1.4. libfuse\n\n```\ngit clone git@github.com:libfuse/libfuse.git || git clone https://github.com/libfuse/libfuse.git\ncd libfuse/\ngit checkout fuse-3.10.1\nmkdir build/; cd build/\nmeson ..\nmeson configure -D prefix=/usr\nmeson configure -D examples=false\nninja && ninja install\nsed -i 's/#user_allow_other/user_allow_other/g' /etc/fuse.conf\nsed -i \"s#prefix=.*#prefix=/usr#g\" /usr/lib64/pkgconfig/fuse3.pc\n```\n\n#### 9.2. fcfs_fused\n\n```\ncd FastCFS/\n./make.sh --module=fuseclient clean && ./make.sh --module=fuseclient && ./make.sh --module=fuseclient install\nmkdir -p /etc/fastcfs/fcfs/\nmkdir -p /etc/fastcfs/vote/\nmkdir -p /etc/fastcfs/auth/\ncp conf/*.conf /etc/fastcfs/fcfs/\ncp -R src/vote/conf/* /etc/fastcfs/vote/\ncp -R src/auth/conf/* /etc/fastcfs/auth/\n```\n"
  },
  {
    "path": "docs/ReleaseNotes.md",
    "content": "## FastCFS V3.1.0 发布，主要改进如下：\n### 新特性\n1. [fdir] 对核心组件FastDIR进行改进，实现了LRU淘汰算法，以有限内存支持海量文件\n   分布式目录服务FastDIR的淘汰算法具有两大特性：\n     1. 按目录结构淘汰：先淘汰子节点，然后淘汰父节点；\n     2. 按数据线程淘汰：每个数据线程作为一个独立的数据单元，数据存取和淘汰均在其数据线程中以无锁方式完成。\n\n### Bug修复\n1. 采用引用计数方式，不再延迟释放 dentry；\n2. delay free namespace string for lockless；\n3. bugfixed: get trunk_file_size from ini file correctly。\n\n---------------\n\n## FastCFS V3.0.0 发布，主要改进如下：\n### 新特性\n1. [fdir] 核心组件FastDIR通过插件方式实现数据存储引擎，采用binlog + 存储引擎插件，按需加载inode数据，单机以有限内存（如64GB）支持100亿级的海量文件\n\n### Bug修复\n1. [fdir] increase/decrease parent's nlink on rename operation\n1. [fdir] set dentry->kv_array->count to 0 correctly\n1. [fstore] should init barray->count to 0\n\n---------------\n\n## FastCFS V2.3.0发布，主要改进如下：\n### 新特性\n1. [fauth] auth server以主备方式支持多节点，避免单点；\n1. [fstore&fdir] leader/master选举/切换引入超时机制，选举时长可控；\n1. [网络] 网络通信相关改进：\n  1）握手失败，server端主动断开连接； \n  2）cluster内部通信server端超时控制； \n  3）调整网络通信超时默认值（连接超时由10秒调整为2秒，收发数据超时由30秒调整为10秒）。\n\n### Bug修复\n\n\n---------------\n## FastCFS V2.2.0 发布，主要改进如下：\n### 新特性\n1. [fstore] 使用libaio实现异步读，随机读性能提升明显；\n1. [fstore] 支持预读机制，顺序读性能提升显著；\n1. [csi] k8s驱动（fastcfs-csi）已同步发布，\n1. [devOPS] 集群运维工具（fcfs.sh）\n\n### Bug修复\n1. [fstore] 修复V2.1.0引入的bug：第一次运行时，一个关键bool变量没有正确赋值；\n2. [fuseclient] 修复列举目录导致元数据缓存的一致性问题；\n3. [fauth] 修复username和poolname格式修饰符不当导致的乱码问题。\n\n\n---------------\n## FastCFS V2.0.0 发布，为了更好地对接虚拟机和K8s，V2.0主要实现了存储池和访问权限控制，并支持配额：\n### 新特性\n1. [fauth] 用户和权限体系\n\n### Bug修复\n\n---------------\n## FastCFS V1.2.0 发布，主要对数据恢复和master任命机制做了改进，修复了5个稳定性bug，FastCFS的可靠性和稳定性上了一个新台阶：\n### 新特性\n\n\n### Bug修复\n\n\n---------------\n## FastCFS V1.0.0 发布：\nFastCFS 3人小团队历经11个月的研发，推出了FastCFS第一个可用版本，MySQL、PostgresSQL和Oracle可以跑通。\n\nFastCFS服务端两个核心组件是 FastStore 和 FastDIR。FastStore是基于块存储的分布式数据存储服务，其文件block大小为4MB，文件 inode + 文件偏移量 (offset)作为block的key，对应的文件内容为 value，FastStore本质是一个分布式Key-Value系统。\n### 主要特性\n1. [fdir] 高性能分布式目录服务，管理文件元数据，支持命名空间\n1. [fstore] FastStore是一个分布式Key-Value系统，提供了文件块操作，基于数据块的无中心设计，（类一致性Hash），理论上可以无限扩容；\n1. [fuseclient] 基于FUSE的标准文件接口已经实现，可以使用fused模块mount到本地，为数据库、虚拟机以及其他使用标准文件接口的应用提供存储。\n\n"
  },
  {
    "path": "docs/TODO-zh_CN.md",
    "content": "# TODO List\n\n欢迎有志之士参与完善！\n\n## 一、测试与反馈\n\n## 二、对外接口\n\n### 1. 对象存储\n\n#### 1.1 S3\n\n#### 1.2 OSS\n\n#### 1.3 OBS\n\n#### 1.4 COS\n\n### 2. VFS\n\n### 3. NBD\n\n### 4. iSCSI\n"
  },
  {
    "path": "docs/VoteNode-zh_CN.md",
    "content": "\n# Vote Node (选举节点)配置及运行\n\n如果你不需要实现双副本防脑裂（即双活互备防脑裂），可以跳过本文档。\n\n本文档以FastCFS RPM包设定的路径（配置文件目录和程序工作目录等）进行说明，如果你采用自助编译安装方式的话，请自行对应。\n\n为了防脑裂，建议配置3个节点（服务器），因选举服务占用资源较少，可以和其他服务共用服务器。\n\n## 1. Vote Node配置文件目录结构\n\n```\n/etc/fastcfs/\n        |\n        |__ vote: 选举节点\n             |__ cluster.conf: 服务器列表，配置服务器ID、IP和端口\n             |__ server.conf: fcfs_voted对应的配置文件\n             |__ client.conf: 客户端配置文件\n```\n\n\n## 2. voted程序工作目录\n\n```\n/opt/fastcfs/\n        |\n        |__ vote\n             |__ voted.pid: 服务进程fcfs_voted的pid文件\n             |__ logs: 日志文件目录\n                  |__ fcfs_voted.log: 错误日志\n```\n\n开启共用选举服务需要设置选举节点、Auth server、FastDIR server和FastStore server。\n\n## 3. 选举节点（vote server）配置\n\n配置文件路径：/etc/fastcfs/vote\n\nVote集群内各个server配置的cluster.conf必须完全一样。\n\n建议配置一次，分发到其他服务器即可。\n\n### 3.1 把Vote集群中的所有服务实例配置到cluster.conf中\n\n每个Vote服务实例包含2个服务端口：cluster 和 service\n\n一个Vote服务实例需要配置一个[server-$id]的section，其中$id为实例ID。\n\n### 3.2 配置 server.conf\n\n* [cluster] 和 [service] 配置的端口（port）必须与cluster.conf中本机的一致，否则启动会报错\n\n### 3.3 启动voted\n\nvoted命令直接重启：\n\n```\n/usr/bin/fcfs_voted /etc/fastcfs/vote/server.conf restart\n```\n\n或者系统服务命令启动：\n\n```\nsudo systemctl restart fastvote\n```\n\n查看日志：\n\n```\ntail /opt/fastcfs/vote/logs/fcfs_voted.log\n```\n\n## 4. Auth server\n\n### 4.1 复制vote server上的如下配置文件到 /etc/fastcfs/vote/\n\n```\n/etc/fastcfs/vote/cluster.conf\n/etc/fastcfs/vote/client.conf\n```\n\n修改 /etc/fastcfs/auth/cluster.conf，将vote_node_enabled 设置为true。\n配置片段如下：\n\n```\n[master-election]\n# if enable vote node when the number of servers is even\n# the default value is false\nvote_node_enabled = true\n\n# the cluster config filename of the vote node\n# this parameter is valid when vote_node_enabled is true\nvote_node_cluster_filename = ../vote/cluster.conf\n```\n\n拷贝完成后重启认证服务（fcfs_authd）\n\n## 5. FastDIR server\n\n修改 /etc/fastcfs/fdir/cluster.conf，将vote_node_enabled 设置为true。\n\n* 详情参见 4. Auth server 部分\n\n拷贝和设置完成后重启FastDIR服务（fdir_serverd）\n\n## 6. FastStore server\n\n* 参见 4. Auth server 部分\n\n修改 /etc/fastcfs/fstore/cluster.conf，将vote_node_enabled 设置为true。\n配置片段如下：\n\n```\n[leader-election]\n\n# if enable vote node when the number of servers is even\n# the default value is false\nvote_node_enabled = true\n\n# the cluster config filename of the vote node\n# this parameter is valid when vote_node_enabled is true\nvote_node_cluster_filename = ../vote/cluster.conf\n```\n\n拷贝完成后重启FastStore服务（fs_serverd）\n\n## 友情提示\n\n* 4 ~ 6部分的配置及启动参见 [配置指南](CONFIGURE-zh_CN.md)\n"
  },
  {
    "path": "docs/YUM-INSTALL-Diy-5nodes-zh_CN.md",
    "content": "### 安装的集群架构\nFastCFS支持大规模的集群，下面以最为典型的最小化集群的部署方式，说明下自定义安装的过程。更大规模的安装方式可参照此过程扩展安装。\n1. 节点类型和数量：共计5个节点，3个服务端节点（3副本策略），2客户端节点\n2. 服务端集群：FastStore和FastDir公用，也可以分开独立部署，生产环境建议分开。本说明为了降低节点数量，采用了公用部署的方式。\n3. 客户端：为了说明支持多客户端，使用了2个客户端节点。生产环境中如果只需要1个客户端，也是没问题的。\n<img src=\"../images/demo-5-nodes-deploy.jpg\" width=\"800\" alt=\"5个节点的集群架构图\">\n\n### 前置依赖\n先安装FastOS.repo yum源，FastCFS所需要的pkg都在此源中，安装成功后就可以安装FastCFS相关软件包了。\n\n- CentOS 7等el7系列的Linux发行版\n```\nrpm -ivh http://www.fastken.cn/yumrepo/el7/noarch/FastOSrepo-1.0.1-1.el7.noarch.rpm\n```\n\n- CentOS 8等el8系列的Linux发行版\n```\nrpm -ivh http://www.fastken.cn/yumrepo/el8/noarch/FastOSrepo-1.0.1-1.el8.noarch.rpm\n```\n\n友情提示：支持的Linux发行版以及与el7或el8的对应关系参见 [yum安装方式](YUM-INSTALL-zh_CN.md)\n\n\n### 服务端集群安装\n在192.168.126.[101,102,103]上分别安装FastStore、FastDIR 两个服务. \n#### 安装FastDIR\n```\nyum install fastDIR-server\n```\n安装过程中会安装所依赖的包，选y就可以了. \n```\n==================================================================================================================\n Package                            Architecture          Version                     Repository             Size\n==================================================================================================================\nInstalling:\n fastDIR-server                     x86_64                2.0.1-1.el8                 FastOS                 71 k\nInstalling dependencies:\n FastCFS-auth-client                x86_64                2.0.1-1.el8                 FastOS                 36 k\n FastCFS-auth-config                x86_64                2.0.1-1.el8                 FastOS                 10 k\n fastDIR-config                     x86_64                2.0.1-1.el8                 FastOS                9.5 k\n libfastcommon                      x86_64                1.0.50-1.el8                FastOS                140 k\n libserverframe                     x86_64                1.1.7-1.el8                 FastOS                 47 k\n\nTransaction Summary\n==================================================================================================================\nInstall  6 Packages\n\nTotal download size: 313 k\nInstalled size: 711 k\nIs this ok [y/N]: y   // 安装过程中会安装所依赖的包，选y就可以了. \n\n```\n安装完毕后，在/etc/fastcfs 下可看到fdir的目录，存储的是FastDIR的配置文件.  配置项的内容，后续会说明。\n```\n$ ll /etc/fastcfs/fdir/\ntotal 16\n-rw-r--r--. 1 root root 1565 May  9 13:49 client.conf\n-rw-r--r--. 1 root root  753 May  9 13:49 cluster.conf\n-rw-r--r--. 1 root root 5242 May  9 13:49 server.conf\n```\n\n#### 安装FastStore\n\n```\nyum install faststore-server\n```\n再说明下：实际生产环境部署中，FastDIR是和FastStore两个服务可以分开部署的，不需要再同一台服务器上。\n```\n==================================================================================================================\n Package                          Architecture           Version                     Repository              Size\n==================================================================================================================\nInstalling:\n faststore-server                 x86_64                 2.0.1-1.el8                 FastOS                 134 k\nInstalling dependencies:\n faststore-config                 x86_64                 2.0.1-1.el8                 FastOS                  11 k\n\nTransaction Summary\n==================================================================================================================\nInstall  2 Packages\n\nTotal download size: 145 k\nInstalled size: 422 k\nIs this ok [y/N]: y  // 安装过程中会安装所依赖的包，选y就可以了. \n```\n安装完毕后，在/etc/fastcfs 下可看到fstore目录，存储的是FastStore的配置文件。配置项的内容，后续会说明。\n```\n$ ll /etc/fastcfs/fstore/\ntotal 20\n-rw-r--r--. 1 root root 1577 May  9 13:52 client.conf\n-rw-r--r--. 1 root root 3256 May  9 13:52 cluster.conf\n-rw-r--r--. 1 root root 4965 May  9 13:52 server.conf\n-rw-r--r--. 1 root root 2609 May  9 13:52 storage.conf\n```\n\n### 安装客户端\n在192.168.126.[201,202]上安装Client包, 包名为FastCFS-fused. \n```\nyum install FastCFS-fused -y \n```\n\n安装完毕后，在/etc/fastcfs 下可看到以下目录，存储是对应服务的配置文件.  \n```\n$ ll /etc/fastcfs/\ndrwxr-xr-x. 3 root root 113 May 14 15:20 auth\ndrwxr-xr-x. 2 root root  23 May 14 15:20 fcfs\ndrwxr-xr-x. 2 root root  64 May 14 15:20 fdir\ndrwxr-xr-x. 2 root root  84 May 14 15:20 fstore\n```\n**为什么在Client的节点上，需要有fdir和fstore的目录呢？因为FastCFS是无中心配置的，Client需要知道fdir和fstore的集群情况，所以就需要fdir和fstore的配置文件。**\n\n### 自定义安装配置\n#### 说明\nFastCFS并没有统一的配置中心，需要在各个节点上单独部署配置文件。配置文件分为两大类：集群配置文件和服务配置文件。\n\n1. **集群配置文件：** 指的是描述 FastDir 和 FastStore 的配置文件，入口文件名称为cluster.conf . 该配置文件中设定是集群的参数，如服务节点的IP和端口号，服务节点的数量等。 cluster.conf 文件全局统一，各个节点上的内容是相同的, FastDir 和 FastStore 有各自的cluster.conf文件。\n\n2. **服务配置文件：** 指的是服务本身的配置文件，入口文件名称server.conf 如线程数量、链接数量、缓冲区大小、日志配置等。 服务配置文件的内容，可以全局不统一。不过从集群的角度考虑，如果各服务器配置相同，最好是统一的。 \n\n#### FastDIR 配置\n\n1. **配置文件说明** \n```\n$ ll /etc/fastcfs/fdir/ \n-rw-r--r--. 1 root root 1565 May  9 13:49 client.conf // 暂时不使用，不用关注\n-rw-r--r--. 1 root root  753 May  9 13:49 cluster.conf // 集群配置文件\n-rw-r--r--. 1 root root 5242 May  9 13:49 server.conf // 服务配置文件 \n```\n\n2. **集群配置文件** ：在101节点上，修改/etc/fastcfs/fdir/cluster.conf 文件，修改为正确的IP地址，修改后的内容如下\n```\n  1 # config the auth config filename\n  2 auth_config_filename = /etc/fastcfs/auth/auth.conf\n  3\n  4\n  5 [group-cluster]\n  6 # the default cluster port\n  7 port = 11011  // 集群之间的通信使用的默认端口号，不用修改\n  8\n  9 [group-service]\n 10 # the default service port\n 11 port = 11012  // 对client提供服务，使用的默认端口号，不用修改\n 12\n 13 # config a server instance\n 14 # section format: [server-$id]\n 15 # server id is a 32 bits natural number (1, 2, 3 etc.),\n 16 [server-1]\n 17\n 18 # format: host[:port]\n 19 # host can be an IP address or a hostname\n 20 # IP address is recommended\n 21 # can occur more than once\n 22 host = 192.168.126.101  // 节点101的地址，使用默认的端口号\n 23\n 24 [server-2]\n 25 # cluster-port = 11013\n 26 # service-port = 11014\n 27 host = 192.168.126.102 // 节点102的地址，使用默认的端口号\n 28\n 29 [server-3]\n 30 # cluster-port = 11015\n 31 # service-port = 11016\n 32 host = 192.168.126.103  // 节点103的地址，使用默认的端口号\n\n```\n3. **服务配置文件** ： 在101节点上，/etc/fastcfs/fdir/server.conf, 该文件配置项较多，但默认不用任何调整，确认文件底部的[service]区内容就好.\n```\n180 [cluster]\n181 # bind an address of this host\n182 # empty for bind all addresses of this host\n183 bind_addr =\n184\n185 # the listen port\n186 port = 11011\n187\n188 # the accept thread count\n189 # default value is 1 which is recommended\n190 accept_threads = 1\n191\n192 # the network thread count\n193 # these threads deal network io\n194 # dispatched by the incoming socket fd\n195 # default value is 4\n196 work_threads = 2\n197\n198 [service]\n199 bind_addr =  // 保持为空，FastDIR 会自动使用本机的IP地址\n200 port = 11012  // 确保与cluster.conf 中 group-service 的设定值相同\n201 accept_threads = 1\n202 work_threads = 4\n```\n4. 把cluster.conf文件复制到102-103,201-202 这5台服务器上,保证所有节点都能知道集群配置\n```\nscp /etc/fastcfs/fdir/cluster.conf 192.168.126.102:/etc/fastcfs/fdir/\nscp /etc/fastcfs/fdir/cluster.conf 192.168.126.103:/etc/fastcfs/fdir/\nscp /etc/fastcfs/fdir/cluster.conf 192.168.126.201:/etc/fastcfs/fdir/\nscp /etc/fastcfs/fdir/cluster.conf 192.168.126.202:/etc/fastcfs/fdir/\n```\n\n#### FastStore 配置\n\n1. **配置文件说明** \n```\n$ ll /etc/fastcfs/fstore\n-rw-r--r--. 1 root root 1577 May  9 13:52 client.conf // 暂时不使用，不用关注\n-rw-r--r--. 1 root root 3253 May 14 13:08 cluster.conf // 集群配置文件\n-rw-r--r--. 1 root root 4965 May  9 13:52 server.conf // 服务配置文件\n-rw-r--r--. 1 root root 2609 May  9 13:52 storage.conf // 服务配置文件\n```\n\n2. **集群配置文件** ：在101节点上，修改/etc/fastcfs/fstore/cluster.conf 文件，仅需要修改server_ids的值和[group-cluster]部分，其它参数不需要调整。修改后的内容如下. \n```\n 57 [server-group-1]\n 58\n 59 # config one or more server id(s) from servers.conf\n 60 ## multiple server ids separated by comma(s).\n 61 ## [start, end] for range, including start and end.\n 62 # this parameter can occurs more than once.\n 63 # server_ids = [1, 3]\n 64 server_ids = [1,3]  //  使用了3副本的策略，有3个服务节点101/102/103，所以设定为[1,3]\n 65\n 66 # the data group id based 1. the formats as:\n 67 ##  * multiple data group ids separated by comma(s).\n 68 ##  * [start, end] for range, including start and end.\n 69 # this parameter can occurs more than once.\n 70 data_group_ids = [1, 32]\n 71 data_group_ids = [33, 64]\n 72\n 73\n 74 [group-cluster]\n 75 # the default cluster port\n 76 port = 21014  // 集群之间的通信使用的默认端口号，不用修改\n 77\n 78 [group-replica]\n 79 # the default replica port\n 80 port = 21015 // Master-Slave 之间同步数据使用的默认端口号，不用修改\n 81\n 82 [group-service]\n 83 # the default service port\n 84 port = 21016  // 对Client提供服务，使用的默认端口号，不用修改\n 85\n 86 # config a server\n 87 # section format: [server-$id]\n 88 # server id is a 32 bits natural number (1, 2, 3 etc.),\n 89 [server-1]\n 90\n 91 # format: host[:port]\n 92 # host can be an IP address or a hostname\n 93 # IP address is recommended\n 94 # can occur more than once\n 95 host = 192.168.126.101  // 节点101的地址，使用默认的端口号 \n 96 \n 97 [server-2]\n 99 host = 192.168.126.102 // 节点102的地址，使用默认的端口号\n100\n101 [server-3]\n103 host = 192.168.126.103  // 节点103的地址，使用默认的端口号\n```\n3. **服务配置文件** 在101节点上，/etc/fastcfs/fstore/server.conf 和  /etc/fastcfs/fstore/storage.conf. 默认均需要修改，只需确认下server.conf的配置是否正确\n```\n190 [replica]\n191 bind_addr =\n192 port = 21015  // 保持与fstore/server.conf 中 [group-replica]设定的port值相同\n193 accept_threads = 1\n194 work_threads = 4\n195\n196 [service]\n197 bind_addr =\n198 port = 21016  // 保持与fstore/server.conf 中 [group-service]设定的port值相同\n199 accept_threads = 1\n200 work_threads = 4\n```\n4. 把cluster.conf文件复制到102-103,201-202 这5台服务器上，保证所有节点都能知道集群配置\n```\nscp /etc/fastcfs/fstore/cluster.conf 192.168.126.102:/etc/fastcfs/fstore/\nscp /etc/fastcfs/fstore/cluster.conf 192.168.126.103:/etc/fastcfs/fstore/\nscp /etc/fastcfs/fstore/cluster.conf 192.168.126.201:/etc/fastcfs/fstore/\nscp /etc/fastcfs/fstore/cluster.conf 192.168.126.202:/etc/fastcfs/fstore/\n```\n\n#### FastClient 配置\n1. Client 需要知道FastDIR和FastStore的集群配置数据，在上述scp过程中，已经分别scp到了对应的目录下，不需要修改.\n2. Client 自身的配置文件为 /etc/fastcfs/fcfs/fuse.conf 属于服务配置文件，也不需要修改. \n \n### 启动服务\n\n安装并配置完成后，可以启动服务并验证.\n\n1. **关闭防火墙：** CentOS 8 默认情况下开启防火墙，会导致各节点之间无法访问，可暂关闭防火墙。\n```\nsudo systemctl stop firewalld.service\n```\n2. **启动FastDir：***  在101/102/103 三个节点上，执行下列命令启动. 注意必须是3个节点都启动。\n```\nsudo systemctl restart fastdir\n```\n或者\n```\nsudo /usr/bin/fdir_serverd /etc/fastcfs/fdir/server.conf restart\n```\n查看日志，确认启动成功.\n```\n$ tail /opt/fastcfs/fdir/logs/fdir_serverd.log\n\n[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\n[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\n[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.\n[2021-05-14 15:40:01] INFO - file: cluster_relationship.c, line: 474, the master server id: 3, ip 192.168.126.103:11011\n```\n日志中有error提示，是因为防火墙导致的3个节点无法互相通信产生的，关闭防火墙后FastDIR会自动进行尝试链接。从最后一行INFO日志可看到，服务已经启动成功了，并且选择了103节点作为Master. \n\n3. **启动FastStore:** 与FastDir的启动方式类似，在101/102/103 三个节点上，执行下列命令启动. 注意必须是3个节点都启动。\n```\nsudo systemctl restart faststore\n```\n或者\n```\nsudo /usr/bin/fs_serverd /etc/fastcfs/fstore/server.conf restart\n```\n查看日志，确认启动成功.\n```\n[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\n[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\n[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\n[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\n[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\n[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\n```\n看到以上类似的日志，即是启动成功了。 \n\n4. **启动FastClient：** 在201、202 节点上启动Client.\n```\nsudo systemctl restart fastcfs\n```\n或者\n```\nsudo  /usr/bin/fcfs_fused /etc/fastcfs/fcfs/fuse.conf restart\n```\n查看日志，确认启动成功\n```\n$ tail /opt/fastcfs/fcfs/logs/fcfs_fused.log \n[2021-05-14 16:14:15] INFO - file: sf_nio.c, line: 205, connect to server 192.168.126.101:11012 successfully\n[2021-05-14 16:14:15] INFO - file: sf_nio.c, line: 205, connect to server 192.168.126.103:11012 successfully\n[2021-05-14 16:14:15] INFO - file: sf_connection_manager.c, line: 827, [FastDIR] connection manager thread start\n[2021-05-14 16:14:15] INFO - file: sf_connection_manager.c, line: 827, [FastStore] connection manager thread start\n[2021-05-14 16:14:55] INFO - file: sf_nio.c, line: 205, connect to server 192.168.126.101:21016 successfully\n```\n查看文件目录是否成功挂载\n```\n$ df -h\nFilesystem           Size  Used Avail Use% Mounted on\ndevtmpfs             189M     0  189M   0% /dev\ntmpfs                219M     0  219M   0% /dev/shm\ntmpfs                219M  6.6M  212M   4% /run\ntmpfs                219M     0  219M   0% /sys/fs/cgroup\n/dev/mapper/cs-root   47G  4.9G   43G  11% /\n/dev/sda1           1014M  242M  773M  24% /boot\ntmpfs                 44M  4.0K   44M   1% /run/user/1000\n/dev/fuse             38G     0   38G   0% /opt/fastcfs/fuse  // 这是即是FastCFS挂载的根目录\n```\n5. 验证 ：在201节点上创建文件\n```\n[first@192.168.126.201 /opt/fastcfs/fuse]\n$ mkdir -p abc/123\n[first@192.168.126.201 /opt/fastcfs/fuse]\n$ touch abc/123/demo.txt\n[first@192.168.126.201 /opt/fastcfs/fuse]\n$ vim abc/123/demo.txt\n[first@192.168.126.201 /opt/fastcfs/fuse]\n$ ll abc/123\ntotal 1\n-rw-rw-r--+ 1 first first 13 May 14 16:22 demo.txt  \n```\n在202节点上查看该文件，可见文件已经创建成功了\n```\n[first@192.168.126.202 /opt/fastcfs/fuse]\n$ tail abc/123/demo.txt\nhello world\n[first@192.168.126.202 /opt/fastcfs/fuse]\n```\n\n### 服务监控\n1. 查看FastDir集群状态\n```\n$ fdir_cluster_stat\nserver_id: 1, host: 192.168.126.101:11012, status: 23 (ACTIVE), is_master: 0\nserver_id: 2, host: 192.168.126.102:11012, status: 23 (ACTIVE), is_master: 0\nserver_id: 3, host: 192.168.126.103:11012, status: 23 (ACTIVE), is_master: 1\n\nserver count: 3\n```\n2. 查看单个FastDir节点状态\n```\n[first@192.168.126.201 /etc/fastcfs/fcfs]\n$ fdir_service_stat 192.168.126.101:11012\n        server_id: 1\n        status: 23 (ACTIVE)\n        is_master: false\n        connection : {current: 4, max: 4}\n        binlog : {current_version: 24}\n        dentry : {current_inode_sn: 1000009, ns_count: 1, dir_count: 3, file_count: 1}\n```\n3. 查看FastStore集群状态\n```\n$ fs_cluster_stat | more\n\ndata_group_id: 1\n        server_id: 1, host: 192.168.126.101:21016, status: 5 (ACTIVE), is_preseted: 1, is_master: 1, data_version: 0\n        server_id: 2, host: 192.168.126.102:21016, status: 5 (ACTIVE), is_preseted: 0, is_master: 0, data_version: 0\n        server_id: 3, host: 192.168.126.103:21016, status: 5 (ACTIVE), is_preseted: 0, is_master: 0, data_version: 0\n\ndata_group_id: 2\n        server_id: 1, host: 192.168.126.101:21016, status: 5 (ACTIVE), is_preseted: 0, is_master: 0, data_version: 0\n        server_id: 2, host: 192.168.126.102:21016, status: 5 (ACTIVE), is_preseted: 1, is_master: 1, data_version: 0\n.....................\n```\n4. 查看磁盘使用情况\n```\ndf -h \n```\n"
  },
  {
    "path": "docs/YUM-INSTALL-zh_CN.md",
    "content": "\n## yum安装方式\n\nyum 安装方式支持intel的x86_64和ARM的aarch64架构，主要用于测试和生产环境搭建。\n支持的Linux发行版如下：\n* CentOS & CentOS Stream\n* Fedora\n* Rocky\n* Anolis\n* AlmaLinux\n* RHEL (Red Hat Enterprise Linux)\n* Oracle Linux\n* Amazon Linux\n* Alibaba Cloud Linux\n* openEuler\n* Kylin\n* UOS\n\n### 1. 安装FastOS.repo\n\n先安装FastOS.repo yum源，然后就可以安装FastCFS相关软件包了。\n\nCentOS 7、RHEL 7、Oracle Linux 7、Alibaba Cloud Linux 2、Anolis OS 7、AlmaLinux 7、Amazon Linux 2、Fedora 27及以下版本：\n```\nrpm -ivh http://www.fastken.cn/yumrepo/el7/noarch/FastOSrepo-1.0.1-1.el7.noarch.rpm\n```\n\nCentOS 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]：\n```\nrpm -ivh http://www.fastken.cn/yumrepo/el8/noarch/FastOSrepo-1.0.1-1.el8.noarch.rpm\n```\n\nCentOS 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及以上版本：\n```\nrpm -ivh http://www.fastken.cn/yumrepo/el9/noarch/FastOSrepo-1.0.1-1.el9.noarch.rpm\n```\n\n\n### 2. fastDIR server安装\n\n在需要运行 fastDIR server的服务器上执行：\n```\nyum install fastDIR-server -y\n```\n\n### 3. faststore server安装\n\n在需要运行 faststore server的服务器上执行：\n```\nyum install faststore-server -y\n```\n\n### 4. FastCFS客户端安装\n\n在需要使用FastCFS存储服务的机器（即FastCFS客户端）上，除了openEuler之外的其他Linux发行版执行：\n```\nyum install FastCFS-fused -y\n```\n\n对于openEuler 20.03和Kylin V10，因需安装的fuse3和系统已有的fuse在依赖关系上存在冲突，需要用rpm命令强制安装fuse3，执行：\n```\narch=$(uname -r | awk -F '.' '{print $NF}');\ndist=el8;\nver='3.16.2-2';\nrpm -ivh http://www.fastken.cn/yumrepo/$dist/$arch/fuse3-libs-$ver.$dist.$arch.rpm  \\\n         http://www.fastken.cn/yumrepo/$dist/$arch/fuse-common-$ver.$dist.$arch.rpm \\\n         http://www.fastken.cn/yumrepo/$dist/$arch/fuse3-$ver.$dist.$arch.rpm --force --nodeps;\n\nyum install FastCFS-fused -y\n```\n\n对于openEuler 22.03及更高版本，直接执行：\n```\nyum install FastCFS-fused -y\n```\n\n### 5. Vote server安装（可选）\n\nVote server作为FastCFS多个服务模块共用的选举节点，主要作用是实现双副本防脑裂（即双活互备防脑裂）。\n\n在需要运行选举节点的服务器上执行：\n```\nyum install FastCFS-vote-server -y\n```\n\n### 6. Auth server安装（可选）\n\n如果需要存储池或访问控制，则需要安装本模块。\n\n在需要运行 Auth server的服务器上执行：\n```\nyum install FastCFS-auth-server -y\n```\n\n安装完成后，需要修改对应的配置文件，服务才可以正常启动。请参阅[配置指南](CONFIGURE-zh_CN.md)\n\nFastCFS后台程序可通过systemd管理。systemd服务名称如下：\n```\n  * fastdir： 目录服务，对应程序为 fdir_serverd\n  * faststore：存储服务，对应程序为 fs_serverd\n  * fastcfs： FUSE后台服务，对应程序为 fcfs_fused\n  * fastvote： 选举节点，对应程序为 fcfs_voted\n  * fastauth： 认证服务，对应程序为 fcfs_authd\n```\n\n可以使用标准的systemd命令对上述5个服务进行管理，例如：\n```\nsystemctl restart fastdir\nsystemctl restart faststore\nsystemctl restart fastcfs\nsystemctl restart fastvote\nsystemctl restart fastauth\n```\n### 6. 一个包含5个节点的安装过程的详细文档\n请参与[5节点安装文档](YUM-INSTALL-Diy-5nodes-zh_CN.md)\n"
  },
  {
    "path": "docs/benchmark-step-by-step.md",
    "content": "\n## FastCFS性能测试参考文档\n\n服务器之间需要实现免密登录，集群部署参见：[FastCFS集群部署工具介绍](fcfs-ops-tool-zh_CN.md)\n\n### 集群快速部署\n\n在施压服务器（即client机器）上执行如下命令：\n\n```\ncurl -o /usr/bin/fcfs.sh http://fastken.cn/fastcfs/ops/fcfs.sh && sudo chmod +x /usr/bin/fcfs.sh;\ncurl -o /usr/bin/fcfs_conf.sh http://fastken.cn/fastcfs/ops/fcfs_conf.sh && sudo chmod +x /usr/bin/fcfs_conf.sh;\n\nmkdir -p fastcfs-ops && cd fastcfs-ops;\nserver_ips='请替换为西文逗号分隔的服务器IP列表';\nclient_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);\ncat > fcfs_conf.settings <<EOF\nfastcfs_version=5.0.0\n\nvote_ips=$server_ips\nauth_ips=$server_ips\nfdir_ips=$server_ips\nfstore_group_count=1\nfstore_group_1=$server_ips\ndata_group_count=64\nEOF\n\n\ncat > fcfs.settings <<EOF\nfastcfs_version=5.0.0\n\nfuseclient_ips=$client_ip\nEOF\n\nfcfs_conf.sh create;\n```\n\n修改conf/fdir/cluster.conf 和 conf/fstore/cluster.conf 的配置项 communication，配置示例如下：\n\n```\n# config the auth config filename\nauth_config_filename = ../auth/auth.conf\n\n# the communication value list:\n##  socket: TCP over ethernet or RDMA network\n##  rdma: RDMA network only\n# default value by the global config with same name\ncommunication = socket\n\n# smart polling mode for RDMA network\n# auto detect to poll by Linux epoll or RDMA busy polling\n# default value is true\nsmart_polling = true\n\n# switch the polling approach between Linux epoll and RDMA busy polling\n# switch to RDMA busy polling when IOPS >= this parameter,\n# otherwise switch to Linux epoll\n# default value is 10240\npolling_switch_on_iops = 1024\n```\n\n修改conf/fcfs/fuse.conf的配置项，配置示例如下：\n\n```\n# the mount point (local path) for FUSE\n# the local path must exist\nmountpoint = /opt/fastcfs/fuse\n\n# if use busy polling for RDMA network\n# should set to true for HPC\n# default value is false\nbusy_polling = true\n\n\n[read-ahead]\n# if enable read ahead feature for FastStore\n# default value is true\nenabled = false\n```\n\n首次部署：\n```\nfcfs.sh setup;\n```\n\n修改配置后重新部署：\n```\nfcfs.sh config;\nfcfs.sh restart;\n```\n\n\n### 安装压测工具\n\nCentOS、Alibaba Cloud Linux等发行版：\n```\nyum install fio FastCFS-api-tests -y\n```\n\nUbuntu 或者Debian：\n```\napt install fio fastcfs-api-tests -y\n```\n\n### fio压测示例\n\n```\ncd /opt/fastcfs/fuse;\njobnum=4; rw=randread;\nout_path=FastCFS-fio/$rw-$jobnum;\nlog_file=$out_path/fio;\nmkdir -p $out_path;\n\nfio --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\n```\n\n### fcfs_beachmark压测示例\n```\nfcfs_beachmark -m randread -s 256M -T 4  -t 300 -f  /opt/fastcfs/fuse/test_file\n```\n"
  },
  {
    "path": "docs/benchmark.md",
    "content": "\n## FastCFS vs. Ceph benchmark under 3 nodes cluster (three copies of data)\n\n<table border=1 cellspacing=0 cellpadding=4 width=640 style=\"border:2px solid\">\n<thead>\n<tr style=\"background-color:#D3D3D3\">\n<td width=128 rowspan=2 align=center><b>read/write type</b></td>\n<td width=128 rowspan=2 align=right><b>fio jobs &nbsp;</b></td>\n<td width=256 colspan=2 align=center><b>IOPS（4KB Block）</b></td>\n<td width=128 rowspan=2 align=right><b>ratio &nbsp;</b></td>\n</tr>\n<tr style=\"background-color:#D3D3D3\">\n<td width=128 align=right><b>FastCFS &nbsp;</b></td>\n<td width=128 align=right><b>Ceph &nbsp;</b></td>\n</tr>\n</thead>\n\n<tr style=\"background-color:#F5F5F5\">\n<td width=128 rowspan=3 align=center><b>sequential write</b></td>\n<td width=128 align=right>4 &nbsp;</td>\n<td width=128 align=right>32,256 &nbsp;</td>\n<td width=128 align=right>5,120 &nbsp;</td>\n<td width=128 align=right>630% &nbsp;</td>\n</tr>\n\n<tr style=\"background-color:#F5F5F5\">\n<td align=right>8 &nbsp;</td>\n<td align=right>55,296 &nbsp;</td>\n<td align=right>8,371 &nbsp;</td>\n<td align=right>661% &nbsp;</td>\n</tr>\n\n<tr style=\"background-color:#F5F5F5\">\n<td align=right>16 &nbsp;</td>\n<td align=right>76,800 &nbsp;</td>\n<td align=right>11,571 &nbsp;</td>\n<td align=right>664% &nbsp;</td>\n</tr>\n\n<tr>\n<td rowspan=3 align=center><b>rand write</b></td>\n<td align=right>4 &nbsp;</td>\n<td align=right>6,374 &nbsp;</td>\n<td align=right>4,454 &nbsp;</td>\n<td align=right>143% &nbsp;</td>\n</tr>\n\n<tr>\n<td align=right>8 &nbsp;</td>\n<td align=right>11,264 &nbsp;</td>\n<td align=right>6,400 &nbsp;</td>\n<td align=right>176% &nbsp;</td>\n</tr>\n\n<tr>\n<td align=right>16 &nbsp;</td>\n<td align=right>16,870 &nbsp;</td>\n<td align=right>7,091 &nbsp;</td>\n<td align=right>238% &nbsp;</td>\n</tr>\n\n<tr style=\"background-color:#F5F5F5\">\n<td rowspan=3 align=center><b>sequential read</b></td>\n<td align=right>4 &nbsp;</td>\n<td align=right>34,880 &nbsp;</td>\n<td align=right>14,848 &nbsp;</td>\n<td align=right>235% &nbsp;</td>\n</tr>\n\n<tr style=\"background-color:#F5F5F5\">\n<td align=right>8 &nbsp;</td>\n<td align=right>62,751 &nbsp;</td>\n<td align=right>24,883 &nbsp;</td>\n<td align=right>252% &nbsp;</td>\n</tr>\n\n<tr style=\"background-color:#F5F5F5\">\n<td align=right>16 &nbsp;</td>\n<td align=right>86,508 &nbsp;</td>\n<td align=right>38,912 &nbsp;</td>\n<td align=right>222% &nbsp;</td>\n</tr>\n\n<tr>\n<td rowspan=3 align=center><b>rand read</b></td>\n<td align=right>4 &nbsp;</td>\n<td align=right>12,527 &nbsp;</td>\n<td align=right>12,160 &nbsp;</td>\n<td align=right>103% &nbsp;</td>\n</tr>\n\n<tr>\n<td align=right>8 &nbsp;</td>\n<td align=right>23,610 &nbsp;</td>\n<td align=right>22,220 &nbsp;</td>\n<td align=right>106% &nbsp;</td>\n</tr>\n\n<tr>\n<td align=right>16 &nbsp;</td>\n<td align=right>41,790 &nbsp;</td>\n<td align=right>36,608 &nbsp;</td>\n<td align=right>114% &nbsp;</td>\n</tr>\n</table>\n\nmore info see Chinese doc: [FastCFS vs. Ceph benchmark detail](benchmark-20210621.pdf)\n"
  },
  {
    "path": "docs/cluster-expansion-zh_CN.md",
    "content": "\n# FastCFS集群扩容手册\n\nFastCFS集群三个服务组件：fauth、fdir 和fstore，下面将分别介绍这三个服务组件的扩容方法和步骤。\n\n## 1. fauth（认证服务）\n\n认证服务的用户和权限等数据保存在fdir中，服务采用热备模式，建议部署2 ~ 3个服务器（节点），主节点失效后会自动切换到备用节点。\n因认证服务器自身不保存数据，因此可以根据实际需要随时增加或减少认证服务器。\n\n## 2. fdir（目录服务）\n\nFastCFS目录服务用于文件元数据管理，目前只支持一组服务器，使用常规服务器（如64GB内存 + SSD）可以支持百亿级文件。\n详情参见[高性能大容量分布式目录服务FastDIR简介](https://my.oschina.net/u/3334339/blog/5405816)\n\n建议将fdir部署在3台服务器上，可以根据实际需要随时增加或减少fdir服务器，fdir将基于binlog自动完成数据同步。\n\n## 3. fstore（数据存储）\n\nfstore 采用分组方式保存文件内容。为了便于扩容，FastCFS引入了数据分组（DG）的概念。\n\n按4MB分块的文件内容（文件块），根据其hash code（文件ID + 文件块偏移量），路由到对应的数据分组。计算公式如下：\n```\nDGI = HashCode % DGC\n\nDGI：数据分组索引号\nHashCode：文件分块哈希值\nDGC：数据分组总数\n```\n\n数据分组是逻辑或虚拟概念，映射到的物理或实体概念是服务器分组（SG），二者是多对一关系，即：一个服务器分组（SG）可以容纳多个数据分组（DG）。\n\nfstore集群的服务器分组总数，英文缩写为SGC。\n\nDG 和SG映射关系配置在fstore的cluster.conf中，配置示例片段（简洁起见，只配置了一个3副本的SG）：\n```\n# SGC\nserver_group_count = 1\n\n# DGC\ndata_group_count = 256\n\n# 配置SG1\n[server-group-1]\nserver_ids = [1-3]\ndata_group_ids = [1, 256]\n\n```\n\nfstore集群扩容，可以一次增加一个SG。当集群规模较小（比如SGC小于等于4）时，建议一次扩容一倍（SGC翻番）。\n\nDGC一旦确定就不可更改，除非建立新集群。因此在初始化集群时，需要确定好数据分组总数（DGC），可以根据业务发展规划，充分预估出DGC。\n\n比如根据存储量预估5年后需要的服务器分组数（SGC）为20，为了充分发挥多核性能，每台服务器上跑32个数据分组（DG），DGC为20 * 32 = 640，按2次幂向上对齐，最终配置为1024。\n\n* 友情提示：建议生产环境DGC至少配置为256。\n\nfstore在线扩容分为两个步骤修改cluster.conf：\n1. 增加扩容的SG 和迁移过去的DG映射；\n2. 新增的SG自动同步完成后，将原有SG迁移出去的DG映射删除。\n\n上述两个步骤将cluster.conf修改完成后，都需要将cluster.conf分发到fstore集群和fuseclient，然后重启fstore集群和fuseclient。推荐重启步骤如下：\n```\n 1. 停止fuseclient\n 2. 重启fstore集群\n 3. 启动fuseclient\n```\n\n将上述配置示例的1个SG扩容为2个SG（均采用3副本），我们如何调整cluster.conf：\n\n### 步骤1\n修改后的cluster.conf内容片段如下：\n\n```\n# SGC，由1扩容为2\nserver_group_count = 2\n\n# DGC\ndata_group_count = 256\n\n# 修改DG映射，将 [129, 256]迁移至SG2\n[server-group-1]\nserver_ids = [1-3]\ndata_group_ids = [1, 128]\n\n# 配置SG2\n# 必须加上server [1-3]用于同步已有数据\n[server-group-2]\nserver_ids = [1-6]\ndata_group_ids = [129, 256]\n```\n\n将cluster.conf分发到fstore集群所有服务器以及所有fuseclient后，重启fstore集群和fuseclient。\n\n等待新增的SG同步完成，然后进入步骤2。\n\n* 友情提示：\n   * 数据同步过程中fstore集群正常提供服务；\n   * 可以使用工具 fs_cluster_stat 查看fstore集群状态，比如：\n```\n# 查看ACTIVE列表，确保所有服务器状态为ACTIVE，确认一下最后一行输出的总数\nfs_cluster_stat -A\n\n# 查看非ACTIVE列表，确保输出为空\nfs_cluster_stat -N\n\n# 抽查某个数据分组（如ID为256）的ACTIVE状态，确保全部为ACTIVE\nfs_cluster_stat -g 256\n\n# 查看帮助\nfs_cluster_stat -h\n```\n\n### 步骤2\n修改后的cluster.conf内容片段如下：\n\n```\n# SGC\nserver_group_count = 2\n\n# DGC\ndata_group_count = 256\n\n[server-group-1]\nserver_ids = [1-3]\ndata_group_ids = [1, 128]\n\n# 去掉server [1-3]\n[server-group-2]\nserver_ids = [4-6]\ndata_group_ids = [129, 256]\n```\n\n将cluster.conf分发到fstore集群所有服务器以及所有fuseclient后，重启fstore集群和fuseclient\n\n### 清除已迁走的数据\n数据迁移完成后，为了清除迁移出去的数据占用空间，V3.2支持清除迁移出去的replica和slice binlog，启动 fs_serverd时带上参数 --migrate-clean 即可，示例如下：\n```\n/usr/bin/fs_serverd  /etc/fastcfs/fstore/server.conf restart --migrate-clean\n```\n\n**注意：** 为了防止误操作，正常情况下启动fs_serverd **不要** 使用--migrate-clean！\n\n* 友情提示：\n   * 配置文件分发和程序启停可以使用fcfs.sh，使用说明参见[FastCFS集群运维工具](fcfs-ops-tool-zh_CN.md)\n   * 步骤1和2重启fstore集群和fuseclient的过程，会导致服务不可用，建议在业务低峰期进行\n"
  },
  {
    "path": "docs/fcfs-ops-tool-zh_CN.md",
    "content": "# FastCFS 运维工具介绍\n\n* [fcfs.sh](#fcfs.sh) -- 用于管理 FastCFS 集群的快捷运维工具\n* [fcfs_conf.sh](#fcfs_conf.sh) -- 用于快速创建集群配置文件的工具\n\n## 1. fcfs.sh 介绍\n\nfcfs.sh 是一个用于快速部署和管理 FastCFS 集群的脚本工具。它基于 SSH 访问协议连接远程节点服务器，并使用服务器上带有 sudo 功能的账号（也可以使用root）进行相关命令的执行。它只需在你的工作站机器上运行，不需要服务器、数据库或者其他额外资源。\n\n如果你需要经常搭建、卸载或配置 FastCFS集群，又想节省大量的重复工作，那么它很适合你。\n\n***注： 该脚本工具目前适用于以下操作系统：***\n\n* CentOS 7 和 CentOS 8 及以上版本\n* Red Hat Linux 8 及以上版本\n* Rocky Linux 8 及以上版本\n* Anolis 7及以上版本\n* AlmaLinux 7及以上版本\n* Fedora Linux 20 及以上版本\n* Oracle Linux 8 及以上版本\n* Amazon Linux\n* Alibaba Cloud Linux\n* BigCLoud 21及以上版本\n* openEuler 20及以上版本\n* Kylin V10及以上版本\n* UOS 20及以上版本\n* Debian 10 及以上版本\n* Ubuntu 18.04 及以上版本\n\n<a name=\"fcfs.sh\"></a>\n### 1.1. 这个工具能做什么？\n\n它能快速构建 FastCFS 集群，包括安装软件、分发配置文件，以及管理集群服务和查看集群日志。\n\n它也能对 FastCFS 单节点或整个集群的相关软件进行升级或降级。\n\n它能重新安装软件或者重新分发配置文件。\n\n### 1.2. 这个工具不能做什么？\n\n它不是一个通用的部署工具， 仅适用于 FastCFS 集群。除了将配置文件分发到集群的各个服务器节点以外，它不对集群配置做任何处理。你必须为它提供可用的集群配置文件。\n\n### 1.3. 获取 fcfs.sh\n\n从以下地址下载 fcfs.sh，并将其放置在工作站机器的bin目录下，以便命令行能够找到：\n\n> http://fastken.cn/fastcfs/ops/fcfs.sh\n\n获取命令：\n\n> sudo curl -o /usr/bin/fcfs.sh  http://fastken.cn/fastcfs/ops/fcfs.sh && sudo chmod +x /usr/bin/fcfs.sh\n\n下载完成后可通过命令 ***which fcfs.sh*** 测试工具是否生效。\n\n### 1.4. 使用工具的前置条件\n\n* fcfs.settings -- 集群运维配置，放置在当前工作目录中\n* conf -- 集群配置文件目录，放置在当前工作目录中\n* 远程服务器 SSH 免密登录，参见 [5. SSH 免密登录](#SSH-password-free-login)\n\n你必须为每个集群创建一个独立的工作目录（例如：***fastcfs-ops***），将 ***fcfs.settings*** 和 ***conf*** 放入其中。然后所有针对该集群的后续命令操作，都必须在该工作目录中执行。\n\n#### 1.4.1. fcfs.settings\n\nfcfs.settings 包括两个字段 ***fastcfs_version*** 和 ***fuseclient_ips***，fastcfs_version 用于指定 FastCFS 主版本号，fuseclient_ips 用于指定要部署 fuse client 的服务器主机。当需要对 FastCFS 集群软件进行升级或降级操作时，你需要先修改 fastcfs_version 对应的版本号为你期望的版本。\n\nfcfs.settings 配置文件内容举例:\n\n```\n# 要安装的集群版本号（例如：5.5.0）\nfastcfs_version=5.5.0\n\n# 要安装 fuseclient 客户端的IP列表，多个ip以英文逗号分隔\nfuseclient_ips=10.0.1.14\n```\n\n***注：推荐使用 5.5.0 及更高版本用于集群部署。***\n\n#### 1.4.2. conf\n\nFastCFS集群的所有配置文件必须提前放置在工作目录中的 ***conf*** 目录，conf 包含以下四个子目录：\n\n* vote -- fvote 服务器的配置文件\n* fdir -- fdir 服务器的配置文件\n* fstore -- fstore 服务器的配置文件\n* auth -- fauth 服务器的配置文件\n* fcfs -- fuseclient 客户端的配置文件\n\n**获取配置文件样例**\n\n```\nmkdir fastcfs-ops\ncd fastcfs-ops/\ncurl -o fcfs-config-sample.tar.gz http://fastken.cn/fastcfs/ops/fcfs-config-sample.tar.gz\ntar -xzvf fcfs-config-sample.tar.gz\n```\n\n**使用fcfs_conf.sh创建集群配置文件**\n\n也可以根据自己的需要，使用脚本工具 fcfs_conf.sh 创建 FastCFS集群配置文件。参见：[fcfs_conf.sh](#fcfs_conf.sh)\n\n<a name=\"SSH-password-free-login\"></a>\n### 1.5. SSH 免密登录\n\nfcfs.sh 将通过 SSH 连接所有集群主机。它需要工作站机器可以通过 SSH 在远程主机上执行脚本，执行命令的用户身份可以是 root 或带 sudo的用户，并且必须支持免密登录。\n\n为了开启免密登录，你需要先为 SSH 生成 rsa 公钥/私钥对：\n\n> ssh-keygen -t rsa -C cfs -f cfs-ssh-key\n\n将生成密钥文件：\n\n* cfs-ssh-key  -- 私钥文件\n* cfs-ssh-key.pub -- 公钥文件\n\n#### 1.5.1. 部署私钥\n\n将私钥文件 cfs-ssh-key 拷贝到工作站机器的当前用户home目录 ***\"~/.ssh/\"***，然后将下面的配置片段加入配置文件 ~/.ssh/config（每个集群主机一份）：\n\n```\nHost [node ip]\n    HostName [node host name]\n    User [ssh user]\n    Port 22\n    PubkeyAuthentication yes\n    IdentityFile ~/.ssh/cfs-ssh-key\n```\n\n#### 1.5.2. 部署公钥\n\n将公钥文件内容加入到每个节点服务器sudo 用户的home目录下 ***.ssh/authorized_keys*** 文件中：\n\n> ssh-copy-id -i cfs-ssh-key.pub [user name]@[node server ip]\n\n并确保服务器上sshd的配置文件中开启了以下配置参数：\n\n```\nRSAAuthentication yes\nPubkeyAuthentication yes\nAuthorizedKeysFile      .ssh/authorized_keys\n```\n\n### 1.6. 工具命令介绍\n\nfcfs.sh工具的命令包括三部分：command，module 和 node。Command 是必须的，module 和 node 是可选的。\n\n用法:\n\n> fcfs.sh command [module] [node]\n\n**Commands**:\n\n* setup -- 安装并运行 FastCFS 软件的快捷命令，组合了 install、config 和 restart三个命令\n* install -- 安装 FastCFS 软件\n* reinstall -- 重新安装 FastCFS 软件\n* remove -- 移除 FastCFS 软件，功能与 erase 命令相同\n* erase -- 移除 FastCFS 软件\n* config -- 将集群配置文件拷贝到目标主机目录\n* start -- 启动集群中的所有或单个模块服务\n* restart -- 重启集群中的所有或单个模块服务\n* stop -- 停止集群中的所有或单个模块服务\n* tail -- 查看指定模块日志的最后一部分内容（等同于远程服务器的tail命令）\n* status -- 查看指定模块服务进程状态\n* help -- 查看命令的详细说明和样例\n\n**Modules**:\n\n* fvote -- Vote 服务器\n* fdir -- fastDIR 服务器\n* fstore -- faststore 服务器\n* fauth -- FastCFS auth 服务器\n* fuseclient -- FastCFS fuse 客户端\n\n**Node**:\n\n可以用于指定要执行命令的单个集群节点主机名或IP，如果不指定，命令将在所有节点上执行。node 参数必须在使用了 module 参数的情况下使用，不能单独直接使用。\n\n#### 1.6.1. FastCFS 一键安装\n\n你可以使用命令 ***setup*** 在集群节点上快速安装 FastCFS 软件，并自动配置和启动模块服务.\n\n**样例**\n\n安装并启动整个 FastCFS 集群:\n\n> fcfs.sh setup\n\n在所有 fdir 节点上安装并启动 fdir 服务器:\n\n> fcfs.sh setup fdir\n\n在节点 10.0.1.11 上安装 fdir 服务器：\n\n> fcfs.sh setup fdir 10.0.1.11\n\n节点 10.0.1.11 必须属于 fdir 集群，否则命令将失败。\n\n#### 1.6.2. FastCFS 软件安装\n\n你可以使用命令 ***install*** 只安装 FastCFS 软件。这个命令是 setup 命令的一部分。\n\n第一次执行该命令时，必须在所有节点上安装所有软件（也就是说，此时不能指定module和node）。之后，当需要增加新节点时，可以携带 module 和 node 参数使用。\n\n**样例**\n\n在所有节点安装所需的 FastCFS 软件:\n\n> fcfs.sh install\n\n在所有 fdir 节点上安装 fdir 服务器：\n\n> fcfs.sh install fdir\n\n在 10.0.1.11 节点上安装 fdir 服务器：\n\n> fcfs.sh install fdir 10.0.1.11\n\n#### 1.6.3. FastCFS 软件升级\n\n当你想升级 FastCFS 软件时，你需要先将配置文件 fcfs.settings 中的 fastcfs_version 字段的值修改为新的版本号，然后执行 install 命令：\n\n> fcfs.sh install\n\n#### 1.6.4. FastCFS 软件降级\n\n如果你确实需要将 FastCFS 软件降级为旧的版本，你必须先执行 ***remove*** 命令：\n\n> fcfs.sh remove\n\n或:\n\n> fcfs.sh erase\n\n并将字段 fastcfs_version 的值修改为旧的版本号，然后执行 install 命令：\n\n> fcfs.sh install\n\n#### 1.6.5. FastCFS 软件卸载\n\n可以使用命令 ***remove*** 或 ***erase*** 卸载 FastCFS 软件。\n\n**样例**\n\n移除所有节点上的所有软件：\n\n> fcfs.sh remove\n\n移除所有 fdir 节点上的 fdir 服务器：\n\n> fcfs.sh remove fdir\n\n移除节点10.0.1.11上的 fdir 服务器：\n\n> fcfs.sh remove fdir 10.0.1.11\n\n#### 1.6.6. FastCFS 配置文件分发\n\n成功安装 FastCFS 软件之后，你必须通过执行命令 ***config*** 来将配置文件分发到各个集群节点。如果节点上的目标配置文件目录不存在，它将会自动创建它们。\n\n**样例**\n\n将所有模块的配置文件分发到所有相应的节点上：\n\n> fcfs.sh config\n\n将 fdir 服务器的配置文件分发到所有 fdir 节点上：\n\n> fcfs.sh config fdir\n\n将 fdir 服务器的配置文件分发到 fdir 节点 10.0.1.11 上：\n\n> fcfs.sh config fdir 10.0.1.11\n\n#### 1.6.7. 集群管理\n\n管理集群的命令包括 ***start***, ***stop***, ***restart***。你可以同时操作所有模块的所有节点，也可以同时操作某一模块的所有节点，也可以单独操作某一模块的某一个节点。\n\n如果想操作单个节点，你必须在 module 参数后指定 node（host）参数（也就是说，你不能同时操作某一节点上的所有模块）。\n\n**样例**\n\n启动所有节点上的所有服务：\n\n> fcfs.sh start\n\n重启所有 fdir 节点上的 fdir 服务：\n\n> fcfs.sh restart fdir\n\n停止 节点10.0.1.11上的 fdir 服务：\n\n> fcfs.sh stop fdir 10.0.1.11\n\n#### 1.6.8. 查看集群日志\n\n当你想查看 FastCFS 服务的最新日志时，你可以使用命令 ***tail*** 来显示指定模块日志文件的最后一部分。\n\n**样例**\n\n显示 10.0.1.11节点上 fdir 服务日志文件的最后 100 行：\n\n> fcfs.sh tail fdir 10.0.1.11 -n 100\n\n或:\n\n> fcfs.sh tail fdir 10.0.1.11 -100\n\n显示每个 fdir 服务器日志的最后 10 行：\n\n> fcfs.sh tail fdir\n\n#### 1.6.9. 查看集群服务进程状态\n\n当你想查看 FastCFS 服务的进程状态时，你可以使用命令 ***status*** 来显示指定模块的服务进程状态。\n\n**样例**\n\n显示 10.0.1.11 节点上 fdir 服务进程状态：\n\n> fcfs.sh status fdir 10.0.1.11\n\n显示每个 fdir 服务器进程状态：\n\n> fcfs.sh status fdir\n\n显示所有服务进程状态：\n\n> fcfs.sh status\n\n<a name=\"fcfs_conf.sh\"></a>\n## 2. fcfs_conf.sh 介绍\n\nfcfs_conf.sh 是一个快速创建 FastCFS集群配置文件的运维工具。它通过 bash shell 访问本地配置文件 fcfs_conf.settings。它只需在你的工作站机器上运行，不需要服务器、数据库或者其他额外资源。\n\n如果你需要在指定的服务器IP列表上快速生成集群配置文件，那么它很适合你。\n\n### 2.1. 获取 fcfs_conf.sh\n\n从以下地址下载 fcfs_conf.sh，并将其放置在工作站机器的bin目录下，以便命令行能够找到：\n\n> http://fastken.cn/fastcfs/ops/fcfs_conf.sh\n\n获取命令：\n\n> sudo curl -o /usr/bin/fcfs_conf.sh  http://fastken.cn/fastcfs/ops/fcfs_conf.sh && sudo chmod +x /usr/bin/fcfs_conf.sh\n\n下载完成后可通过命令 ***which fcfs_conf.sh*** 测试工具是否生效。\n\n### 2.2. 使用fcfs_conf.sh的前置条件\n\n* fcfs_conf.settings -- 生成集群配置文件的参数配置，放置在当前工作目录中\n\n你必须为每个集群创建一个独立的工作目录（例如：***fastcfs-ops***），将 ***fcfs_conf.settings***放入其中。然后在该工作目录中执行fcfs_conf.sh命令。\n\n#### 2.2.1. fcfs_conf.settings\n\nfcfs_conf.settings 包括以下六种字段：\n\n* fastcfs_version -- 要生成哪个版本集群的配置文件\n* vote_ips -- vote 服务器IP地址列表，多个IP以英文半角逗号分隔\n* auth_ips -- FastCFS auth 服务器IP地址列表，多个IP以英文半角逗号分隔\n* fdir_ips --  fastDIR服务器IP地址列表，多个IP以英文半角逗号分隔\n* fstore_group_count -- faststore服务器分组数量\n* fstore_group_[N] -- faststore服务器分组IP地址列表，多个IP以英文半角逗号分隔\n* data_group_count -- faststore服务区数据分组数（推荐64的整数倍，建议至少配置256）\n\n参数 fstore_group_count 设置的数量必须与 fstore_group_[N] 数量相匹配，并且编号从1开始递增。\n\nfcfs_conf.settings 配置文件内容举例:\n\n```\n# 要生成配置的集群版本号（例如：5.5.0）\nfastcfs_version=5.5.0\n\n# 集群主机列表和分组\nvote_ips=10.0.1.11,10.0.1.12,10.0.1.13\nauth_ips=10.0.1.11,10.0.1.12,10.0.1.13\nfdir_ips=10.0.1.11,10.0.1.12,10.0.1.13\nfstore_group_count=2\nfstore_group_1=10.0.1.11,10.0.1.12,10.0.1.13\nfstore_group_2=10.0.2.14,10.0.2.15,10.0.2.16\ndata_group_count=256\n```\n\n***注：推荐使用 5.5.0 及以后的版本用于生成集群配置文件，尤其是vote（投票）模块。***\n\n### 2.3. fcfs_conf.sh工具命令介绍\n\nfcfs_conf.sh工具的命令包括两部分：command，module。Command 是必须的，module 是可选的。\n\n用法:\n\n> fcfs_conf.sh command [module]\n\n**Commands**:\n\n* create -- 创建配置文件\n* help -- 查看命令的详细说明和样例\n\n**Modules**:\n\n* fvote -- vote 服务器\n* fdir -- fastDIR 服务器\n* fstore -- faststore 服务器\n* fauth -- FastCFS auth 服务器\n* fuseclient -- FastCFS fuse 客户端\n\n## 3. conf_tpl_tar.sh 介绍\n\n一个简单的集群配置文件模版打包工具，仅供FastCFS开发者使用。\n\n它将从FastCFS, fastDIR, faststore的源代码仓库将FastCFS 集群配置文件模版打包成tar包。\n\n这三个仓库必须在同一个目录，并且 conf_tpl_tar.sh 必须在子目录 FastCFS/shell/ 中执行。\n\n命令格式:\n\n```\nconf_tpl_tar.sh <version> [update]\n```\n\n* version -- 要创建模版压缩包的集群版本号.\n* update -- 在创建之前获取最新的源代码，可选。\n\n举例:\n\n> ./conf_tpl_tar.sh 5.5.0 update\n\n将在当前目录创建配置模版压缩包文件 **conf.5.5.0.tpl.tar.gz**。\n"
  },
  {
    "path": "docs/fcfs-ops-tool.md",
    "content": "# FastCFS ops tools introduction\n\n* [fcfs.sh](#fcfs.sh) -- a ops tool for quickly manage FastCFS clusters\n* [fcfs_conf.sh](#fcfs_conf.sh) -- a ops tool for quickly create FastCFS clusters config files\n\n## 1. fcfs.sh introduction\n\nfcfs.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.\n\nIf you set up and tear down FastCFS clusters a lot, and want minimal extra repeating works, this is for you.\n\n***Tip： This shell support for the following operating systems:***\n\n* CentOS 7, CentOS 8 or higher\n* Red Hat Linux 8 or higher\n* Rocky Linux 8 or higher\n* Fedora Linux 20 or higher\n* Oracle Linux 8 or higher\n* Amazon Linux\n* Alibaba Cloud Linux\n* Debian 10 or higher\n* Ubuntu 18.04 or higher\n\n<a name=\"fcfs.sh\"></a>\n### 1.1. What this tool can do?\n\nIt can quickly setup FastCFS cluster, include install softwares, distribute config files, manage cluster services and query cluster logs.\n\nIt can also upgrade or downgrade FastCFS softwares on single node or full cluster.\n\nIt can reinstall software or redistribute config files.\n\n### 1.2. What this tool cannot do?\n\nIt 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.\n\n### 1.3. Fetch fcfs.sh\n\nGet it from http://fastken.cn/fastcfs/ops/fcfs.sh and put it in workstation's bin path.\n\n> sudo curl -o /usr/bin/fcfs.sh  http://fastken.cn/fastcfs/ops/fcfs.sh && sudo chmod +x /usr/bin/fcfs.sh\n\n### 1.4. Use preconditions\n\n* fcfs.settings -- cluster ops settings, in current working directory\n* conf -- cluster config files, in current working directory\n* remote server SSH password-free login, see also [SSH password-free login](#SSH-password-free-login)\n\nYou 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.\n\n#### 1.4.1. fcfs.settings\n\nfcfs.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.\n\nfcfs.settings content Example:\n\n```\n# Version of FastCFS cluster\nfastcfs_version=5.5.0\n\n# Hosts which fuseclient will install，multiple hosts separated by comma\nfuseclient_ips=10.0.1.14\n```\n\n***Tip：Use version 5.5.0 and later for deploy.***\n\n#### 1.4.2. conf\n\nAll config files of FastCFS cluster must be put into working directory beforehand, conf include five subdirectories：\n\n* vote -- config files for fvote server\n* fdir -- config files for fdir server\n* fstore -- config files for fstore server\n* auth -- config files for fauth server\n* fcfs -- config files for fuseclient\n\n**Fetch config sample**\n\n```\nmkdir fastcfs-ops\ncd fastcfs-ops/\ncurl -o fcfs-config-sample.tar.gz http://fastken.cn/fastcfs/ops/fcfs-config-sample.tar.gz\ntar -xzvf fcfs-config-sample.tar.gz\n```\n\n**Create config files use fcfs_conf.sh**\n\nYou can also create FastCFS cluster config files use fcfs_conf.sh according to your own needs. see also [fcfs_conf.sh](#fcfs_conf.sh)\n\n<a name=\"SSH-password-free-login\"></a>\n### 1.5. SSH password-free login\n\nfcfs.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.\n\nTo enable password-free login, you need generate rsa key pair for ssh:\n\n> ssh-keygen -t rsa -C cfs -f cfs-ssh-key\n\nit will generate key files：\n\n* cfs-ssh-key  -- private key file\n* cfs-ssh-key.pub -- public key file\n\n#### 1.5.1. Deploy private key\n\nCopy 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):\n\n```\nHost [node ip]\n    HostName [node host name]\n    User [ssh user]\n    Port 22\n    PubkeyAuthentication yes\n    IdentityFile ~/.ssh/cfs-ssh-key\n```\n\n#### 1.5.2. Deploy public key\n\nCopy public key to file .ssh/authorized_keys in every node server's sudo user home:\n\n> ssh-copy-id -i cfs-ssh-key.pub [user name]@[node server ip]\n\nand ensure that the following lines are in server's sshd config:\n\n```\nRSAAuthentication yes\nPubkeyAuthentication yes\nAuthorizedKeysFile      .ssh/authorized_keys\n```\n\n### 1.6. Tool commands introduction\n\nThe tool's command include three parts: command, module and node. Command is required, module and node are optional.\n\nUsage:\n\n> fcfs.sh command [module] [node]\n\n**Commands**:\n\n* setup -- Setup FastCFS softwares, a shortcut command combines install, config, and restart\n* install -- Install FastCFS softwares\n* reinstall -- Reinstall FastCFS softwares\n* remove -- Remove FastCFS softwares, same as erase\n* erase -- Erase FastCFS softwares\n* config -- Copy cluster config files to target host path\n* start -- Start all or one module service in cluster\n* restart -- Restart all or one module service in cluster\n* stop -- Stop all or one module service in cluster\n* tail -- Display the last part of the specified module's log\n* status -- Display the service processes status\n* help -- Show the detail of commands and examples\n\n**Modules**:\n\n* fvote -- Vote server\n* fdir -- fastDIR server\n* fstore -- faststore server\n* fauth -- FastCFS auth server\n* fuseclient -- FastCFS fuse client\n\n**Node**:\n\nYou can specify a single cluster IP, or command will be executed on all nodes.\n\n#### 1.6.1. FastCFS setup\n\nYou can use command ***setup*** to quickly setup FastCFS softwares on nodes, then config and start module services.\n\n**Examples**\n\nSetup the full FastCFS cluster:\n\n> fcfs.sh setup\n\nSetup fdir server on all fdir nodes:\n\n> fcfs.sh setup fdir\n\nSetup fdir server on node 10.0.1.11:\n\n> fcfs.sh setup fdir 10.0.1.11\n\nnode 10.0.1.11 must belong to fdir cluster, or it will fail.\n\n#### 1.6.2. FastCFS softwares install\n\nYou can use command ***install*** to install softwares only. This command is part of setup.\n\nFirst 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.\n\n**Examples**\n\nInstall all FastCFS softwares on all nodes:\n\n> fcfs.sh install\n\nInstall fdir server on all fdir nodes:\n\n> fcfs.sh install fdir\n\nInstall fdir server on node 10.0.1.11:\n\n> fcfs.sh install fdir 10.0.1.11\n\n#### 1.6.3. FastCFS softwares upgrade\n\nWhen you want to upgrade FastCFS's software, you can modify fcfs.settings, change field fastcfs_version to new version, then execute install command:\n\n> fcfs.sh install\n\n#### 1.6.4. FastCFS softwares downgrade\n\nIf you realy want to downgrade FastCFS softwares to an old version, you must execute command ***remove*** first:\n\n> fcfs.sh remove\n\nor:\n\n> fcfs.sh erase\n\nand change field fastcfs_version to old version, then execute install command: \n\n> fcfs.sh install\n\n#### 1.6.5. FastCFS softwares remove\n\nYou can use command ***remove*** or ***erase*** to remove FastCFS softwares.\n\n**Examples**\n\nRemove all softwares on all nodes:\n\n> fcfs.sh remove\n\nRemove fdir server on all nodes:\n\n> fcfs.sh remove fdir\n\nRemove fdir server on node 10.0.1.11:\n\n> fcfs.sh remove fdir 10.0.1.11\n\n#### 1.6.6. FastCFS config files distribute\n\nAfter 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.\n\n**Exmaples**\n\nDistribute all module config files to all nodes respective：\n\n> fcfs.sh config\n\nDistribute fdir server config files to all fdir nodes：\n\n> fcfs.sh config fdir\n\nDistribute fdir server config files to fdir node 10.0.1.11：\n\n> fcfs.sh config fdir 10.0.1.11\n\n#### 1.6.7. Cluster manage\n\nManage 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.\n\nIf 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.\n\nStart all service on all nodes:\n\n> fcfs.sh start\n\nRestart fdir service on all fdir nodes:\n\n> fcfs.sh restart fdir\n\nStop fdir service on fdir node 10.0.1.11:\n\n> fcfs.sh stop fdir 10.0.1.11\n\n#### 1.6.8. Display cluster logs\n\nWhen you want to query FastCFS services last logs, you can use command ***tail*** to display the last part of the specified module's log.\n\n**Example**\n\nDisplay the last 100 lines of fdir server log:\n\n> fcfs.sh tail fdir 10.0.1.11 -n 100\n\nor:\n\n> fcfs.sh tail fdir 10.0.1.11 -100\n\nDisplay the last 10 lines of each fdir server log:\n\n> fcfs.sh tail fdir\n\n#### 1.6.9. Display cluster service status\n\nWhen you want to query FastCFS services process status, you can use command ***status*** to display the status of the specified module's service process.\n\n**Example**\n\nDisplay the status of fdir service process:\n\n> fcfs.sh status fdir 10.0.1.11\n\nDisplay the status of each fdir service process:\n\n> fcfs.sh status fdir\n\nDisplay the status of all service processes:\n\n> fcfs.sh status\n\n<a name=\"fcfs_conf.sh\"></a>\n## 2. fcfs_conf.sh introduction\n\nfcfs_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.\n\nIf you want to generate cluster config files with specify server ips quickly, this is for you.\n\n### 2.1. Fetch fcfs_conf.sh\n\nGet it from http://fastken.cn/fastcfs/ops/fcfs_conf.sh and put it in workstation's bin path.\n\n> sudo curl -o /usr/bin/fcfs_conf.sh  http://fastken.cn/fastcfs/ops/fcfs_conf.sh && sudo chmod +x /usr/bin/fcfs_conf.sh\n\n### 2.2. Use preconditions of fcfs_conf.sh\n\n* fcfs_conf.settings -- cluster ops settings for create config files, in current working directory\n\nYou 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.\n\n#### 2.2.1. fcfs_conf.settings\n\nfcfs.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.\n\n* fastcfs_version -- The FastCFS version you expect to create config files for.\n* vote_ips -- vote server IP list, multiple hosts separated by comma\n* auth_ips -- FastCFS auth server IP list, multiple hosts separated by comma\n* fdir_ips -- fastDIR server IP list, multiple hosts separated by comma\n* fstore_group_count -- faststore server group count\n* fstore_group_[N] -- faststore server group IP list, multiple hosts separated by comma\n* data_group_count - faststore data group count(multiples of 64 are recommended)\n\nParameter fstore_group_count's value must match fstore_group_[N], and N increases from 1。\n\nfcfs_conf.settings content Example:\n\n```\n# Version of FastCFS cluster\nfastcfs_version=5.5.0\n\n# Cluster hosts list and group count\nvote_ips=10.0.1.11,10.0.1.12,10.0.1.13\nauth_ips=10.0.1.11,10.0.1.12,10.0.1.13\nfdir_ips=10.0.1.11,10.0.1.12,10.0.1.13\nfstore_group_count=2\nfstore_group_1=10.0.1.11,10.0.1.12,10.0.1.13\nfstore_group_2=10.0.2.14,10.0.2.15,10.0.2.16\ndata_group_count=128\n```\n\n***Tip：Use version 5.5.0 and later for create config files, especially for vote server.***\n\n### 2.3. fcfs_conf.sh Tool commands introduction\n\nThe fcfs_conf.sh command include two parts: command and module. Command is required, module is optional.\n\nUsage:\n\n> fcfs_conf.sh command [module]\n\n**Commands**:\n\n* create -- Create config files.\n* help -- Show the detail of commands and examples\n\n**Modules**:\n\n* fvote -- Vote server\n* fdir -- fastDIR server\n* fstore -- faststore server\n* fauth -- FastCFS auth server\n* fuseclient -- FastCFS fuse client\n\n## 3. conf_tpl_tar.sh introduction\n\nA simple cluster config file templates pack tool for developer of FastCFS.\n\nIt will pack FastCFS cluster config file templates to a tar file from source code repository FastCFS, fastDIR, faststore. \n\nThree repository FastCFS, fastDIR, faststore must at same path, and execute conf_tpl_tar.sh in FastCFS/shell/\n\nCommand:\n\n```\nconf_tpl_tar.sh <version> [update]\n```\n\n* version -- Cluster version number to create template tar file.\n* update -- Pull the last source codes before create tar file. Optional\n\nExmaple:\n\n> ./conf_tpl_tar.sh 5.5.0 update\n\nIt will create tar file **conf.5.5.0.tpl.tar.gz** in current dir.\n"
  },
  {
    "path": "docs/index.md",
    "content": "# FastCFS -- a high performance general distributed file system for databases, K8s and VM etc.\n\nEnglish | [Chinese](./README-zh_CN.md)\n\n## 1. About\n\nFastCFS is a general distributed file system with strong consistency, high performance, high availability and supporting 10 billion massive files.\nFastCFS can be used as the back-end storage of databases (MySQL, PostgresSQL, Oracle etc.), K8s and NAS.\n\n## 2. Current Version\n\nV3.1.0\n\n## 3. Supported Platforms\n\n* Linux: Kernel version >= 3.10  (Full support)\n* MacOS or FreeBSD (Only server side)\n\n## 4. Dependencies\n\n* [libfuse](https://github.com/libfuse/libfuse) (version 3.9.4 or newer)\n    * [Python](https://python.org/) (version 3.5 or newer)\n    * [Ninja](https://ninja-build.org/) (version 1.7 or newer)\n    * [gcc](https://www.gnu.org/software/gcc/) (version 4.7.0 or newer)\n* [libfastcommon](https://github.com/happyfish100/libfastcommon) (tag: V1.0.55)\n* [libserverframe](https://github.com/happyfish100/libserverframe) (tag: V1.1.12)\n* [libdiskallocator](https://github.com/happyfish100/libdiskallocator) (tag: V1.0.1)\n* [fastDIR](https://github.com/happyfish100/fastDIR) (tag: V3.1.0)\n* [faststore](https://github.com/happyfish100/faststore) (tag: V3.1.0)\n* [FastCFS](https://github.com/happyfish100/FastCFS) (tag: V3.1.0)\n\n## 5. Installation\n\n### 5.1 DIY installation\n\nyou can use [Cluster Operation Tool](docs/fcfs-ops-tool.md) to deploy FastCFS\n\nstep by step please see [INSTALL](docs/INSTALL.md)\n\nrecommend to execute libfuse_setup.sh for compiling and installing libfuse\n\n### 5.2 easy installation\n\nlibfastcommon, libserverframe, fastDIR, faststore and FastCFS can be compiled, installed and auto configurated by fastcfs.sh\n\nfastcfs.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.\n\n```\ngit clone https://github.com/happyfish100/FastCFS.git; cd FastCFS/\n```\n\nfastcfs.sh usage:\n\n```\n* install: pull/update codes from gitee, then make and install\n* config: copy config files and configure them with local ip\n* start | stop | restart: for service processes control\n```\n\none click to build (deploy and run) single node demo environment (MUST run by root):\n\n```\n./helloWorld.sh\n```\n\nor execute following commands (MUST run by root):\n\n```\n./fastcfs.sh install\n./fastcfs.sh config --force\n./fastcfs.sh restart\n```\n\nnow you can see the mounted path of FastCFS by the command:\n\n```\ndf -h /opt/fastcfs/fuse | grep fuse\n```\n\n## 6. Benchmark\n\nFastCFS 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.\n\n* [FastCFS vs. Ceph benchmark](docs/benchmark.md)\n\n## 7. K8s CSI Driver\n\n[fastcfs-csi](https://github.com/happyfish100/fastcfs-csi)\n\n## 8. Chinese Relative articles\n\n<a href=\"https://blog.csdn.net/happy_fish100/\" target=\"_blank\">CSDN blog</a>\n\n## 9. TODO List\n\n*  [fstore] provide cluster tools for automatic expansion\n*  [fstore] hierarchical storage & slice merging: supporting two-level storage, such as SSD + HDD\n*  [fdir & fstore] binlog deduplication (fdir binlog, fstore replica & slice binlog)\n*  [fstore] after the machine recovery, the data masters should be rebalanced\n\n## 10. Contact us\n\nemail: 384681(at)qq(dot)com\n\nWeChat subscription: search \"fastdfs\" for the related articles (Chinese Only)\n"
  },
  {
    "path": "docs/shared-storage-guide-zh_CN.md",
    "content": "# 共享数据配置指南\n\nFastCFS客户端缓存默认是开启的，这主要针对独享数据场景（对同一文件单节点读写），也可以支持非实时场景下对同一文件一写多读。\n\n如果将FastCFS作为Oracle RAC等系统的共享存储（对同一文件多节点写入和多节点读取），为了保证数据一致性，需要关闭客户端相关缓存。\n\n默认安装的客户端配置文件为 /etc/fastcfs/fcfs/fuse.conf，下面列出需要修改的配置项：\n\n```\n[FastDIR]\n\n# 对于文件追加写或文件truncate操作，通过加锁避免冲突\n# if use sys lock for file append and truncate to avoid conflict\n# set true when the files appended or truncated by many nodes (FUSE instances)\n# default value is false\nuse_sys_lock_for_append = true\n\n# 禁用异步报告文件属性（即采用同步报告方式）\n# if async report file attributes (size, modify time etc.) to the FastDIR server\n# default value is true\nasync_report_enabled = false\n\n# 禁用合并写\n[write-combine]\n# if enable write combine feature for FastStore\n# default value is true\nenabled = false\n\n# 禁用预读机制\n[read-ahead]\n# if enable read ahead feature for FastStore\n# default value is true\nenabled = false\n\n\n[FUSE]\n# 禁用Linux对inode缓存\n# cache time for file entry in seconds\n# default value is 1.0s\nentry_timeout = 0.0\n\n# 禁用Linux对文件属性缓存\n# cache time for file attribute in seconds\n# default value is 1.0s\nattribute_timeout = 0.0\n\n# 禁用内核合并写\n# if enable kernel writeback cache\n# default value is true\nwriteback_cache = false\n\n# 禁用内核读缓存\n# if keep kernel cache for read\n# set to true for unshared data scene (private data for single node)\n# should set to false on shared data scene for multi nodes\n# default value is true\nkernel_cache = false\n\n```\n\n友情提示：配置文件修改后，需要重启fcfs_fused方可生效。\n"
  },
  {
    "path": "docs/version-history-zh_CN.md",
    "content": "# FastCFS 重大版本一览\n\n* V1.0：2020年12月第一个版本（历时12个月）\n* V2.0：2021年4月支持k8s（历时3个月）\n* V3.0：2021年12月实现fdir存储插件，支持百亿级海量文件（历时6个月）\n* V3.3：2022年4月生产环境可用（从V3.1到V3.3，历时4个月）\n* V3.6：2022年9月文件读写性能大幅提升（从V3.4到V3.6，历时5个月）\n* V3.7：2022年11月遵循POSIX ACL，有用户用作NFS后端存储（历时2个月）\n* V4.0：2023年6月实现fstore存储插件，支持单机PB级存储（历时4个月）\n* V4.3：2023年8月V4系列稳定版本（从V4.1到V4.3，历时2个月）\n* V5.0：2023年11月原生支持RDMA，解决网络瓶颈（历时4个月）\n* V5.3：2024年3月支持IPv6和bug修复（从V5.1到V5.3，历时4个月）\n* V5.4：2025年4月存储插件性能优化和bug修复（历时3个月）\n* V5.5：2025年11月适配io_uring高性能异步IO框架，最新稳定版本\n"
  },
  {
    "path": "docs/versions.json",
    "content": "[\n  {\"version\": \"v3.1.0\", \"title\": \"v3.2.0\", \"aliases\": [\"latest\"]},\n  {\"version\": \"v3.1.0\", \"title\": \"v3.1.0\", \"aliases\": []}\n]"
  },
  {
    "path": "fastcfs.sh",
    "content": "#!/bin/bash\n#\n# fastcfs install, config and start a local fastcfs cluster on Linux.\n# fastcfs's primary goals are to be the best shell for local FastCFS\n# application development and to support all fastcfs features that fit.\n#\n# fastcfs shell support commands\n#    setup -- combin install, config and restart, quickly setup single node FastCFS cluster\n#    install -- pull code, make, install\n#    config -- copy config files to default conf path(/etc/fastcfs/)\n#    start -- start fdir_serverd, fs_serverd, fcfs_fused service\n#    restart -- restart fdir_serverd, fs_serverd, fcfs_fused service\n#    stop -- stop fdir_serverd, fs_serverd, fcfs_fused service\n#\n# When setup, This shell will make and install these six libs in order:\n#     libfastcommon\n#     libserverframe\n#     libdiskallocator\n#     auth_client\n#     fastDIR\n#     faststore\n#     FastCFS\n#\n# If no source code in build path, it will git clone from:\n#     https://gitee.com/fastdfs100/libfastcommon.git\n#     https://gitee.com/fastdfs100/libserverframe.git\n#     https://gitee.com/fastdfs100/libdiskallocator.git\n#     https://gitee.com/fastdfs100/fastDIR.git\n#     https://gitee.com/fastdfs100/faststore.git\n#\n# FastCFS modules and repositores\nCOMMON_LIB=\"libfastcommon\"\nCOMMON_LIB_URL=\"https://gitee.com/fastdfs100/libfastcommon.git\"\nFRAME_LIB=\"libserverframe\"\nFRAME_LIB_URL=\"https://gitee.com/fastdfs100/libserverframe.git\"\nDISK_ALLOCATOR_LIB=\"libdiskallocator\"\nDISK_ALLOCATOR_LIB_URL=\"https://gitee.com/fastdfs100/libdiskallocator.git\"\nFDIR_LIB=\"fastDIR\"\nFDIR_LIB_URL=\"https://gitee.com/fastdfs100/fastDIR.git\"\nSTORE_LIB=\"faststore\"\nSTORE_LIB_URL=\"https://gitee.com/fastdfs100/faststore.git\"\nFASTCFS_LIB=\"..\"\nVOTECLIENT_LIB=\"..\"\nAUTHCLIENT_LIB=\"..\"\n\nSTORE_CONF_FILES=(client.conf server.conf cluster.conf storage.conf dbstore.conf)\nSTORE_CONF_PATH=\"/etc/fastcfs/fstore/\"\n\nFDIR_CONF_FILES=(client.conf cluster.conf server.conf dbstore.conf)\nFDIR_CONF_PATH=\"/etc/fastcfs/fdir/\"\n\nAUTH_CONF_FILES=(auth.conf client.conf cluster.conf server.conf session.conf)\nAUTH_CONF_PATH=\"/etc/fastcfs/auth/\"\nAUTH_KEYS_FILES=(session_validate.key)\nAUTH_KEYS_PATH=\"/etc/fastcfs/auth/keys/\"\n\nFUSE_CONF_FILES=(fuse.conf)\nFUSE_CONF_PATH=\"/etc/fastcfs/fcfs/\"\n\nYUM_OS_ARRAY=(Red Rocky Oracle Fedora CentOS AlmaLinux Alibaba Anolis Amazon openEuler Kylin UOS BigCLoud)\n\nBUILD_PATH=\"build\"\nthis_shell_name=$0\nmode=$1    # setup|install|config|start|restart|stop\nmake_shell=make.sh\nforce_reconfig=0\nuname=$(uname)\n\npull_source_code() {\n  if [ $# != 2 ]; then\n    echo \"ERROR: $0 - pull_source_code() function need repository name and url!\"\n    exit 1\n  fi\n\n  module_name=$1\n  module_url=$2\n  if ! [ -d $module_name ]; then\n    echo \"INFO: The $module_name local repository does not exist!\"\n    echo \"INFO: =====Begin to clone $module_name , it will take some time...=====\"\n    git clone $module_url\n    git checkout master\n  else\n    echo \"INFO: The $module_name local repository have existed.\"\n    cd $module_name\n    echo \"INFO: =====Begin to pull $module_name, it will take some time...=====\"\n    git checkout master\n    git pull\n    cd ..\n  fi\n}\n\nmake_op() {\n  if [ $# -lt 3 ]; then\n    echo \"ERROR: $0 - make_op() function need module repository name and mode!\"\n    exit 1\n  fi\n\n  module_name=$1\n  module_src_path=$2\n  extend_options=$4\n  make_mode=$3\n\n  module_lib_path=$BUILD_PATH/$module_src_path\n  if ! [ -d $module_lib_path ]; then\n    echo \"ERROR: $0 - module repository {$module_name} does not exist!\"\n    echo \"ERROR: You must checkout from remote repository first!\"\n    exit 1\n  else  \n    cd $module_lib_path\n    echo \"INFO: =====Begin to $make_mode module [$module_name]=====\"\n    command=\"./$make_shell\"\n    if [ ${#extend_options} -gt 0 ]; then\n      command=\"$command $extend_options\"\n    fi\n    if [ $make_mode != \"make\" ]; then\n      command=\"$command $make_mode\"\n    fi\n    echo \"INFO: Execute command ($command) in path ($PWD)\"\n    result=`$command`\n    echo \"INFO: Execute result=(\"\n    echo \"$result\"\n    echo \")\"\n    cd -\n  fi\n}\n\nsed_replace()\n{\n    sed_cmd=$1\n    filename=$2\n    if [ \"$uname\" = \"FreeBSD\" ] || [ \"$uname\" = \"Darwin\" ]; then\n       sed -i \"\" \"$sed_cmd\" $filename\n    else\n       sed -i \"$sed_cmd\" $filename\n    fi\n}\n\nsplit_to_array() {\n  if ! [ -z $2 ]; then\n    IFS=',' read -ra $2 <<< \"$1\"\n  fi\n}\n\nplaceholder_replace() {\n  filename=$1\n  placeholder=$2\n  value=$3\n  sed_replace \"s#\\${$placeholder}#$value#g\" $filename\n}\n\nparse_config_arguments() {\n  for arg do\n    case \"$arg\" in\n      --force)\n        force_reconfig=1\n      ;;\n    esac\n  done\n}\n\ncheck_paths_infile() {\n  # Check all paths in this config file, if not exist, will create it.\n  check_conf_file=$1\n  check_result=`sed -n \\\n-e '/^mountpoint *=/ p' \\\n-e '/^base_path *=/ p' \\\n-e '/^path *=/ p' \\\n$check_conf_file|sed 's/\\([^=]*\\)=\\([^=]\\)/\\2/g'`\n\n  for check_path in ${check_result[@]}; do\n    if ! [ -d $check_path ]; then\n      if ! mkdir -p $check_path; then\n        echo \"ERROR: Create target path in file $check_conf_file failed:$check_path!\"\n        exit 1\n      else\n        echo \"INFO: Created target path in file $check_conf_file successfully:$check_path!\"\n      fi\n    fi\n  done\n}\n\nreplace_host() {\n  # Replace host with ip of current host.\n  replace_conf_file=$1\n  target_ip=$2\n  sed_replace \"s#^host *=.*#host = $target_ip#g\" $replace_conf_file\n}\n\ncopy_config_to() {\n  config_file_array=$1\n  src_path=$2\n  dest_path=$3\n  if ! [ -d $dest_path ]; then\n    if ! mkdir -p $dest_path; then\n      echo \"ERROR: Create target conf path failed:$dest_path!\"\n      exit 1\n    fi\n  fi\n  for CONF_FILE in ${config_file_array[@]}; do\n    tmp_src_file=$src_path/$CONF_FILE\n    tmp_dest_file=${dest_path}$CONF_FILE\n    if [ -f $tmp_src_file ]; then\n      if [ -f $tmp_dest_file ]; then\n        if ! [ $force_reconfig -eq 1 ]; then\n          echo \"ERROR: file $tmp_dest_file exist, \"\n          echo 'you must specify --force to force reconfig, it will overwrite the exists files!'\n          echo \"Usage: $this_shell_name config --force\"\n          exit 1\n        else\n          # Backup exists file\n          cp -f $tmp_dest_file $tmp_dest_file.bak\n        fi\n      fi\n      echo \"INFO: Copy file $CONF_FILE to $dest_path\"\n      cp -f $tmp_src_file $tmp_dest_file\n    elif ! [ -f $tmp_dest_file ]; then\n      echo \"ERROR: file $tmp_dest_file not exist\"\n      exit 1\n    fi\n    file_extension=\"${tmp_dest_file##*.}\"\n    if [ $file_extension = \"conf\" ]; then\n      check_paths_infile $tmp_dest_file\n      replace_host $tmp_dest_file \"$IP\"\n    fi\n  done\n}\n\nconfig_faststore() {\n  # Copy faststore's config file to target config path\n  echo \"INFO: Config faststore:\"\n  store_src_conf_path=$BUILD_PATH/$STORE_LIB/conf\n  copy_config_to \"${STORE_CONF_FILES[*]}\" $store_src_conf_path $STORE_CONF_PATH\n}\n\nconfig_fastdir() {\n  # Copy fastDIR's config file to target config path\n  echo \"INFO: Config fastDIR:\"\n  fdir_src_conf_path=$BUILD_PATH/$FDIR_LIB/conf\n  copy_config_to \"${FDIR_CONF_FILES[*]}\" $fdir_src_conf_path $FDIR_CONF_PATH\n}\n\nconfig_auth() {\n  # Copy auth's config file to target config path\n  echo \"INFO: Config auth:\"\n  auth_src_conf_path=src/auth/conf\n  copy_config_to \"${AUTH_CONF_FILES[*]}\" $auth_src_conf_path $AUTH_CONF_PATH\n  auth_src_keys_path=src/auth/conf/keys\n  copy_config_to \"${AUTH_KEYS_FILES[*]}\" $auth_src_keys_path $AUTH_KEYS_PATH\n}\n\nconfig_fuse() {\n  # Copy fuse's config file to target config path\n  echo \"INFO: Config fuse:\"\n  fuse_src_conf_path=conf\n  copy_config_to \"${FUSE_CONF_FILES[*]}\" $fuse_src_conf_path $FUSE_CONF_PATH\n}\n\nget_first_local_ip() {\n  ip_cmd=`which ip`\n  if [ -z \"$ip_cmd\" ]; then\n    ipconfig_cmd=`which ifconfig`\n    if [ -z \"$ipconfig_cmd\" ]; then\n      echo \"ERROR: Command ip or ifconfig not found, please install one first.\" 1>&2\n      exit\n    else\n      CMD=\"$ipconfig_cmd -a | grep -w inet | grep -v 127.0.0.1 | awk '{print \\$2}' | tr -d 'addr:' | head -n 1\"\n    fi\n  else\n    CMD=\"$ip_cmd addr | grep -w inet | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}' | grep -v 127.0.0.1 | head -n 1\"\n  fi\n  sh -c \"$CMD\"\n}\n\npull_source_codes() {\n  # Clone or pull source code from github.com if not exists.\n  echo \"INFO: Begin to pull source codes...\"\n  # Pull fastCFS self.\n  git pull\n  # Create build path if not exists.\n  if ! [ -d $BUILD_PATH ]; then\n    mkdir -m 775 $BUILD_PATH\n    echo \"INFO: Build path {$BUILD_PATH} not exist, created.\"\n  fi\n  cd $BUILD_PATH\n  \n  # Pull libfastcommon\n  pull_source_code $COMMON_LIB $COMMON_LIB_URL \n\n  # Pull libserverframe\n  pull_source_code $FRAME_LIB $FRAME_LIB_URL\n\n  # Pull libdiskallocator\n  pull_source_code $DISK_ALLOCATOR_LIB $DISK_ALLOCATOR_LIB_URL\n\n  # Pull fastDIR\n  pull_source_code $FDIR_LIB $FDIR_LIB_URL\n\n  # Pull faststore\n  pull_source_code $STORE_LIB $STORE_LIB_URL\n  cd ..\n}\n\nmake_install() {\n  lib_name=$1\n  lib_src_path=$2\n  op_extend_param=$3\n  make_op $lib_name $lib_src_path clean\n  make_op $lib_name $lib_src_path make $op_extend_param\n  make_op $lib_name $lib_src_path install $op_extend_param\n}\n\nmake_installs() {\n  # Make and install all lib repositories sequentially.\n  make_install $COMMON_LIB $COMMON_LIB\n  make_install $FRAME_LIB $FRAME_LIB\n  make_install $DISK_ALLOCATOR_LIB $DISK_ALLOCATOR_LIB\n  make_install vote_client $VOTECLIENT_LIB --module=vote_client\n  make_install auth_client $AUTHCLIENT_LIB --module=auth_client\n  make_install $FDIR_LIB $FDIR_LIB\n  make_install $STORE_LIB $STORE_LIB\n  make_install FastCFS $FASTCFS_LIB --exclude=auth_client\n  echo \"INFO: Congratulation, setup complete successfully!\"\n  echo \"INFO: Then, you can execute command [fastcfs.sh config] next step.\"\n}\n\nservice_op() {\n  operate_mode=$1\n  fs_serverd ${STORE_CONF_PATH}server.conf $operate_mode\n  fdir_serverd ${FDIR_CONF_PATH}server.conf $operate_mode\n  if [ $operate_mode != 'stop' ]; then\n     sleep 3\n  fi\n  fcfs_authd ${AUTH_CONF_PATH}server.conf $operate_mode\n  if [ $operate_mode != 'stop' ]; then\n     sleep 1\n  fi\n  fcfs_fused ${FUSE_CONF_PATH}fuse.conf $operate_mode\n}\n\nuname=$(uname)\nif [ $uname = 'Linux' ]; then\n  osname=$(cat /etc/os-release | grep -w NAME | awk -F '=' '{print $2;}' | \\\n          awk -F '\"' '{if (NF==3) {print $2} else {print $1}}' | awk '{print $1}')\n  if [ $osname = 'CentOS' ]; then\n    osversion=$(cat /etc/system-release | awk '{print $4}')\n  fi\n  if [ -z $osversion ]; then\n    osversion=$(cat /etc/os-release | grep -w VERSION_ID | awk -F '=' '{print $2;}' | \\\n            awk -F '\"' '{if (NF==3) {print $2} else {print $1}}')\n  fi\n  os_major_version=$(echo $osversion | awk -F '.' '{print $1}')\n  if [ $osname = 'Fedora' ]; then\n     if [ $os_major_version -lt 20 ]; then\n       os_major_version=6\n     elif [ $os_major_version -lt 28 ]; then\n       os_major_version=7\n     elif [ $os_major_version -lt 34 ]; then\n       os_major_version=8\n     else\n       os_major_version=9\n     fi\n  elif [ $osname = 'Alibaba' ]; then\n     if [ $os_major_version -lt 3 ]; then\n       os_major_version=7\n     else\n       os_major_version=8\n     fi\n  elif [ $osname = 'Amazon' ]; then\n    if [ $os_major_version -le 2 ]; then\n      os_major_version=7\n    else\n      os_major_version=8\n    fi\n  elif [ $osname = 'openEuler' ] || [ $osname = 'Kylin' ] || [ $osname = 'UOS' ] || [ $osname = 'BigCLoud' ]; then\n      os_major_version=8\n  fi\nelse\n  echo \"Unsupport OS: $uname\" 1>&2\n  exit 1\nfi\n\ncheck_install_fastos_repo() {\n  if [ $osname = 'Ubuntu' ] || [ $osname = 'Debian' ] || [ $osname = 'Deepin' ]; then\n    if [ ! -f /etc/apt/sources.list.d/fastos.list ]; then\n      apt install curl gpg -y\n      curl http://www.fastken.com/aptrepo/packages.fastos.pub | gpg --dearmor > /tmp/fastos-archive-keyring.gpg\n      install -D -o root -g root -m 644 /tmp/fastos-archive-keyring.gpg /usr/share/keyrings/fastos-archive-keyring.gpg\n      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'\n      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'\n      rm -f /tmp/fastos-archive-keyring.gpg\n    fi\n  else\n    repo=$(rpm -q FastOSrepo 2>/dev/null)\n    if [ $? -ne 0 ]; then\n      if [ $os_major_version -eq 7 ]; then\n        rpm -ivh http://www.fastken.com/yumrepo/el7/noarch/FastOSrepo-1.0.1-1.el7.noarch.rpm\n      else\n        rpm -ivh http://www.fastken.com/yumrepo/el8/noarch/FastOSrepo-1.0.1-1.el8.noarch.rpm\n      fi\n    fi\n  fi\n}\n\ninstall_all_softwares() {\n  if [ $osname = 'Ubuntu' -a $os_major_version -ge 18 ] || \\\n     [ $osname = 'Debian' -a $os_major_version -ge 10 ] || \\\n     [ $osname = 'Deepin' -a $os_major_version -ge 20 ]; then\n    check_install_fastos_repo\n    apt update\n    apt install fastcfs-auth-server fastcfs-vote-server fastdir-server faststore-server fastcfs-fused -y\n  elif [[ \" ${YUM_OS_ARRAY[@]} \" =~ \" ${osname} \" ]] && [ $os_major_version -ge 7 ]; then\n    check_install_fastos_repo\n    yum install FastCFS-auth-server FastCFS-vote-server fastDIR-server faststore-server FastCFS-fused -y\n  else\n    ./libfuse_setup.sh\n    pull_source_codes\n    make_installs\n  fi\n}\n\nconfig_all_modules() {\n  IP=$(get_first_local_ip)\n  parse_config_arguments $*\n  config_faststore\n  config_fastdir\n  config_auth\n  config_fuse\n}\n\ncase \"$mode\" in\n  'setup')\n    install_all_softwares\n    config_all_modules $*\n    service_op restart\n  ;;\n  'install')\n    install_all_softwares\n  ;;\n  'config')\n    config_all_modules $*\n  ;;\n  'start')\n    # Start services.\n    service_op start\n  ;;\n  'restart')\n    # Restart services.\n    service_op restart\n  ;;\n  'stop')\n    # Start services.\n    service_op stop\n  ;;\n  *)\n    # usage\n    echo \"Usage: $0 {setup|install|config|start|restart|stop}\"\n    exit 1\n  ;;\nesac\n\nexit 0\n"
  },
  {
    "path": "helloWorld.sh",
    "content": "#!/bin/bash\n\necho \"just for FastCFS demo: 1 fdir instance and 1 fstore instance\"\n\nmounted_path=/opt/fastcfs/fuse\n\n./fastcfs.sh install || exit 1\n./fastcfs.sh config --force\n./fastcfs.sh restart\n\necho \"waiting services ready ...\"\n\nsleep 3\nif [ -d $mounted_path ]; then\n  df -h $mounted_path | grep fuse\n  if [ $? -eq 0 ]; then\n    echo \"\"\n    echo \"the mounted path is: $mounted_path\"\n    echo \"have a nice day!\"\n    exit 0\n  fi\nfi\n\ntail_cmd='tail -n 20 /opt/fastcfs/fcfs/logs/fcfs_fused.log'\necho 'some mistake happen!'\necho $tail_cmd\nsh -c \"$tail_cmd\"\nexit 1\n"
  },
  {
    "path": "libfuse_setup.sh",
    "content": "#!/bin/bash\n\nYUM_OS_ARRAY=(Red Rocky Oracle Fedora CentOS AlmaLinux Alibaba Anolis Amazon)\n\nget_gcc_version() {\n  old_lang=$LANG\n  LANG=en_US.UTF-8 && gcc -v 2>&1 | grep 'gcc version' | \\\n          awk -F 'version' '{print $2}' | awk '{print $1}' | \\\n          awk -F '.' '{print $1}'\n  LANG=$old_lang\n}\n\napt_check_install_gcc() {\n  version=$1\n  pkg_gcc=\"gcc-$version\"\n  pkg_cpp=\"g++-$version\"\n  apt list $pkg_gcc 2>&1 | grep $pkg_gcc && apt install $pkg_gcc $pkg_cpp -y\n}\n\napt_install_gcc() {\n  apt_check_install_gcc 7\n  if [ $? -ne 0 ]; then\n    apt_check_install_gcc 8\n    if [ $? -ne 0 ]; then\n      apt_check_install_gcc 9\n    fi\n  fi\n}\n\napt_install_required_gcc() {\n  os_major_version=$1\n  apt install gcc g++ -y\n  if [ $? -ne 0 ]; then\n    apt_install_gcc $os_major_version\n  else\n    gcc_version=$(get_gcc_version)\n    if [ -z \"$gcc_version\" ] || [ \"$gcc_version\" -lt 4 ]; then\n      apt_install_gcc $os_major_version\n    fi\n  fi\n}\n\nyum_check_install_gcc() {\n  prefix=$1\n  version=$2\n  pkg_prefix=\"${prefix}toolset-${version}\"\n  pkg_gcc=\"${pkg_prefix}-gcc\"\n  pkg_cpp=\"${pkg_prefix}-gcc-c++\"\n  yum list $pkg_gcc 2>&1 | grep \"$pkg_gcc\" && \\\n      yum install $pkg_gcc $pkg_cpp -y && \\\n      source /opt/rh/$pkg_prefix/enable\n}\n\nyum_install_gcc() {\n  os_major_version=$1\n  if [ $os_major_version -lt 8 ]; then\n    yum install centos-release-scl scl-utils-build -y\n    prefix='dev'\n  else\n    prefix='gcc-'\n  fi\n\n  yum_check_install_gcc \"$prefix\" 7\n  if [ $? -ne 0 ]; then\n    yum_check_install_gcc \"$prefix\" 8\n    if [ $? -ne 0 ]; then\n      yum_check_install_gcc \"$prefix\" 9\n    fi\n  fi\n}\n\nyum_install_required_gcc() {\n  os_major_version=$1\n  yum install gcc gcc-c++ -y\n  if [ $? -ne 0 ]; then\n    yum_install_gcc $os_major_version\n  else\n    gcc_version=$(get_gcc_version)\n    if [ -z \"$gcc_version\" ] || [ \"$gcc_version\" -lt 4 ]; then\n      yum_install_gcc $os_major_version\n    fi\n  fi\n}\n\nif [ -z \"$LANG\" ]; then\n  export LANG=en_US.UTF-8\nfi\n\nuname=$(uname)\nif [ $uname = 'Linux' ]; then\n  osname=$(cat /etc/os-release | grep -w NAME | awk -F '=' '{print $2;}' | \\\n          awk -F '\"' '{if (NF==3) {print $2} else {print $1}}' | awk '{print $1}')\n  if [ $osname = 'CentOS' ]; then\n    osversion=$(cat /etc/system-release | awk '{print $4}')\n  fi\n  if [ -z $osversion ]; then\n    osversion=$(cat /etc/os-release | grep -w VERSION_ID | awk -F '=' '{print $2;}' | \\\n            awk -F '\"' '{if (NF==3) {print $2} else {print $1}}')\n  fi\n  os_major_version=$(echo $osversion | awk -F '.' '{print $1}')\n  if [ $osname = 'Fedora' ]; then\n     if [ $os_major_version -lt 20 ]; then\n       os_major_version=6\n     elif [ $os_major_version -lt 28 ]; then\n       os_major_version=7\n     elif [ $os_major_version -lt 34 ]; then\n       os_major_version=8\n     else\n       os_major_version=9\n     fi\n  elif [ $osname = 'Alibaba' ]; then\n     if [ $os_major_version -lt 3 ]; then\n       os_major_version=7\n     else\n       os_major_version=8\n     fi\n  elif [ $osname = 'Amazon' ]; then\n    if [ $os_major_version -le 2 ]; then\n      os_major_version=7\n    else\n      os_major_version=8\n    fi\n  fi\nelse\n  echo \"Unsupport OS: $uname\" 1>&2\n  exit 1\nfi\n\nif [[ \" ${YUM_OS_ARRAY[@]} \" =~ \" ${osname} \" ]] && [ $os_major_version -ge 7 ]; then\n  repo=$(rpm -q FastOSrepo 2>/dev/null)\n  if [ $? -ne 0 ]; then\n    if [ $os_major_version -eq 7 ]; then\n      rpm -ivh http://www.fastken.com/yumrepo/el7/x86_64/FastOSrepo-1.0.0-1.el7.centos.x86_64.rpm\n    else\n      rpm -ivh http://www.fastken.com/yumrepo/el8/x86_64/FastOSrepo-1.0.0-1.el8.x86_64.rpm\n    fi\n  fi\n\n  rpm -q fuse-devel >/dev/null && yum remove fuse-devel -y\n  rpm -q fuse >/dev/null && yum remove fuse -y\n  yum install fuse3-devel -y\n  exit 0\nelse\n  git_version=$(git --version 2>&1 | grep 'version' | awk '{print $3}')\n  make_version=$(make --version 2>&1 | grep 'Make' | awk '{print $3}')\n  PKG_CONFIG_PRG=$(which pkg-config 2>&1)\n  if [ $? -ne 0 ]; then\n    PKG_CONFIG_PRG=''\n  fi\n\n  gcc_version=$(get_gcc_version)\n  python_version=$(python3 --version 2>&1 | grep Python | awk '{print $2}')\n  pip3_version=$(pip3 --version 2>&1 | awk '{print $2}')\n\n  if [ $osname = 'Ubuntu' -o $osname = 'Debian' ]; then\n    if [ -z \"$git_version\" ]; then\n      apt install git -y\n    fi\n\n    if [ -z \"$make_version\" ]; then\n      apt install make -y\n    fi\n\n    dpkg -s libaio-dev >/dev/null 2>&1 || apt install libaio-dev -y\n    if [ -z \"$PKG_CONFIG_PRG\" ]; then\n      apt install pkg-config -y\n    fi\n\n    if [ -z \"$python_version\" ]; then\n      apt install python3 -y\n    fi\n\n    if [ -z \"$pip3_version\" ]; then\n      apt install python3-pip -y\n    fi\n\n    if [ -z \"$gcc_version\" ] || [ \"$gcc_version\" -lt 4 ]; then\n      apt_install_required_gcc\n    fi\n  else\n    if [ -z \"$git_version\" ]; then\n      yum install git -y\n    fi\n\n    if [ -z \"$make_version\" ]; then\n      yum install make -y\n    fi\n\n    if [ -z \"$PKG_CONFIG_PRG\" ]; then\n      yum install pkgconfig -y || yum install pkgconf-pkg-config -y\n    fi\n\n    if [ -z \"$python_version\" ]; then\n      yum install python3 -y || yum install python36 -y || yum install python38 -y || exit 2\n    fi\n\n    if [ -z \"$pip3_version\" ]; then\n      yum install python3-pip -y\n    fi\n\n    if [ -z \"$gcc_version\" ] || [ \"$gcc_version\" -lt 4 ]; then\n      yum_install_required_gcc\n    fi\n  fi\nfi\n\nMESON_PRG=$(which meson 2>&1)\nif [ $? -ne 0 ]; then\n  pip3 install meson || exit\nfi\n\nNINJA_PRG=$(which ninja 2>&1)\nif [ $? -ne 0 ]; then\n  pip3 install ninja || exit\nfi\n\nmkdir -p build; cd build\ngit clone https://gitee.com/mirrors/libfuse.git\ncd libfuse/\ngit checkout fuse-3.10.1\nrm -rf build/ && mkdir build/ && cd build/\nmeson ..\nmeson configure -D prefix=/usr\nmeson configure -D examples=false\nninja\nninja install\nsed -i 's/#user_allow_other/user_allow_other/g' /etc/fuse.conf\ncd ..\n"
  },
  {
    "path": "make.sh",
    "content": "ENABLE_STATIC_LIB=0\nENABLE_SHARED_LIB=1\nTARGET_PREFIX=$DESTDIR/usr\nTARGET_CONF_PATH=$DESTDIR/etc/fdir\n\nmodule=''\nexclude=''\njni_file=''\nstart=1\nfor arg do\n  case \"$arg\" in\n    --module=*)\n      module=${arg#--module=}\n      start=$(expr $start + 1)\n    ;;\n    --exclude=*)\n      exclude=${arg#--exclude=}\n      start=$(expr $start + 1)\n    ;;\n    --jni=*)\n      jni_file=${arg#--jni=}\n      start=$(expr $start + 1)\n    ;;\n  esac\ndone\n\nindex=$start\neval param1=\"\\$$index\"\nindex=$(expr $index + 1)\neval param2=\"\\$$index\"\n\nDEBUG_FLAG=0\nPRELOAD_WITH_CAPI=1\n\narch=$(uname -r | awk -F '.' '{print $NF;}')\nif [ \"$arch\" = \"x86_64\" ]; then\n  PRELOAD_WITH_PAPI=1\nelse\n  PRELOAD_WITH_PAPI=0\nfi\n\nexport CC=gcc\nCFLAGS='-Wall'\nGCC_VERSION=$(gcc -dM -E -  < /dev/null | grep -w __GNUC__ | awk '{print $NF;}')\nif [ -n \"$GCC_VERSION\" ] && [ $GCC_VERSION -ge 7 ]; then\n  CFLAGS=\"$CFLAGS -Wformat-truncation=0 -Wformat-overflow=0\"\nfi\n\nCFLAGS=\"$CFLAGS -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE\"\nif [ \"$PRELOAD_WITH_CAPI\" = \"1\" ]; then\n  CFLAGS=\"$CFLAGS -DFCFS_PRELOAD_WITH_CAPI\"\nfi\nif [ \"$PRELOAD_WITH_PAPI\" = \"1\" ]; then\n  CFLAGS=\"$CFLAGS -DFCFS_PRELOAD_WITH_PAPI\"\nfi\n\nif [ \"$DEBUG_FLAG\" = \"1\" ]; then\n  CFLAGS=\"$CFLAGS -g -DDEBUG_FLAG\"\nelse\n  CFLAGS=\"$CFLAGS -g -O3\"\nfi\n\nif [ -f /usr/include/fastcommon/_os_define.h ]; then\n  OS_BITS=$(grep -F OS_BITS /usr/include/fastcommon/_os_define.h | awk '{print $NF;}')\nelif [ -f /usr/local/include/fastcommon/_os_define.h ]; then\n  OS_BITS=$(grep -F OS_BITS /usr/local/include/fastcommon/_os_define.h | awk '{print $NF;}')\nelse\n  OS_BITS=64\nfi\n\nuname=$(uname)\nLIB_VERSION=\nif [ \"$OS_BITS\" -eq 64 ]; then\n  if [ $uname = 'Linux' ]; then\n    osname=$(cat /etc/os-release | grep -w NAME | awk -F '=' '{print $2;}' | \\\n            awk -F '\"' '{if (NF==3) {print $2} else {print $1}}' | awk '{print $1}')\n    if [ $osname = 'Ubuntu' -o $osname = 'Debian' ]; then\n      LIB_VERSION=lib\n    else\n      LIB_VERSION=lib64\n    fi\n  else\n    LIB_VERSION=lib\n  fi\nelse\n  LIB_VERSION=lib\nfi\n\nLIBS=''\nif [ \"$uname\" = \"Linux\" ]; then\n  if [ \"$OS_BITS\" -eq 64 ]; then\n    LIBS=\"$LIBS -L/usr/lib64\"\n  else\n    LIBS=\"$LIBS -L/usr/lib\"\n  fi\n  CFLAGS=\"$CFLAGS\"\nelif [ \"$uname\" = \"FreeBSD\" ] || [ \"$uname\" = \"Darwin\" ]; then\n  LIBS=\"$LIBS -L/usr/lib\"\n  CFLAGS=\"$CFLAGS\"\n  if [ \"$uname\" = \"Darwin\" ]; then\n    CFLAGS=\"$CFLAGS -DDARWIN\"\n    TARGET_PREFIX=$TARGET_PREFIX/local\n  fi\nelif [ \"$uname\" = \"SunOS\" ]; then\n  LIBS=\"$LIBS -L/usr/lib\"\n  CFLAGS=\"$CFLAGS -D_THREAD_SAFE\"\n  LIBS=\"$LIBS -lsocket -lnsl -lresolv\"\nelif [ \"$uname\" = \"AIX\" ]; then\n  LIBS=\"$LIBS -L/usr/lib\"\n  CFLAGS=\"$CFLAGS -D_THREAD_SAFE\"\nelif [ \"$uname\" = \"HP-UX\" ]; then\n  LIBS=\"$LIBS -L/usr/lib\"\n  CFLAGS=\"$CFLAGS\"\nfi\n\nhave_pthread=0\nif [ -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\n  LIBS=\"$LIBS -lpthread\"\n  have_pthread=1\nelif [ \"$uname\" = \"HP-UX\" ]; then\n  lib_path=\"/usr/lib/hpux$OS_BITS\"\n  if [ -f $lib_path/libpthread.so ]; then\n    LIBS=\"-L$lib_path -lpthread\"\n    have_pthread=1\n  fi\nelif [ \"$uname\" = \"FreeBSD\" ]; then\n  if [ -f /usr/lib/libc_r.so ]; then\n    line=$(nm -D /usr/lib/libc_r.so | grep -F pthread_create | grep -w T)\n    if [ $? -eq 0 ]; then\n      LIBS=\"$LIBS -lc_r\"\n      have_pthread=1\n    fi\n  elif [ -f /lib64/libc_r.so ]; then\n    line=$(nm -D /lib64/libc_r.so | grep -F pthread_create | grep -w T)\n    if [ $? -eq 0 ]; then\n      LIBS=\"$LIBS -lc_r\"\n      have_pthread=1\n    fi\n  elif [ -f /usr/lib64/libc_r.so ]; then\n    line=$(nm -D /usr/lib64/libc_r.so | grep -F pthread_create | grep -w T)\n    if [ $? -eq 0 ]; then\n      LIBS=\"$LIBS -lc_r\"\n      have_pthread=1\n    fi\n  fi\nfi\n\nif [ $have_pthread -eq 0 ] && [ \"$uname\" != \"Darwin\" ]; then\n   /sbin/ldconfig -p | grep -F libpthread.so > /dev/null\n   if [ $? -eq 0 ]; then\n      LIBS=\"$LIBS -lpthread\"\n   else\n      echo -E 'Require pthread lib, please check!'\n      exit 2\n   fi\nfi\n\nsed_replace()\n{\n    sed_cmd=$1\n    filename=$2\n    if [ \"$uname\" = \"FreeBSD\" ] || [ \"$uname\" = \"Darwin\" ]; then\n       sed -i \"\" \"$sed_cmd\" $filename\n    else\n       sed -i \"$sed_cmd\" $filename\n    fi\n}\n\nreplace_makefile()\n{\n    cp Makefile.in Makefile\n    sed_replace \"s#\\\\\\$(CFLAGS)#$CFLAGS#g\" Makefile\n    sed_replace \"s#\\\\\\$(LIBS)#$LIBS#g\" Makefile\n    sed_replace \"s#\\\\\\$(TARGET_PREFIX)#$TARGET_PREFIX#g\" Makefile\n    sed_replace \"s#\\\\\\$(LIB_VERSION)#$LIB_VERSION#g\" Makefile\n    sed_replace \"s#\\\\\\$(TARGET_CONF_PATH)#$TARGET_CONF_PATH#g\" Makefile\n    sed_replace \"s#\\\\\\$(ENABLE_STATIC_LIB)#$ENABLE_STATIC_LIB#g\" Makefile\n    sed_replace \"s#\\\\\\$(ENABLE_SHARED_LIB)#$ENABLE_SHARED_LIB#g\" Makefile\n}\n\nbase_path=$(pwd)\n\ncd src/include/fastcfs/\nfor subdir in api vote; do\n  link=$(readlink $subdir)\n  if [ $? -ne 0 ] || [ \"$link\" != \"../../$subdir\" -a \"$link\" != \"../../$subdir/\" ]; then\n    ln -sf ../../$subdir $subdir\n  fi\ndone\ncd $base_path\n\nif [ -z $module ] || [ \"$module\" = 'auth_server' -o \"$module\" = 'authserver' ]; then\n  if [ \"x$exclude\" != 'xauth_server' -a \"x$exclude\" != 'xauthserver' ]; then\n    cd $base_path/src/auth/server\n    replace_makefile\n    make $param1 $param2\n  fi\nfi\n\nif [ -z $module ] || [ \"$module\" = 'auth_client' -o \"$module\" = 'authclient' ]; then\n  if [ \"x$exclude\" != 'xauth_client' -a \"x$exclude\" != 'xauthclient' ] && [ \"x$exclude\" != 'xclient' ]; then\n    cd $base_path/src/auth/client\n    replace_makefile\n    make $param1 $param2\n\n    cd $base_path/src/auth/client/tools\n    replace_makefile\n    make $param1 $param2\n  fi\nfi\n\nif [ -z $module ] || [ \"$module\" = 'vote_server' -o \"$module\" = 'voteserver' ]; then\n  if [ \"x$exclude\" != 'xvote_server' -a \"x$exclude\" != 'xvoteserver' ]; then\n    cd $base_path/src/vote/server\n    replace_makefile\n    make $param1 $param2\n  fi\nfi\n\nif [ -z $module ] || [ \"$module\" = 'vote_client' -o \"$module\" = 'voteclient' ]; then\n  if [ \"x$exclude\" != 'xvote_client' -a \"x$exclude\" != 'xvoteclient' ] && [ \"x$exclude\" != 'xclient' ]; then\n    cd $base_path/src/vote/client\n    replace_makefile\n    make $param1 $param2\n\n    cd $base_path/src/vote/client/tools\n    replace_makefile\n    make $param1 $param2\n  fi\nfi\n\nif [ -z $module ] || [ \"$module\" = 'api' ] || [ \"$module\" = 'fuseclient' -o \"$module\" = 'fuse_client' ]; then\n  if [ \"x$exclude\" != 'xapi' ]; then\n    cd $base_path/src/api\n    replace_makefile\n    make $param1 $param2\n\n    cd tests || exit\n    replace_makefile\n    make $param1 $param2\n  fi\nfi\n\nif [ \"$uname\" = \"Linux\" ]; then\nif [ -z $module ] || [ \"$module\" = 'preload' ]; then\n  if [ \"x$exclude\" != 'xpreload' ]; then\n    cd $base_path/src/preload\n    replace_makefile\n    make $param1 $param2\n  fi\nfi\nfi\n\nif [ -z $module ] || [ \"$module\" = 'tools' ]; then\n  if [ \"x$exclude\" != 'xtools' ]; then\n    cd $base_path/src/tools\n    replace_makefile\n    make $param1 $param2\n  fi\nfi\n\nif [ \"$uname\" = \"Linux\" ]; then\nif [ -z $module ] || [ \"$module\" = 'fuse_client' -o \"$module\" = 'fuseclient' ]; then\n  if [ \"x$exclude\" != 'xfuse_client' -a \"x$exclude\" != 'xfuseclient' ]; then\n    cd $base_path/src/fuse\n    replace_makefile\n    make $param1 $param2\n  fi\nfi\nfi\n\nif [ \"$module\" = 'jni' ]; then\n  if [ ! -z $JAVA_HOME ]; then\n    path=$JAVA_HOME\n  elif [ ! -z $jni_file ]; then\n    if [ -d $jni_file ]; then\n      path=$jni_file\n    else\n      path=$(dirname $jni_file)\n    fi\n  else\n    if [ -d /Library ]; then\n      path=/Library\n    else\n      path=/usr/lib\n    fi\n  fi\n\n  files=$(find $path -name jni.h 2>/dev/null | grep '/include/jni.h$')\n  if [ -z \"$files\" ]; then\n    files=$(locate jni.h | grep '/include/jni.h$')\n    if [ -z \"$files\" ]; then\n      echo \"can't locate jni.h, please install java SDK first.\"\n      exit 2\n    fi\n  fi\n\n  count=$(echo \"$files\" | wc -l)\n  if [ $count -eq 1 ]; then\n    filename=$files\n  else\n    i=0\n    for file in $files; do\n       i=$(expr $i + 1)\n       echo \"$i. $file\"\n    done\n\n    printf \"please input the correct file no.: \"\n    read n\n    if [ -z \"$n\" ]; then\n      echo \"invalid file no.\"\n      exit 2\n    fi\n\n    filename=$(echo \"$files\" | head -n $n | tail -n 1)\n  fi\n\n  INCLUDES=\n  path=$(dirname $filename)\n  for d in $(find $path -type d); do\n    INCLUDES=\"$INCLUDES -I$d\"\n  done\n\n  cd $base_path/src/java/jni\n  replace_makefile\n  sed_replace \"s#\\\\\\$(INCLUDES)#$INCLUDES#g\" Makefile\n  make $param1 $param2\nfi\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_name: FastCFS Docs\nsite_description:  'FastCFS Documents,Version: v3.2.0'\n\nrepo_name: GitHub\nrepo_url: https://github.com/happyfish100/FastCFS/\n\nplugins:\n  - search\n  - mike:\n      version_selector: true\nnav:\n  - Introduction:\n    - FastCFS Introduction: \"index.md\"\n    - Release Notes: \"ReleaseNotes.md\"\n    - Benchmarks: \"benchmark.md\"\n  - Concepts:\n    - Architecture:\n        - FastDir: \"concepts/fastDir.md\"\n        - FastStore: \"concepts/fast-store.md\"\n        - FastAuth: \"concepts/fastDir.md\"\n  - UserGuide:\n      - Quickstart: \"Easy-install-detail-zh_CN.md\"\n      - Install: \"INSTALL.md\"\n\nmarkdown_extensions:\n  - toc:\n      permalink: True\n  - footnotes\n  - tables\n  - meta  # 定义元数据，通过文章上下文控制，如disqus\n  - pymdownx.caret  # 下划线上标\n  - pymdownx.tilde  # 删除线下标\n  - pymdownx.critic  # 增加删除修改高亮注释，可修饰行内或段落\n  - pymdownx.details  # 提示块可折叠\n  - pymdownx.inlinehilite  # 行内代码高亮\n  - pymdownx.mark  # 文本高亮\n  - pymdownx.smartsymbols  # 符号转换\n  - pymdownx.superfences  # 代码嵌套在列表里\n  - codehilite:    # 代码高亮，显示行号\n      guess_lang: false\n      linenums: true\n  - pymdownx.tasklist:  # 复选框checklist\n      custom_checkbox: true\n\ntheme:\n  name: material\n  logo: img/logo.png\n  favicon: img/logo.png\n  palette:\n    - scheme: default\n      toggle:\n        icon: material/toggle-switch-off-outline\n        name: Switch to dark mode\n    - scheme: slate\n      primary: deep orange\n      accent: deep orange\n      toggle:\n        icon: material/toggle-switch\n        name: Switch to light mode\nextra:\n  version:\n    provider: mike\n  alternate:\n    - name: English\n      link: /\n      lang: en\n    - name: 中文\n      link: /zh/\n      lang: zh\n\n"
  },
  {
    "path": "shell/Dockerfile",
    "content": "FROM nginx:mainline-alpine\n# COPY dependency.2.0.1.settings /usr/share/nginx/html/\nCOPY conf.2.3.0.tpl.tar.gz /usr/share/nginx/html/"
  },
  {
    "path": "shell/conf_tpl_tar.sh",
    "content": "#!/bin/bash\n#\n# conf_tpl_tar.sh is a tool for quickly pack FastCFS cluster config files to template tar.\n# this is for FastCFS developer.\n#\nBUILD_PATH=\"build\"\nFDIR_LIB=\"fastDIR\"\nSTORE_LIB=\"faststore\"\n\nshell_name=$0\nconf_file_version=$1\nupdate_sources=$2\n\n#---Usage info section begin---#\nprint_usage() {\n  # Print usage to console.\n  echo \"\"\n  echo \"Usage: $shell_name <version> [update]\"\n  echo \"\"\n  echo \"A self-sufficient config file templates pack shell for FastCFS cluster\"\n  echo \"\"\n  echo \"version:\"\n  echo \"  Version of the tar file, exp: 3.4.0.\"\n  echo \"  Tar file example: conf.3.4.0.tpl.tar.gz\"\n  echo \"\"\n  echo \"update: Pull source code from remote before execute tar command.\"\n  echo \"\"\n}\n\nif [ -z $conf_file_version ]; then\n  print_usage\n  exit 1\nfi\n#---Usage info section end---#\n\npull_source_code() {\n  local module_name=$1\n  local source_path=$2\n  if ! [ -d $source_path ]; then\n    echo \"ERROR: source code path {$source_path} for $module_name not exist.\"\n    exit 1\n  fi\n  cd $source_path\n  echo \"INFO: =====Begin to pull $module_name, it will take some time...=====\"\n  git checkout master\n  git pull\n  cd -\n}\n\npull_source_codes() {\n  # Update source code from github.com.\n  echo \"INFO: Begin to pull source codes...\"\n  # Pull fastCFS self.\n  pull_source_code \"FastCFS\" ../\n  # Pull fastDIR\n  pull_source_code fastDIR ../../fastDIR\n  # Pull faststore\n  pull_source_code faststore ../../faststore\n}\n\nif [ \"$update_sources\" = \"update\" ]; then\n  pull_source_codes\nfi\n\nconfig_file_template_path=\"conf.$conf_file_version.tpl\"\nconfig_file_template_tar=\"$config_file_template_path.tar.gz\"\n# Create build path if not exists.\nif ! [ -d $config_file_template_path ]; then\n  mkdir -m 775 $config_file_template_path\n  echo \"INFO: temp tpl path {$config_file_template_path} not exist, created.\"\nelse\n  rm -rf \"$config_file_template_path/auth\"\n  rm -rf \"$config_file_template_path/fcfs\"\n  rm -rf \"$config_file_template_path/fdir\"\n  rm -rf \"$config_file_template_path/fstore\"\n  rm -rf \"$config_file_template_path/vote\"\nfi\n# Copy config files from source to temp tpl path\nmkdir -p \"$config_file_template_path/auth/keys\"\nmkdir \"$config_file_template_path/fcfs\"\nmkdir \"$config_file_template_path/fdir\"\nmkdir \"$config_file_template_path/fstore\"\nmkdir \"$config_file_template_path/vote\"\n`cp ../src/auth/conf/keys/*.key $config_file_template_path/auth/keys/`\n`cp ../src/auth/conf/*.conf $config_file_template_path/auth/`\n`cp ../src/vote/conf/*.conf $config_file_template_path/vote/`\n`cp ../conf/*.conf $config_file_template_path/fcfs/`\n`cp ../../fastDIR/conf/*.conf $config_file_template_path/fdir/`\n`cp ../../faststore/conf/*.conf $config_file_template_path/fstore/`\n`tar -czvf $config_file_template_tar $config_file_template_path`\nexit 0"
  },
  {
    "path": "shell/fcfs.settings",
    "content": "# FastCFS cluster ops settings be used with shell fcfs.sh\n# This settings file must be placed at same path with fcfs.sh\n# You can edit it, or download it from http://www.fastcfs.com/ops.\n\nfastcfs_version=5.3.0\n\nfuseclient_ips=192.168.142.9\n"
  },
  {
    "path": "shell/fcfs.sh",
    "content": "#!/bin/bash\n#\n# fcfs.sh is a ops tool for quickly deploy FastCFS clusters.\n# It only relying on SSH access to the servers and sudo.\n# It runs fully on your workstation, requiring no servers, databases, or anything like that.\n#\n# If you set up and tear down FastCFS clusters a lot, and want minimal extra repeating works,\n# this is for you.\n#\n#---1. Config path section begin---#\ndeclare -ir MIN_VERSION_OF_Ubuntu=18\ndeclare -ir MIN_VERSION_OF_Debian=10\ndeclare -ir MIN_VERSION_OF_Deepin=20\ndeclare -ir MIN_VERSION_OF_CentOS=7\ndeclare -ir MIN_VERSION_OF_Red=8\ndeclare -ir MIN_VERSION_OF_Rocky=8\ndeclare -ir MIN_VERSION_OF_Oracle=8\ndeclare -ir MIN_VERSION_OF_Fedora=20\ndeclare -ir MIN_VERSION_OF_AlmaLinux=7\ndeclare -ir MIN_VERSION_OF_Alibaba=2\ndeclare -ir MIN_VERSION_OF_Anolis=7\ndeclare -ir MIN_VERSION_OF_Amazon=2\ndeclare -ir MIN_VERSION_OF_openEuler=20\ndeclare -ir MIN_VERSION_OF_UOS=20\ndeclare -ir MIN_VERSION_OF_BigCLoud=21\nYUM_OS_ARRAY=(Red Rocky Oracle Fedora CentOS AlmaLinux Alibaba Anolis Amazon openEuler Kylin UOS BigCLoud)\nAPT_OS_ARRAY=(Ubuntu Debian Deepin)\n\nrepo_affix=\"\"\nfcfs_settings_file=\"fcfs.settings\"\nfcfs_dependency_file_server=\"http://www.fastken.cn/fastcfs/ops/dependency\"\nfcfs_cache_path=\".fcfs\"\nfcfs_installed_file=\"installed.settings\"\n\nSTORE_CONF_FILES=(client.conf server.conf cluster.conf storage.conf dbstore.conf)\nSTORE_CONF_PATH=\"/etc/fastcfs/fstore/\"\nSTORE_LOG_FILE=\"/opt/fastcfs/fstore/logs/fs_serverd.log\"\n\nFDIR_CONF_FILES=(client.conf cluster.conf server.conf dbstore.conf)\nFDIR_CONF_PATH=\"/etc/fastcfs/fdir/\"\nFDIR_LOG_FILE=\"/opt/fastcfs/fdir/logs/fdir_serverd.log \"\n\nVOTE_CONF_FILES=(client.conf cluster.conf server.conf)\nVOTE_CONF_PATH=\"/etc/fastcfs/vote/\"\nVOTE_LOG_FILE=\"/opt/fastcfs/vote/logs/fcfs_voted.log \"\n\nAUTH_CONF_FILES=(auth.conf client.conf cluster.conf server.conf session.conf)\nAUTH_CONF_PATH=\"/etc/fastcfs/auth/\"\nAUTH_KEYS_FILES=(session_validate.key)\nAUTH_KEYS_PATH=\"/etc/fastcfs/auth/keys/\"\nAUTH_LOG_FILE=\"/opt/fastcfs/auth/logs/fcfs_authd.log\"\n\nFUSE_CONF_FILES=(fuse.conf)\nFUSE_CONF_PATH=\"/etc/fastcfs/fcfs/\"\nFUSE_LOG_FILE=\"/opt/fastcfs/fcfs/logs/fcfs_fused.log\"\n#---1. Config path section end---#\n\n#---2. Get shell and command section begin---#\nshell_name=$0\nshell_command=$1\nlocal_os_uname=$(uname)\n#---2. Get shell and command section end---#\n\n#---3. Usage info section begin---#\nprint_usage() {\n  # Print usage to console.\n  echo \"\"\n  echo \"Usage: $shell_name <command> [module] [node]\"\n  echo \"\"\n  echo \"A self-sufficient operation shell for FastCFS cluster\"\n  echo \"\"\n  echo \"Commands:\"\n  echo \"  setup      Setup FastCFS softwares, a shortcut command combines install, config, and restart\"\n  echo \"  install    Install FastCFS softwares\"\n  echo \"  reinstall  Reinstall FastCFS softwares, only used for yum\"\n  echo \"  erase      Erase FastCFS softwares\"\n  echo \"  remove     Remove FastCFS softwares, same as erase\"\n  echo \"  config     Copy cluster config files to target host path\"\n  echo \"  start      Start all or one module service in cluster\"\n  echo \"  stop       Stop all or one module service in cluster\"\n  echo \"  restart    Restart all or one module service in cluster\"\n  echo \"  tail       Display the last part of the specified module's log\"\n  echo \"  status     Display the service processes status\"\n  echo \"  help       Show the detail of commands and examples\"\n  echo \"\"\n}\n\nprint_detail_usage() {\n  # Print usage to console.\n  print_usage\n  echo \"Modules:\"\n  echo \"  fvote        FastCFS vote server\"\n  echo \"  fdir         fastDIR server\"\n  echo \"  fstore       faststore server\"\n  echo \"  fauth        FastCFS auth server\"\n  echo \"  fuseclient   FastCFS fuse client\"\n  echo \"\"\n  echo \"Node:\"\n  echo \"  You can specify a single cluster IP, or command will be executed on all nodes.\"\n  echo \"\"\n  echo \"Tail command options:\"\n  echo \"  -n number\"\n  echo \"          The location is number lines\"\n  echo \"\"\n  echo \"  -number\"\n  echo \"          The location is number lines\"\n  echo \"\"\n  echo \"Examples:\"\n  echo \"  $shell_name setup\"\n  echo \"          Setup all FastCFS softwares on all nodes, then config and start module services.\"\n  echo \"\"\n  echo \"  $shell_name install\"\n  echo \"          Install all FastCFS softwares on all nodes.\"\n  echo \"\"\n  echo \"  $shell_name erase\"\n  echo \"          Erase all FastCFS softwares on all nodes.\"\n  echo \"\"\n  echo \"  $shell_name config fdir 172.16.168.128\"\n  echo \"          Copy fdir config file to host 172.16.168.128.\"\n  echo \"\"\n  echo \"  $shell_name start fdir\"\n  echo \"          Start all fdir servers.\"\n  echo \"\"\n  echo \"  $shell_name start fdir 172.16.168.128\"\n  echo \"          Start fdir server on host 172.16.168.128.\"\n  echo \"\"\n  echo \"  $shell_name tail fdir 172.16.168.128 -n 100\"\n  echo \"          Display the last 100 lines of fdir server log.\"\n  echo \"\"\n  echo \"  $shell_name status\"\n  echo \"          Display all modules service process status.\"\n  echo \"\"\n  echo \"  $shell_name status fdir\"\n  echo \"          Display all the status of fdir service processes status.\"\n  echo \"\"\n  echo \"\"\n}\n\ncase \"$shell_command\" in\n  'setup' | 'install' | 'reinstall' | 'erase' | 'remove' | 'config' | 'start' | 'restart' | 'stop' | 'tail' | 'status')\n  ;;\n  'help')\n    print_detail_usage\n    exit 1\n  ;;\n  *)\n    print_usage\n    exit 1\n  ;;\nesac\n#---3. Usage info section end---#\n\n#---4. Tool functions section begin---#\nsed_replace()\n{\n    local sed_cmd=$1\n    local file_name=$2\n    local current_uname=$(uname)\n    if [ \"$current_uname\" = \"FreeBSD\" ] || [ \"$current_uname\" = \"Darwin\" ]; then\n       sed -i \"\" \"$sed_cmd\" $file_name\n    else\n       sed -i \"$sed_cmd\" $file_name\n    fi\n}\n\nsplit_to_array() {\n  if ! [ -z $2 ]; then\n    IFS=',' read -ra $2 <<< \"$1\"\n  fi\n}\n\n# Check preceding version less than or equals following.\nfunction version_le() { test \"$(echo \"$@\" | tr \" \" \"\\n\" | sort -V | head -n 1)\" == \"$1\"; }\n\n# Execute command on a host over SSH\nexecute_remote_command() {\n  local remote_host=$1\n  local remote_command=$2\n  local result=$(ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -T $remote_host $remote_command)\n  echo $result\n}\n\n# Execute command on a host over SSH without return\nexecute_remote_command_no_return() {\n  local remote_host=$1\n  local remote_command=$2\n  ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -T $remote_host $remote_command\n}\n\n# Get remote server's uname\nget_remote_uname() {\n  local remote_host=$1\n  local remote_uname=$(execute_remote_command $remote_host \"uname\")\n  echo $remote_uname\n}\n\n# Get remote server's os name\nget_remote_osname() {\n  local remote_host=$1\n  local osname_command='cat /etc/os-release \n| grep -w NAME \n| awk -F '\\''='\\'' '\\''{print $2;}'\\'' \n| awk -F '\\''\"'\\'' '\\''{if (NF==3) {print $2} else {print $1}}'\\'' \n| awk '\\''{print $1}'\\'''\n  local remote_osname=$(execute_remote_command $remote_host \"$osname_command\")\n  echo \"$remote_osname\"\n}\n\n# Get remote Ubuntu server's version\nget_os_version() {\n  local remote_host=$1\n  local os_version_command='cat /etc/os-release \n| grep -w VERSION_ID \n| awk -F '\\''='\\'' '\\''{print $2;}'\\'' \n| awk -F '\\''\"'\\'' '\\''{if (NF==3) {print $2} else {print $1}}'\\'''\n  local remote_os_version=$(execute_remote_command $remote_host \"$os_version_command\")\n  echo \"$remote_os_version\"\n}\n\n# Get remote CentOS server's version\nget_centos_version() {\n  local remote_host=$1\n  local centos_version_command='cat /etc/system-release | awk '\\''{print $4}'\\'''\n  local remote_centos_version=$(execute_remote_command $remote_host \"$centos_version_command\")\n  echo \"$remote_centos_version\"\n}\n#---4. Tool functions section end---#\n\n#---5. Parse cluster servers section begin---#\nINT_REGEXP='^[0-9]+$'\nRANGE_REGEXP='^\\[[0-9]*, *[0-9]*\\]$'\n\nparse_value_in_section() {\n  local conf_file=$1\n  local section_name=$2\n  local field_name=$3\n  local value=$(\\\nsed -n \"/^\\\\[$section_name\\\\]/,/^\\[/ p\" $conf_file | \\\nsed -n \"/^$field_name *=/ p\" | \\\nsed 's/\\([^=]*\\)=\\([^=]\\)/\\2/g' | \\\nsed 's/ *^//g' | sed 's/ *$//g')\n  echo $value\n}\n\nparse_fstore_servers() {\n  local conf_file=$1\n  if ! [ -f $conf_file ]; then\n    echo \"ERROR: fstore cluster config file $conf_file does not exist.\"\n    exit 1\n  fi\n  local server_groups=`sed -n '/^\\[server-group/ p' $conf_file | sed 's/\\[//' | sed 's/\\]//'`\n  for server_group in ${server_groups[@]}; do\n    let fstore_group_count=$fstore_group_count+1\n    local fstore_server_hosts=\"\"\n    local server_ids=$(parse_value_in_section $conf_file $server_group \"server_ids\")\n    # echo \"server_ids in $server_group is:==$server_ids==\"\n    if [[ $server_ids =~ $INT_REGEXP ]]; then\n      #单个整数，只有一个节点\n      # echo \"$server_ids is integer\"\n      local server_host=$(parse_value_in_section $conf_file \"server-$server_ids\" \"host\")\n      server_host=${server_host%%:*}\n      if [ -n server_host ]; then\n        fstore_server_hosts=$server_host\n      fi\n    elif [[ $server_ids =~ $RANGE_REGEXP ]]; then\n      # echo \"[$server_ids] is range\"\n      local src_str=`echo $server_ids | sed 's/\\[//' | sed 's/\\]//' | sed 's/, */../'`\n      local dest=\"echo {$src_str}\"\n      for server_id in $(eval $dest); do\n        local server_host=$(parse_value_in_section $conf_file \"server-$server_id\" \"host\")\n        server_host=${server_host%%:*}\n        if [ -n server_host ]; then\n          fstore_server_hosts=\"$fstore_server_hosts $server_host\"\n        fi\n      done \n    else\n      echo \"ERROR: Invalid server_ids format:[$server_ids] in config file $conf_file\"\n      exit 1\n    fi\n    fstore_server_hosts=`echo $fstore_server_hosts | sed 's/ *^//g'`\n    if [[ -n $fstore_server_hosts ]]; then\n      local array_str=\"fstore_group_$fstore_group_count=($fstore_server_hosts)\"\n      eval $array_str\n    else\n      echo \"ERROR: Parse fstore server hosts failed, $conf_file must conform to format below:\"\n      echo \"  [server-group-1]\"\n      echo \"  server_ids = [1, 3]\"\n      echo \"  # Or server_ids = 1\"\n      echo \"  [server-1]\"\n      echo \"  host = 10.0.1.11\"\n      exit 1\n    fi\n  done\n}\n\nparse_fdir_servers() {\n  local conf_file=$1\n  if ! [ -f $conf_file ]; then\n    echo \"ERROR: fdir cluster config file $conf_file does not exist.\"\n    exit 1\n  fi\n  local fdir_servers=`sed -n '/^\\[server-/ p' $conf_file | sed 's/\\[//' | sed 's/\\]//'`\n  local fdir_server_hosts=\"\"\n  for fdir_server in ${fdir_servers[@]}; do\n    local server_host=$(parse_value_in_section $conf_file $fdir_server \"host\")\n    server_host=${server_host%%:*}\n    if [ -n server_host ]; then\n      fdir_server_hosts=\"$fdir_server_hosts $server_host\"\n    fi\n  done\n  fdir_server_hosts=`echo $fdir_server_hosts | sed 's/ *^//g'`\n  if [[ -n $fdir_server_hosts ]]; then\n    local array_str=\"fdir_group=($fdir_server_hosts)\"\n    eval $array_str\n  else\n    echo \"ERROR: Parse fdir server hosts failed, $conf_file must conform to format below:\"\n    echo \"  [server-1]\"\n    echo \"  host = 10.0.1.11\"\n    echo \"  ...\"\n    exit 1\n  fi\n}\n\nparse_fauth_servers() {\n  local conf_file=$1\n  if ! [ -f $conf_file ]; then\n    echo \"ERROR: fauth cluster config file $conf_file does not exist.\"\n    exit 1\n  fi\n  local fauth_servers=`sed -n '/^\\[server-/ p' $conf_file | sed 's/\\[//' | sed 's/\\]//'`\n  local fauth_server_hosts=\"\"\n  for fauth_server in ${fauth_servers[@]}; do\n    local server_host=$(parse_value_in_section $conf_file $fauth_server \"host\")\n    server_host=${server_host%%:*}\n    if [ -n server_host ]; then\n      fauth_server_hosts=\"$fauth_server_hosts $server_host\"\n    fi\n  done\n  fauth_server_hosts=`echo $fauth_server_hosts | sed 's/ *^//g'`\n  if [[ -n $fauth_server_hosts ]]; then\n    local array_str=\"fauth_group=($fauth_server_hosts)\"\n    eval $array_str\n  else\n    echo \"ERROR: Parse fauth server hosts failed, $conf_file must conform to format below:\"\n    echo \"  [server-1]\"\n    echo \"  host = 10.0.1.11\"\n    echo \"  ...\"\n    exit 1\n  fi\n}\n\nparse_fvote_servers() {\n  local conf_file=$1\n  if ! [ -f $conf_file ]; then\n    echo \"ERROR: fvote cluster config file $conf_file does not exist.\"\n    exit 1\n  fi\n  local fvote_servers=`sed -n '/^\\[server-/ p' $conf_file | sed 's/\\[//' | sed 's/\\]//'`\n  local fvote_server_hosts=\"\"\n  for fvote_server in ${fvote_servers[@]}; do\n    local server_host=$(parse_value_in_section $conf_file $fvote_server \"host\")\n    server_host=${server_host%%:*}\n    if [ -n server_host ]; then\n      fvote_server_hosts=\"$fvote_server_hosts $server_host\"\n    fi\n  done\n  fvote_server_hosts=`echo $fvote_server_hosts | sed 's/ *^//g'`\n  if [[ -n $fvote_server_hosts ]]; then\n    local array_str=\"fvote_group=($fvote_server_hosts)\"\n    eval $array_str\n  else\n    echo \"ERROR: Parse fvote server hosts failed, $conf_file must conform to format below:\"\n    echo \"  [server-1]\"\n    echo \"  host = 10.0.1.11\"\n    echo \"  ...\"\n    exit 1\n  fi\n}\n#---5. Parse cluster servers section end---#\n\n#---6. Settings and cluster info section begin---#\ncheck_fcfs_cache_path() {\n  if ! [ -d $fcfs_cache_path ]; then\n    if ! mkdir -p $fcfs_cache_path; then\n      echo \"ERROR: Create fcfs cache path failed, $fcfs_cache_path!\"\n      exit 1\n    else\n      echo \"INFO: Created fcfs cache path successfully, $fcfs_cache_path.\"\n    fi\n  fi\n}\n\n# Load cluster settings from file fcfs.settings\nload_fcfs_settings() {\n  if ! [ -f $fcfs_settings_file ]; then\n    echo \"ERROR: File $fcfs_settings_file does not exist\"\n    exit 1\n  else\n    fcfs_settings=`sed -e 's/#.*//' -e '/^$/ d' $fcfs_settings_file`\n    eval $fcfs_settings\n    split_to_array $fuseclient_ips fuseclient_ip_array\n    if [ -z $fastcfs_version ]; then\n       echo \"ERROR: param fastcfs_version has no value in $fcfs_settings_file\"\n       exit 1\n    fi\n    if [ -z $fuseclient_ips ]; then\n       echo \"ERROR: param fuseclient_ips has no value in $fcfs_settings_file\"\n       exit 1\n    fi\n  fi\n}\n\n# Load installed settings from file .fcfs/installed.settings\nload_installed_settings() {\n  local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file\n  if [ -f $installed_mark_file ]; then\n    local installed_marks=`sed -e 's/#.*//' -e '/^$/ d' $installed_mark_file`\n    eval $installed_marks\n  fi\n}\n\n# Load cluster dependent lib version settings from file dependency.[FastCFS-version].settings\nload_dependency_settings() {\n  dependency_file_version=$1\n  if [ -z $dependency_file_version ]; then\n    echo \"ERROR: Dependency file version cannot be empty.\"\n    exit 1\n  fi\n  dependency_settings_file=\"$fcfs_cache_path/dependency.$dependency_file_version.settings\"\n  if ! [ -f $dependency_settings_file ]; then\n    # File not exist in local path, will get it from remote server match the version\n    echo \"WARN: File $dependency_settings_file does not exist, getting it from remote server $fcfs_dependency_file_server.\"\n    check_fcfs_cache_path\n    remote_file_url=\"$fcfs_dependency_file_server/$dependency_file_version\"\n    download_res=`curl -f -o $dependency_settings_file $remote_file_url`\n\n    if ! [ -f $dependency_settings_file ]; then\n      echo \"ERROR: Cannot download file $dependency_settings_file from $remote_file_url.\"\n      echo \"       Please make sure that remote file exists and network is accessible.\"\n      exit 1\n    fi\n  fi\n  dependency_settings=`sed -e 's/#.*//' -e '/^$/ d' $dependency_settings_file`\n  eval $dependency_settings\n  if [ -z $libfastcommon ]; then\n    echo \"WARN: Dependency libfastcommon has no version value in $dependency_settings_file.\"\n  fi\n  if [ -z $libserverframe ]; then\n    echo \"WARN: Dependency libserverframe has no version value in $dependency_settings_file.\"\n  fi\n  if [ -z $fvote ]; then\n    echo \"WARN: Dependency fvote has no version value in $dependency_settings_file.\"\n  fi\n  if [ -z $fauth ]; then\n    echo \"WARN: Dependency fauth has no version value in $dependency_settings_file.\"\n  fi\n  if [ -z $fdir ]; then\n    echo \"WARN: Dependency fdir has no version value in $dependency_settings_file.\"\n  fi\n  if [ -z $fstore ]; then\n    echo \"WARN: Dependency fstore has no version value in $dependency_settings_file.\"\n  fi\n}\n\n# Get module name from command args, if not specify, will handle all modules.\nfdir_need_execute=0\nfstore_need_execute=0\nfauth_need_execute=0\nfvote_need_execute=0\nfuseclient_need_execute=0\n\nhas_module_param=1\ncheck_module_param() {\n  if [ $# -gt 1 ]; then\n    module_name=$2\n    case \"$module_name\" in\n      'fdir')\n        fdir_need_execute=1\n      ;;\n      'fstore')\n        fstore_need_execute=1\n      ;;\n      'fauth')\n        fauth_need_execute=1\n      ;;\n      'fvote')\n        fvote_need_execute=1\n      ;;\n      'fuseclient')\n        fuseclient_need_execute=1\n      ;;\n      *)\n        has_module_param=0\n        if ! [ $shell_command = 'tail' ] || ! [[ $module_name =~ ^- ]]; then\n          echo \"ERROR: Module name invalid, $module_name.\"\n          echo \"       Allowed module names: fdir, fstore, fauth, fvote, fuseclient.\"\n          exit 1\n        else\n          fdir_need_execute=1\n          fstore_need_execute=1\n          fauth_need_execute=1\n          fvote_need_execute=1\n          fuseclient_need_execute=1\n        fi\n      ;;\n    esac\n  else\n    has_module_param=0\n    fdir_need_execute=1\n    fstore_need_execute=1\n    fauth_need_execute=1\n    fvote_need_execute=1\n    fuseclient_need_execute=1\n  fi\n}\n\n# Get node host from command args, if not specify, will handle all nodes.\n# It must specify after module name.\nhas_node_param=0\ncheck_node_param() {\n  if [ $# -gt 2 ] && ! [[ $3 =~ ^- ]] && [ $has_module_param = 1 ]; then\n    node_host_need_execute=$3\n    has_node_param=1\n  fi\n}\n\n# Load cluster groups from cluster config files.\n#   fdir_group=(172.16.168.128)\n#   fstore_group_count=1\n#   fstore_group_1=(172.16.168.128)\n#   fauth_group=(172.16.168.128)\nload_cluster_groups() {\n  if [ $fdir_need_execute -eq 1 ]; then\n    parse_fdir_servers \"conf/fdir/cluster.conf\"\n  fi\n  if [ $fstore_need_execute -eq 1 ]; then\n    parse_fstore_servers \"conf/fstore/cluster.conf\"\n  fi\n  if [ $fauth_need_execute -eq 1 ]; then\n    parse_fauth_servers \"conf/auth/cluster.conf\"\n  fi\n  if [ $fvote_need_execute -eq 1 ]; then\n    parse_fvote_servers \"conf/vote/cluster.conf\"\n  fi\n}\n\nreplace_cluster_osname_mark() {\n  local mark_file=$1\n  local remote_osname=$2\n  sed_replace \"s#^cluster_host_osname=.*#cluster_host_osname=$remote_osname#g\" $mark_file\n}\n\nsave_cluster_osname_mark() {\n  check_fcfs_cache_path\n  local remote_osname=$1\n  local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file\n  if [ -f $installed_mark_file ]; then\n    local cluster_osname_marks=`grep cluster_host_osname $installed_mark_file`\n    if [ -z $cluster_osname_marks ]; then\n      echo \"cluster_host_osname=$remote_osname\" >> $installed_mark_file\n    else\n      # replace old value\n      replace_cluster_osname_mark $installed_mark_file \"$remote_osname\"\n    fi\n  else\n    echo \"cluster_host_osname=$remote_osname\" >> $installed_mark_file\n  fi\n}\n\n# Check remote host os and version.\n#   The whole cluster's host must have same os type.\n#   The hosts os version must great than or equal xxx_MIN_VERSION\ncheck_remote_os_and_version() {\n  pkg_manage_tool=\"\"\n  cluster_host_osname=\"\"\n  declare -a all_server_ips\n  all_server_ips+=(${fdir_group[@]})\n  if [ ! -z $fstore_group_count ]; then\n    for ((i=1; i <= $fstore_group_count; i++)); do\n      local fstore_group=\"fstore_group_$i[@]\"\n      all_server_ips+=(${!fstore_group})\n    done\n  fi\n  all_server_ips+=(${fauth_group[@]})\n  all_server_ips+=(${fvote_group[@]})\n  all_server_ips+=(${fuseclient_ip_array[@]})\n\n  declare -a distinct_server_ips\n  for server_ip in ${all_server_ips[@]}; do\n    if ! [[ ${distinct_server_ips[*]} =~ (^|[[:space:]])\"$server_ip\"($|[[:space:]]) ]]; then\n      distinct_server_ips+=(\"$server_ip\")\n      echo \"INFO: Begin check os name and version on server $server_ip\"\n      local remote_uname=$(get_remote_uname $server_ip)\n      if [ $remote_uname = 'Linux' ]; then\n        local remote_osname=$(get_remote_osname $server_ip)\n        if [ \"$cluster_host_osname\" != '' ]; then\n          if [ \"$cluster_host_osname\" != \"$remote_osname\" ]; then\n            echo \"Error: Cluster's servers must have same OS, $cluster_host_osname and $remote_osname cannot be mixed use\"\n            exit 1\n          fi\n        else\n          cluster_host_osname=$remote_osname\n          if [[ \" ${YUM_OS_ARRAY[@]} \" =~ \" ${remote_osname} \" ]]; then\n            pkg_manage_tool=\"yum\"\n          elif [[ \" ${APT_OS_ARRAY[@]} \" =~ \" ${remote_osname} \" ]]; then\n            pkg_manage_tool=\"apt\"\n          fi\n        fi\n\n        local remote_osversion\n        if [ $remote_osname = 'CentOS' ]; then\n          remote_osversion=$(get_centos_version $server_ip)\n        elif [[ \" ${YUM_OS_ARRAY[@]} \" =~ \" ${remote_osname} \" ]] || [[ \" ${APT_OS_ARRAY[@]} \" =~ \" ${remote_osname} \" ]]; then\n          remote_osversion=$(get_os_version $server_ip)\n        else\n          echo \"Error: Unsupport OS, $remote_osname on server $server_ip\"\n          exit 1\n        fi\n        declare -i remote_os_major_version=$(echo $remote_osversion | awk -F '.' '{print $1}')\n        local min_version_name=\"MIN_VERSION_OF_$remote_osname\"\n        local min_version=${!min_version_name}\n        if [ ! -z $min_version ] && [ $remote_os_major_version -lt $min_version ]; then\n          echo \"$remote_osname's version must be great than or equal $min_version, but was $remote_os_major_version on server $server_ip\"\n          exit 1\n        fi\n      else\n        echo \"ERROR: Unsupport OS, $remote_uname on server $server_ip\"\n        exit 1\n      fi\n    fi\n  done\n  if [ \"$cluster_host_osname\" != '' ]; then\n    save_cluster_osname_mark \"$cluster_host_osname\"\n  else\n    echo \"ERROR: Check remote host os failed.\"\n    exit 1\n  fi\n}\n\nfuseclient_share_fdir=0\nfuseclient_share_fstore=0\nfuseclient_share_fauth=0\nfuseclient_share_fvote=0\n\ncheck_if_client_share_servers() {\n  if ! [ ${#fuseclient_ip_array[@]} -eq 0 ]; then\n    for fuseclient_server_ip in ${fuseclient_ip_array[@]}; do\n      for fdir_server_ip in ${fdir_group[@]}; do\n        if [ $fdir_server_ip = \"$fuseclient_server_ip\" ]; then\n          fuseclient_share_fdir=1\n        fi\n      done\n\n      if [ ! -z $fstore_group_count ]; then\n        for ((i=1; i <= $fstore_group_count; i++)); do\n          local fstore_group=\"fstore_group_$i[@]\"\n          for fstore_server_ip in ${!fstore_group}; do\n            if [ $fstore_server_ip = \"$fuseclient_server_ip\" ]; then\n              fuseclient_share_fstore=1\n            fi\n          done\n        done\n      fi\n\n      for fauth_server_ip in ${fauth_group[@]}; do\n        if [ $fauth_server_ip = \"$fuseclient_server_ip\" ]; then\n          fuseclient_share_fauth=1\n        fi\n      done\n      for fvote_server_ip in ${fvote_group[@]}; do\n        if [ $fvote_server_ip = \"$fuseclient_server_ip\" ]; then\n          fuseclient_share_fvote=1\n        fi\n      done\n    done\n  fi\n}\n#---6. Settings and cluster info section end---#\n\n#---7. Iterate hosts for execute command section begin---#\nexecute_command_on_fdir_servers() {\n  local command_name=$1\n  local function_name=$2\n  local module_name=$3\n  if [ $fdir_need_execute -eq 1 ]; then\n    local fdir_node_match_setting=0\n    for fdir_server_ip in ${fdir_group[@]}; do\n      if [ -z $node_host_need_execute ] || [ $fdir_server_ip = \"$node_host_need_execute\" ]; then\n        if [ $command_name = 'status' ]; then\n          echo \"\"\n          echo \"INFO: Begin display $module_name status on server $fdir_server_ip.\"\n        else\n          echo \"INFO: Begin $command_name $module_name on server $fdir_server_ip.\"\n        fi\n        $function_name $fdir_server_ip \"$module_name\" $command_name\n      fi\n      if [ $fdir_server_ip = \"$node_host_need_execute\" ]; then\n        fdir_node_match_setting=1\n      fi\n    done\n    if ! [ -z $node_host_need_execute ] && [ $fdir_node_match_setting -eq 0 ]; then\n      echo \"ERROR: The node $node_host_need_execute not match host in fdir cluster.conf.\"\n    fi\n  fi\n}\n\nexecute_command_on_fstore_servers() {\n  local command_name=$1\n  local function_name=$2\n  local module_name=$3\n  if [ $fstore_need_execute -eq 1 ]; then\n    local fstore_node_match_setting=0\n    for ((i=1; i <= $fstore_group_count; i++)); do\n      local fstore_group=\"fstore_group_$i[@]\"\n      for fstore_server_ip in ${!fstore_group}; do\n        if [ -z $node_host_need_execute ] || [ $fstore_server_ip = \"$node_host_need_execute\" ]; then\n          if [ $command_name = 'status' ]; then\n            echo \"\"\n            echo \"INFO: Begin display $module_name status on server $fstore_server_ip.\"\n          else\n            echo \"INFO: Begin $command_name $module_name on server $fstore_server_ip.\"\n          fi\n          $function_name $fstore_server_ip \"$module_name\" $command_name\n        fi\n        if [ $fstore_server_ip = \"$node_host_need_execute\" ]; then\n          fstore_node_match_setting=1\n        fi\n      done\n    done\n    if ! [ -z $node_host_need_execute ] && [ $fstore_node_match_setting -eq 0 ]; then\n      echo \"ERROR: The node $node_host_need_execute not match host in fstore cluster.conf.\"\n    fi\n  fi\n}\n\nexecute_command_on_fauth_servers() {\n  local command_name=$1\n  local function_name=$2\n  local module_name=$3\n  if [ $fauth_need_execute -eq 1 ]; then\n    local fauth_node_match_setting=0\n    for fauth_server_ip in ${fauth_group[@]}; do\n      if [ -z $node_host_need_execute ] || [ $fauth_server_ip = \"$node_host_need_execute\" ]; then\n        if [ $command_name = 'status' ]; then\n          echo \"\"\n          echo \"INFO: Begin display $module_name status on server $fauth_server_ip.\"\n        else\n          echo \"INFO: Begin $command_name $module_name on server $fauth_server_ip.\"\n        fi\n        $function_name $fauth_server_ip \"$module_name\" $command_name\n      fi\n      if [ $fauth_server_ip = \"$node_host_need_execute\" ]; then\n        fauth_node_match_setting=1\n      fi\n    done\n    if ! [ -z $node_host_need_execute ] && [ $fauth_node_match_setting -eq 0 ]; then\n      echo \"ERROR: The node $node_host_need_execute not match host in fauth cluster.conf.\"\n    fi\n  fi\n}\n\nexecute_command_on_fvote_servers() {\n  local command_name=$1\n  local function_name=$2\n  local module_name=$3\n  if [ $fvote_need_execute -eq 1 ]; then\n    local fvote_node_match_setting=0\n    for fvote_server_ip in ${fvote_group[@]}; do\n      if [ -z $node_host_need_execute ] || [ $fvote_server_ip = \"$node_host_need_execute\" ]; then\n        if [ $command_name = 'status' ]; then\n          echo \"\"\n          echo \"INFO: Begin display $module_name status on server $fvote_server_ip.\"\n        else\n          echo \"INFO: Begin $command_name $module_name on server $fvote_server_ip.\"\n        fi\n        $function_name $fvote_server_ip \"$module_name\" $command_name\n      fi\n      if [ $fvote_server_ip = \"$node_host_need_execute\" ]; then\n        fvote_node_match_setting=1\n      fi\n    done\n    if ! [ -z $node_host_need_execute ] && [ $fvote_node_match_setting -eq 0 ]; then\n      echo \"ERROR: The node $node_host_need_execute not match host in fvote cluster.conf.\"\n    fi\n  fi\n}\n\nexecute_command_on_fuseclient_servers() {\n  local command_name=$1\n  local function_name=$2\n  local module_name=$3\n  if [ $fuseclient_need_execute -eq 1 ]; then\n    if [ ${#fuseclient_ip_array[@]} -eq 0 ]; then\n      echo \"ERROR: Param fuseclient_ips has no value in $fcfs_settings_file.\"\n      exit 1\n    else\n      local fuseclient_node_match_setting=0\n      for fuseclient_server_ip in ${fuseclient_ip_array[@]}; do\n        if [ -z $node_host_need_execute ] || [ $fuseclient_server_ip = \"$node_host_need_execute\" ]; then\n          if [ $command_name = 'status' ]; then\n            echo \"\"\n            echo \"INFO: Begin display $module_name status on server $fuseclient_server_ip.\"\n          else\n            echo \"INFO: begin $command_name $module_name on server $fuseclient_server_ip.\"\n          fi\n          $function_name $fuseclient_server_ip \"$module_name\" $command_name\n        fi\n        if [ $fuseclient_server_ip = \"$node_host_need_execute\" ]; then\n          fuseclient_node_match_setting=1\n        fi\n      done\n      if ! [ -z $node_host_need_execute ] && [ $fuseclient_node_match_setting -eq 0 ]; then\n        echo \"ERROR: The node $node_host_need_execute not match param fuseclient_ips in $fcfs_settings_file.\"\n        exit 1\n      fi\n    fi\n  fi\n}\n#---7. Iterate hosts for execute command section end---#\n\n#---8. Install section begin---#\ncheck_remote_osname() {\n  uname=$(uname)\n  if [ $uname = 'Linux' ]; then\n    osname=$(cat /etc/os-release | grep -w NAME | awk -F '=' '{print $2;}' | \\\n            awk -F '\"' '{if (NF==3) {print $2} else {print $1}}' | awk '{print $1}')\n    if [ $osname = 'CentOS' ]; then\n      osversion=$(cat /etc/system-release | awk '{print $4}')\n    fi\n    if [ -z $osversion ]; then\n      osversion=$(cat /etc/os-release | grep -w VERSION_ID | awk -F '=' '{print $2;}' | \\\n              awk -F '\"' '{if (NF==3) {print $2} else {print $1}}')\n    fi\n    os_major_version=$(echo $osversion | awk -F '.' '{print $1}')\n    if [ $osname = 'Fedora' ]; then\n      if [ $os_major_version -lt 20 ]; then\n        os_major_version=6\n      elif [ $os_major_version -lt 28 ]; then\n        os_major_version=7\n      elif [ $os_major_version -lt 38 ]; then\n        os_major_version=8\n      else\n        os_major_version=9\n      fi\n    elif [ $osname = 'Alibaba' ]; then\n      if [ $os_major_version -lt 3 ]; then\n        os_major_version=7\n      elif [ $os_major_version -lt 4 ]; then\n        os_major_version=8\n      else\n        os_major_version=9\n      fi\n    elif [ $osname = 'Amazon' ]; then\n      if [ $os_major_version -lt 3 ]; then\n        os_major_version=7\n      elif [ $os_major_version -lt 2023 ]; then\n        os_major_version=8\n      else\n        os_major_version=9\n      fi\n    elif [ $osname = 'openEuler' ]; then\n      if [ $os_major_version -lt 24 ]; then\n        os_major_version=8\n      else\n        os_major_version=9\n      fi\n    elif [ $osname = 'Kylin' ]; then\n      os_major_version=$(echo $os_major_version | awk '{print $1}')\n      if [ $os_major_version = 'V10' ]; then\n        os_major_version=8\n      else\n        os_major_version=9\n      fi\n    elif [ $osname = 'UOS' ] || [ $osname = 'BigCLoud' ]; then\n      os_major_version=8\n    fi\n  else\n    echo \"Error: Unsupport OS, $uname\" 1>&2\n    exit 1\n  fi\n}\n\ncheck_yum_install_fastos_repo() {\n  repo=$(rpm -q FastOSrepo 2>/dev/null)\n  if [ $? -ne 0 ]; then\n    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\n    if [ $? -ne 0 ]; then\n      echo \"ERROR: FastOSrepo rpm install failed.\"\n      exit 1\n    fi\n  fi\n}\n\ncheck_apt_install_fastos_repo() {\n  if [ ! -f /etc/apt/sources.list.d/fastos.list ]; then\n    sudo apt-get install curl gpg -y\n    sudo curl http://www.fastken.cn/aptrepo${repo_affix}/packages.fastos.pub | sudo gpg --dearmor > /tmp/fastos-archive-keyring.gpg\n    sudo install -D -o root -g root -m 644 /tmp/fastos-archive-keyring.gpg /usr/share/keyrings/fastos-archive-keyring.gpg\n    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'\n    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'\n    sudo rm -f /tmp/fastos-archive-keyring.gpg\n  fi\n}\n\nexecute_yum() {\n  local yum_command=$1\n  local program_name=$2\n  repo_affix=$3\n  if [ $os_major_version -ge 7 ]; then\n    check_yum_install_fastos_repo\n    echo \"INFO: yum $yum_command $program_name -y.\"\n    sudo yum $yum_command $program_name -y\n    if [ $? -ne 0 ]; then\n      echo \"ERROR: \\\"yum $yum_command $program_name -y\\\" execute failed.\"\n      exit 1\n    fi\n  else\n    echo \"ERROR: Unsupport OS, $uname\" 1>&2\n    echo \"       Command setup and install can only be used for CentOS 7 or higher.\"\n    exit 1\n  fi\n}\n\nexecute_apt() {\n  local apt_command=$1\n  local program_name=$2\n  repo_affix=$3\n  if [ $apt_command = 'install' ]; then\n    check_apt_install_fastos_repo\n    sudo apt-get update\n  fi\n  echo \"INFO: apt $apt_command $program_name -y.\"\n  sudo apt-get $apt_command $program_name -y\n  if [ $? -ne 0 ]; then\n    echo \"ERROR: \\\"apt $apt_command $program_name -y\\\" execute failed.\"\n    exit 1\n  fi\n}\n\nexecute_yum_on_remote() {\n  local remote_host=$1\n  local program_name=$2\n  local yum_command=$3\n  execute_remote_command_no_return $remote_host \"\n$(declare -f check_remote_osname);\ncheck_remote_osname;\n$(declare -f check_yum_install_fastos_repo);\n$(declare -f execute_yum);\nexecute_yum $yum_command \\\"$program_name\\\" \\\"$repo_affix\\\"\"\n  if [ $? -ne 0 ]; then\n    exit 1\n  fi\n}\n\nexecute_apt_on_remote() {\n  local remote_host=$1\n  local program_name=$2\n  local apt_command=$3\n  execute_remote_command_no_return $remote_host \"\n$(declare -f check_apt_install_fastos_repo);\n$(declare -f execute_apt);\nexecute_apt $apt_command \\\"$program_name\\\" \\\"$repo_affix\\\"\"\n  if [ $? -ne 0 ]; then\n    exit 1\n  fi\n}\n\nreplace_installed_version_mark() {\n  # Replace host with ip of current host.\n  local mark_file=$1\n  local installed_version=$2\n  sed_replace \"s#^fastcfs_version_installed=.*#fastcfs_version_installed=$installed_version#g\" $mark_file\n}\n\nsave_installed_version_mark() {\n  check_fcfs_cache_path\n  local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file\n  if [ -f $installed_mark_file ]; then\n    local installed_marks=`grep fastcfs_version_installed $installed_mark_file`\n    if [ -z $installed_marks ]; then\n      echo \"fastcfs_version_installed=$fastcfs_version\" >> $installed_mark_file\n    else\n      # replace old value\n      replace_installed_version_mark $installed_mark_file \"$fastcfs_version\"\n    fi\n  else\n    echo \"fastcfs_version_installed=$fastcfs_version\" >> $installed_mark_file\n  fi\n}\n\nremove_installed_mark() {\n  local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file\n  if [ -f $installed_mark_file ]; then\n    if ! rm -rf $installed_mark_file; then\n      echo \"ERROR: Delete installed mark file failed, $installed_mark_file!\"\n      exit 1\n    fi\n  fi\n}\n\nsave_installed_mark() {\n  check_fcfs_cache_path\n  local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file\n  if [ -f $installed_mark_file ]; then\n    local installed_marks=`grep fastcfs_installed $installed_mark_file`\n    if [ -z $installed_marks ]; then\n      echo \"fastcfs_installed=1\" >> $installed_mark_file\n    fi\n  else\n    echo \"fastcfs_installed=1\" >> $installed_mark_file\n  fi\n}\n\ncheck_installed_version() {\n  # First install cannot specify module\n  if [ -z $fastcfs_version_installed ] && [ $has_module_param = 1 ]; then\n    echo \"ERROR: First execute setup or install cannot specify module.\"\n    exit 1\n  elif ! [ -z $fastcfs_version_installed ] && [ $fastcfs_version_installed = $fastcfs_version ]; then\n    if [ \"$shell_command\" = \"reinstall\" ]; then\n      # Before reinstall programm, need user to reconfirm\n      for ((;;)) do\n        echo -n \"WARN: Reinstall the installed program confirm[y/N]\"\n        read var\n        if ! [ \"$var\" = \"y\" ] && ! [ \"$var\" = \"Y\" ] && ! [ \"$var\" = \"yes\" ] && ! [ \"$var\" = \"YES\" ]; then\n          exit 1\n        fi\n        break;\n      done\n    else\n      if ! [ $has_module_param = 1 -a $has_node_param = 1 ]; then\n        echo \"ERROR: FastCFS $fastcfs_version have installed.\"\n        exit 1\n      fi\n    fi\n  elif ! [ -z $fastcfs_version_installed ] && ! [ $fastcfs_version_installed = $fastcfs_version ]; then\n    if version_le $fastcfs_version_installed $fastcfs_version; then\n      # Upgrade cannot specify module\n      if [ $has_module_param = 1 ]; then\n        echo \"ERROR: Upgrade cannot specify module.\"\n        exit 1\n      fi\n      for ((;;)) do\n        echo -n \"WARN: Upgrade installed program confirm, from $fastcfs_version_installed to $fastcfs_version ? [y/N]\"\n        read var\n        if ! [ \"$var\" = \"y\" ] && ! [ \"$var\" = \"Y\" ] && ! [ \"$var\" = \"yes\" ] && ! [ \"$var\" = \"YES\" ]; then\n          exit 1\n        fi\n        break;\n      done\n    else\n      # Downgrade must remove old version first\n      echo \"ERROR: Downgrade install must remove old version first.\"\n      exit 1\n    fi\n  fi\n}\n\n# Install packages to target by yum manage nodes.\ninstall_packages_by_yum() {\n  check_installed_version\n  fdir_programs=\"fastDIR-server-$fdir libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth\"\n  execute_command_on_fdir_servers install execute_yum_on_remote \"$fdir_programs\"\n  fstore_programs=\"faststore-server-$fstore libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth\"\n  execute_command_on_fstore_servers install execute_yum_on_remote \"$fstore_programs\"\n  fauth_programs=\"FastCFS-auth-server-$fauth libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth fastDIR-client-$fdir\"\n  execute_command_on_fauth_servers install execute_yum_on_remote \"$fauth_programs\"\n  fvote_programs=\"FastCFS-vote-server-$fvote libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-vote-client-$fvote\"\n  execute_command_on_fvote_servers install execute_yum_on_remote \"$fvote_programs\"\n  fuseclient_programs=\"FastCFS-fused-$fuseclient libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth FastCFS-api-libs-$fcfsapi faststore-client-$fstore fastDIR-client-$fdir\"\n  execute_command_on_fuseclient_servers install execute_yum_on_remote \"$fuseclient_programs\"\n  save_installed_mark\n  save_installed_version_mark\n}\n\n# Remove packages from target by yum manage nodes.\nerase_packages_by_yum() {\n  # Before remove programm, need user to reconfirm\n  for ((;;)) do\n    echo -n \"WARN: Delete the installed program confirm[y/N]\"\n    read var\n    if ! [ \"$var\" = \"y\" ] && ! [ \"$var\" = \"Y\" ] && ! [ \"$var\" = \"yes\" ] && ! [ \"$var\" = \"YES\" ]; then\n      exit 1\n    fi\n    break;\n  done\n  fdir_programs=\"fastDIR-server libfastcommon libserverframe FastCFS-auth-client FastOSrepo\"\n  execute_command_on_fdir_servers erase execute_yum_on_remote \"$fdir_programs\"\n  fstore_programs=\"faststore-server libfastcommon libserverframe FastCFS-auth-client FastOSrepo\"\n  execute_command_on_fstore_servers erase execute_yum_on_remote \"$fstore_programs\"\n  fauth_programs=\"FastCFS-auth-server libfastcommon libserverframe FastCFS-auth-client fastDIR-client FastOSrepo\"\n  execute_command_on_fauth_servers erase execute_yum_on_remote \"$fauth_programs\"\n  fvote_programs=\"FastCFS-vote-server libfastcommon libserverframe FastCFS-vote-client FastOSrepo\"\n  execute_command_on_fvote_servers erase execute_yum_on_remote \"$fvote_programs\"\n  fuseclient_programs=\"FastCFS-fused libfastcommon libserverframe FastCFS-auth-client FastCFS-api-libs faststore-client fastDIR-client FastOSrepo\"\n  execute_command_on_fuseclient_servers erase execute_yum_on_remote \"$fuseclient_programs\"\n  remove_installed_mark\n}\n\n# Reinstall packages to target by yum manage nodes.\nreinstall_packages_by_yum() {\n  check_installed_version\n  fdir_programs=\"fastDIR-server-$fdir libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth\"\n  execute_command_on_fdir_servers reinstall execute_yum_on_remote \"$fdir_programs\"\n  fstore_programs=\"faststore-server-$fstore libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth\"\n  execute_command_on_fstore_servers reinstall execute_yum_on_remote \"$fstore_programs\"\n  fauth_programs=\"FastCFS-auth-server-$fauth libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth fastDIR-client-$fdir\"\n  execute_command_on_fauth_servers reinstall execute_yum_on_remote \"$fauth_programs\"\n  fvote_programs=\"FastCFS-vote-server-$fauth libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-vote-client-$fauth\"\n  execute_command_on_fvote_servers reinstall execute_yum_on_remote \"$fvote_programs\"\n  fuseclient_programs=\"FastCFS-fused-$fuseclient libfastcommon-$libfastcommon libserverframe-$libserverframe FastCFS-auth-client-$fauth FastCFS-api-libs-$fcfsapi faststore-client-$fstore fastDIR-client-$fdir\"\n  execute_command_on_fuseclient_servers reinstall execute_yum_on_remote \"$fuseclient_programs\"\n  save_installed_mark\n  save_installed_version_mark\n}\n\n# Install packages to target by apt manage nodes.\ninstall_packages_by_apt() {\n  execute_command_on_fdir_servers install execute_apt_on_remote \"fastdir-server\"\n  execute_command_on_fstore_servers install execute_apt_on_remote \"faststore-server\"\n  execute_command_on_fauth_servers install execute_apt_on_remote \"fastcfs-auth-server\"\n  execute_command_on_fvote_servers install execute_apt_on_remote \"fastcfs-vote-server\"\n  execute_command_on_fuseclient_servers install execute_apt_on_remote \"fastcfs-fused\"\n  save_installed_mark\n}\n\n# Remove packages from target by apt manage nodes.\nerase_packages_by_apt() {\n  # Before remove programm, need user to reconfirm\n  for ((;;)) do\n    echo -n \"WARN: Delete the installed program confirm[y/N]\"\n    read var\n    if ! [ \"$var\" = \"y\" ] && ! [ \"$var\" = \"Y\" ] && ! [ \"$var\" = \"yes\" ] && ! [ \"$var\" = \"YES\" ]; then\n      exit 1\n    fi\n    break;\n  done\n  execute_command_on_fdir_servers remove execute_apt_on_remote \"fastdir-server\"\n  execute_command_on_fstore_servers remove execute_apt_on_remote \"faststore-server\"\n  execute_command_on_fauth_servers remove execute_apt_on_remote \"fastcfs-auth-server\"\n  execute_command_on_fvote_servers remove execute_apt_on_remote \"fastcfs-vote-server\"\n  execute_command_on_fuseclient_servers remove execute_apt_on_remote \"fastcfs-fused\"\n  remove_installed_mark\n}\n#---8. Install section end---#\n\n#---9. Config section begin---#\ncheck_remote_path() {\n  dest_path=$1\n  if ! [ -d $dest_path ]; then\n    if ! sudo mkdir -p $dest_path; then\n      echo \"ERROR: Create target conf path failed, $dest_path!\"\n      exit 1\n    fi\n  fi\n}\n\ncheck_path_on_remote() {\n  remote_host=$1\n  remote_target_path=$2\n  execute_remote_command_no_return $remote_host \"\n$(declare -f check_remote_path);\ncheck_remote_path $remote_target_path\"\n}\n\ncheck_paths_infile_remote() {\n  # Check all paths in this config file, if not exist, will create it.\n  check_conf_file=$1\n  check_result=`sed -n \\\n-e '/^mountpoint *=/ p' \\\n-e '/^base_path *=/ p' \\\n-e '/^path *=/ p' \\\n$check_conf_file|sed 's/\\([^=]*\\)=\\([^=]\\)/\\2/g'`\n\n  for check_path in ${check_result[@]}; do\n    if ! [ -d $check_path ]; then\n      if ! sudo mkdir -p $check_path; then\n        echo \"ERROR: Create target path in file $check_conf_file failed, $check_path!\"\n        exit 1\n      else\n        echo \"INFO: Created target path in file $check_conf_file successfully, $check_path.\"\n      fi\n    fi\n  done\n}\n\ncheck_paths_infile() {\n  remote_host=$1\n  remote_target_file=$2$3\n  execute_remote_command_no_return $remote_host \"\n$(declare -f check_paths_infile_remote);\ncheck_paths_infile_remote $remote_target_file\"\n}\n\ncopy_config_to_remote() {\n  local target_server_ip=$1\n  local target_module=$2\n  local src_path=\"conf/$target_module\"\n  local config_files=\"\"\n  local dest_path=\"\"\n\n  case \"$target_module\" in\n    'fdir')\n      config_file_array=\"${FDIR_CONF_FILES[*]}\"\n      dest_path=$FDIR_CONF_PATH\n    ;;\n    'fstore')\n      config_file_array=\"${STORE_CONF_FILES[*]}\"\n      dest_path=$STORE_CONF_PATH\n    ;;\n    'fauth')\n      config_file_array=\"${AUTH_CONF_FILES[*]}\"\n      src_path=\"conf/auth\"\n      dest_path=$AUTH_CONF_PATH\n    ;;\n    'keys')\n      config_file_array=\"${AUTH_KEYS_FILES[*]}\"\n      src_path=\"conf/auth/keys\"\n      dest_path=$AUTH_KEYS_PATH\n    ;;\n    'fvote')\n      config_file_array=\"${VOTE_CONF_FILES[*]}\"\n      src_path=\"conf/vote\"\n      dest_path=$VOTE_CONF_PATH\n    ;;\n    'fuseclient')\n      config_file_array=\"${FUSE_CONF_FILES[*]}\"\n      src_path=\"conf/fcfs\"\n      dest_path=$FUSE_CONF_PATH\n    ;;\n    *)\n      echo \"ERROR: Target module name is invalid, $target_module.\"\n      exit 1\n    ;;\n  esac\n\n  check_path_on_remote $target_server_ip $dest_path\n\n  for CONF_FILE in ${config_file_array[@]}; do\n    local tmp_src_file=$src_path/$CONF_FILE\n    if [ -f $tmp_src_file ]; then\n      echo \"INFO: Copy file $tmp_src_file to $dest_path of server $target_server_ip.\"\n      scp $tmp_src_file $target_server_ip:/tmp/\n      execute_remote_command_no_return $target_server_ip \"sudo cp /tmp/$CONF_FILE $dest_path\"\n      execute_remote_command_no_return $target_server_ip \"sudo rm /tmp/$CONF_FILE\"\n      if [ $? -ne 0 ]; then\n        exit 1\n      fi\n      local file_extension=\"${CONF_FILE##*.}\"\n      if [ $file_extension = \"conf\" ]; then\n        check_paths_infile $target_server_ip $dest_path $CONF_FILE\n      fi\n    else\n      echo \"WARN: File $tmp_src_file does not exist.\"\n    fi\n  done\n}\n\nsave_configed_mark() {\n  check_fcfs_cache_path\n  local installed_mark_file=$fcfs_cache_path/$fcfs_installed_file\n  if [ -f $installed_mark_file ]; then\n    local configed_marks=`grep fastcfs_configed $installed_mark_file`\n    if [ -z $configed_marks ]; then\n      echo \"fastcfs_configed=1\" >> $installed_mark_file\n    fi\n  else\n    echo \"fastcfs_configed=1\" >> $installed_mark_file\n  fi\n}\n\ndeploy_config_files() {\n  if [ -z $fastcfs_configed ] && [ $has_module_param = 1 ]; then\n    echo \"ERROR: First execute config cannot specify module.\"\n    exit 1\n  fi\n  execute_command_on_fvote_servers config copy_config_to_remote fvote\n  execute_command_on_fdir_servers config copy_config_to_remote fdir\n  execute_command_on_fstore_servers config copy_config_to_remote fstore\n  execute_command_on_fauth_servers config copy_config_to_remote fauth\n  execute_command_on_fauth_servers config copy_config_to_remote keys\n  execute_command_on_fuseclient_servers config copy_config_to_remote fuseclient\n\n  if [ $fuseclient_share_fvote -eq 0 ]; then\n    execute_command_on_fuseclient_servers config copy_config_to_remote fvote\n  fi\n  if [ $fuseclient_share_fdir -eq 0 ]; then\n    execute_command_on_fuseclient_servers config copy_config_to_remote fdir\n  fi\n  if [ $fuseclient_share_fstore -eq 0 ]; then\n    execute_command_on_fuseclient_servers config copy_config_to_remote fstore\n  fi\n  if [ $fuseclient_share_fauth -eq 0 ]; then\n    execute_command_on_fuseclient_servers config copy_config_to_remote fauth\n    execute_command_on_fuseclient_servers config copy_config_to_remote keys\n  fi\n  # Save configed mark into installed.settings\n  save_configed_mark\n}\n#---9. Config section end---#\n\n#---10. Service op section begin---#\nservice_op_on_remote() {\n  service_name=$1\n  operate_mode=$2\n  conf_file=$3\n  sudo $service_name $conf_file $operate_mode\n  if [ $? -ne 0 ] && [ $operate_mode != \"stop\" ] && [ $operate_mode != \"status\" ]; then\n    echo \"ERROR: Service $service_name $operate_mode failed.\"\n    exit 1\n  fi\n}\n\nservice_op() {\n  target_server_ip=$1\n  target_module=$2\n  operate_mode=$3\n  case \"$target_module\" in\n    'fvote')\n      service_name=\"fcfs_voted\"\n      conf_file=\"${VOTE_CONF_PATH}server.conf\"\n    ;;\n    'fdir')\n      service_name=\"fdir_serverd\"\n      conf_file=\"${FDIR_CONF_PATH}server.conf\"\n    ;;\n    'fstore')\n      service_name=\"fs_serverd\"\n      conf_file=\"${STORE_CONF_PATH}server.conf\"\n    ;;\n    'fauth')\n      service_name=\"fcfs_authd\"\n      conf_file=\"${AUTH_CONF_PATH}server.conf\"\n    ;;\n    'fuseclient')\n      service_name=\"fcfs_fused\"\n      conf_file=\"${FUSE_CONF_PATH}fuse.conf\"\n    ;;\n    *)\n      echo \"ERROR: Target module name is invalid, $target_module.\"\n      exit 1\n    ;;\n  esac\n  execute_remote_command_no_return $target_server_ip \"\n$(declare -f service_op_on_remote);\nservice_op_on_remote $service_name $operate_mode $conf_file\"\n  if [ $? -ne 0 ] && [ $operate_mode != \"stop\" ]; then\n    exit 1\n  fi\n}\n\ncluster_service_op() {\n  operate_mode=$1\n  execute_command_on_fvote_servers $operate_mode service_op fvote\n  if [ $operate_mode != 'stop' ] && [ $operate_mode != 'status' ]; then\n     sleep 1\n  fi\n  execute_command_on_fdir_servers $operate_mode service_op fdir\n  execute_command_on_fstore_servers $operate_mode service_op fstore\n  if [ $operate_mode != 'stop' ] && [ $operate_mode != 'status' ]; then\n     sleep 3\n  fi\n  execute_command_on_fauth_servers $operate_mode service_op fauth\n  if [ $operate_mode != 'stop' ] && [ $operate_mode != 'status' ]; then\n     sleep 1\n  fi\n  execute_command_on_fuseclient_servers $operate_mode service_op fuseclient\n}\n#---10. Service op section end---#\n\n#---11. Tail log section begin---#\ntail_remote_log() {\n  local tail_server=$1\n  local tail_args=$2\n  execute_remote_command_no_return $tail_server \"tail $tail_args\"\n  echo \"========================================================\"\n  echo \"========Log boundary====================================\"\n  echo \"========================================================\"\n}\n\n# View last N lines log of the cluster with system command tail;\ntail_log() {\n  shift\n  if [ $has_module_param = 1 ]; then\n    shift\n    if [ $has_node_param = 1 ]; then\n      shift\n    fi\n  fi\n  local tail_args=\"$*\"\n  execute_command_on_fdir_servers tail tail_remote_log \"$tail_args $FDIR_LOG_FILE\"\n  execute_command_on_fstore_servers tail tail_remote_log \"$tail_args $STORE_LOG_FILE\"\n  execute_command_on_fauth_servers tail tail_remote_log \"$tail_args $AUTH_LOG_FILE\"\n  execute_command_on_fvote_servers tail tail_remote_log \"$tail_args $VOTE_LOG_FILE\"\n  execute_command_on_fuseclient_servers tail tail_remote_log \"$tail_args $FUSE_LOG_FILE\"\n}\n#---11. Tail log section end---#\n\n#---12. Command execute section begin---#\ncheck_module_param $*\ncheck_node_param $*\nload_fcfs_settings\nload_cluster_groups\nload_installed_settings\ncase \"$shell_command\" in\n  'setup' | 'install' | 'reinstall')\n    check_remote_os_and_version\n    if [[ \" ${YUM_OS_ARRAY[@]} \" =~ \" ${cluster_host_osname} \" ]]; then\n      if [ -z $fastcfs_version ]; then\n        echo \"ERROR: Param fastcfs_version in $fcfs_settings_file cannot be empty.\"\n        exit 1\n      fi\n      load_dependency_settings $fastcfs_version\n    fi\n  ;;\n  'erase' | 'remove')\n    if [[ \" ${YUM_OS_ARRAY[@]} \" =~ \" ${cluster_host_osname} \" ]]; then\n      pkg_manage_tool=\"yum\"\n    elif [[ \" ${APT_OS_ARRAY[@]} \" =~ \" ${cluster_host_osname} \" ]]; then\n      pkg_manage_tool=\"apt\"\n    fi\n  ;;\nesac\n\ncase \"$shell_command\" in\n  'setup' | 'config')\n    check_if_client_share_servers\n  ;;\nesac\n\nif [ -z $fastcfs_installed ]; then\n  case \"$shell_command\" in\n    'reinstall' | 'erase' | 'remove' | 'config' | 'start' | 'restart' | 'stop' | 'tail' | 'status')\n      echo \"ERROR: The FastCFS softwares have not been installed, you must execute setup or install first.\"\n      exit 1\n    ;;\n  esac\nfi\nif [ -z $fastcfs_configed ]; then\n  case \"$shell_command\" in\n    'start' | 'restart' | 'stop' | 'tail' | 'status')\n      echo \"ERROR: The FastCFS softwares have not been configed, you must execute config first.\"\n      exit 1\n    ;;\n  esac\nfi\n\ncase \"$shell_command\" in\n  'setup')\n    (\"install_packages_by_$pkg_manage_tool\";deploy_config_files;cluster_service_op restart)\n  ;;\n  'install')\n    (\"install_packages_by_$pkg_manage_tool\")\n  ;;\n  'reinstall')\n    if [ \"$pkg_manage_tool\" = \"yum\" ]; then\n      (\"reinstall_packages_by_$pkg_manage_tool\")\n    else\n      echo \"ERROR: The reinstall command can only be used for yum.\"\n      exit 1\n    fi\n  ;;\n  'erase' | 'remove')\n    (\"erase_packages_by_$pkg_manage_tool\")\n  ;;\n  'config')\n    deploy_config_files\n  ;;\n  'start')\n    cluster_service_op start\n  ;;\n  'restart')\n    cluster_service_op restart\n  ;;\n  'stop')\n    cluster_service_op stop\n  ;;\n  'tail')\n    tail_log $*\n  ;;\n  'status')\n    cluster_service_op status\n  ;;\n  *)\n    print_usage\n    exit 1\n  ;;\nesac\n\nexit 0\n#---12. Command execute section end---#\n"
  },
  {
    "path": "shell/fcfs_conf.settings",
    "content": "# FastCFS cluster ops settings be used with shell fcfs_conf.sh\n# This settings file must be placed at same path with fcfs_conf.sh\n# You can edit it, or download it from http://www.fastcfs.com/ops.\n\nfastcfs_version=5.3.0\n\nvote_ips=10.0.1.11,10.0.1.12,10.0.1.13\nauth_ips=10.0.1.11,10.0.1.12,10.0.1.13\nfdir_ips=10.0.1.11,10.0.1.12,10.0.1.13\nfstore_group_count=1\nfstore_group_1=10.0.1.11,10.0.1.12,10.0.1.13\ndata_group_count=64\n"
  },
  {
    "path": "shell/fcfs_conf.sh",
    "content": "#!/bin/bash\n#\n# fcfs_conf.sh is a ops tool for quickly generate FastCFS cluster config files.\n# It only relying on bash shell access to the local fcfs_conf.settings file.\n# It runs fully on your workstation, requiring no servers, databases, or anything like that.\n#\n# If you want to generate cluster config files with specify server ips quickly,\n# this is for you.\n#\nfcfs_settings_file=\"fcfs_conf.settings\"\n# fcfs_tpl_file_server=\"http://www.fastken.cn/fastcfs/ops/dependency\"\n#conf.2.3.0.tpl.tar.gz\nfcfs_tpl_file_server=\"http://www.fastken.cn/fastcfs/ops/config\"\nfcfs_cache_path=\".fcfs\"\n\nLOCAL_CONF_PATH=\"conf\"\nSTORE_CONF_FILES=(client.conf server.conf cluster.conf storage.conf dbstore.conf)\nFDIR_CONF_FILES=(client.conf cluster.conf server.conf dbstore.conf)\nAUTH_CONF_FILES=(auth.conf client.conf cluster.conf server.conf session.conf)\nAUTH_KEYS_FILES=(session_validate.key)\nVOTE_CONF_FILES=(cluster.conf server.conf client.conf)\nFUSE_CONF_FILES=(fuse.conf)\nTRUNCATE_MARK_LINE=\"## Important:server group mark, don't modify this line.\"\n\nshell_name=$0\nshell_command=$1\nuname=$(uname)\n\n#---Usage info section begin---#\nprint_usage() {\n  # Print usage to console.\n  echo \"\"\n  echo \"Usage: $shell_name <command> [module]\"\n  echo \"\"\n  echo \"A self-sufficient config file generator shell for FastCFS cluster\"\n  echo \"\"\n  echo \"Commands:\"\n  echo \"  create     Create config files for FastCFS cluster with specify ips in $fcfs_settings_file\"\n  echo \"  help       Show the detail of commands and examples\"\n  echo \"\"\n}\n\nprint_detail_usage() {\n  # Print usage to console.\n  print_usage\n  echo \"Modules:\"\n  echo \"  fvote        FastCFS vote server\"\n  echo \"  fdir         fastDIR server\"\n  echo \"  fstore       faststore server\"\n  echo \"  fauth        FastCFS auth server\"\n  echo \"  fuseclient   FastCFS fuse client\"\n  echo \"\"\n  echo \"Node:\"\n  echo \"  You can specify a single module, or command will be executed on modules.\"\n  echo \"\"\n  echo \"Examples:\"\n  echo \"  $shell_name create\"\n  echo \"          Create config files for all modules.\"\n  echo \"\"\n  echo \"  $shell_name create fdir\"\n  echo \"          Create config files for fastDIR cluster.\"\n  echo \"\"\n  echo \"\"\n}\n\ncase \"$shell_command\" in\n  'create')\n  ;;\n  'help')\n    print_detail_usage\n    exit 1\n  ;;\n  *)\n    print_usage\n    exit 1\n  ;;\nesac\n#---Usage info section end---#\n\n#---Tool functions begin---#\nsed_replace()\n{\n    sed_cmd=$1\n    filename=$2\n    if [ \"$uname\" = \"FreeBSD\" ] || [ \"$uname\" = \"Darwin\" ]; then\n       sed -i \"\" \"$sed_cmd\" $filename\n    else\n       sed -i \"$sed_cmd\" $filename\n    fi\n}\n\nsplit_to_array() {\n  if ! [ -z $2 ]; then\n    IFS=',' read -ra $2 <<< \"$1\"\n  fi\n}\n\ncreate_path_not_exist() {\n  local target_path=$1\n  if ! [ -d $target_path ]; then\n    if ! mkdir -p $target_path; then\n      echo \"ERROR: Create path failed, $target_path!\"\n      exit 1\n    else\n      echo \"INFO: Created path successfully, $target_path.\"\n    fi\n  fi\n}\n#---Tool functions end---#\n\n#---Settings and cluster info section begin---#\n# Load cluster settings from file fcfs_conf.settings\nload_fcfs_settings() {\n  if ! [ -f $fcfs_settings_file ]; then\n    echo \"ERROR: File $fcfs_settings_file does not exist\"\n    exit 1\n  else\n    fcfs_settings=`sed -e 's/#.*//' -e '/^$/ d' $fcfs_settings_file`\n    eval $fcfs_settings\n    split_to_array $auth_ips auth_ip_array\n    split_to_array $fdir_ips fdir_ip_array\n    split_to_array $vote_ips vote_ip_array\n    split_to_array $fstore_groups fstore_group_array\n  fi\n}\n\n# Check config file templates in local cache path.\ncheck_conf_template() {\n  conf_file_version=$1\n  if [ -z $conf_file_version ]; then\n    echo \"ERROR: Config file version cannot be empty.\"\n    exit 1\n  fi\n  local conf_tpl_dir=\"conf.$conf_file_version.tpl\"\n  config_file_template_path=\"$fcfs_cache_path/$conf_tpl_dir\"\n  if ! [ -d $config_file_template_path ]; then\n    # Template path not exist in local cache path,\n    # will create it and download templates from remote server match the version\n    echo \"WARN: Template path $config_file_template_path does not exist, getting it from remote server $fcfs_tpl_file_server.\"\n    create_path_not_exist $fcfs_cache_path\n    cd $fcfs_cache_path\n    local template_tar_file=\"$conf_tpl_dir.tar.gz\"\n    remote_template_url=\"$fcfs_tpl_file_server/$template_tar_file\"\n    download_res=`curl -f -o $template_tar_file $remote_template_url`\n\n    if ! [ -f $template_tar_file ]; then\n      echo \"ERROR: Cannot download file $template_tar_file from $remote_template_url.\"\n      echo \"       Please make sure that remote template file exists and network is accessible.\"\n      exit 1\n    fi\n    # 解压模版文件\n    tar -xzvf $template_tar_file\n    if ! [ -d $conf_tpl_dir ]; then\n      echo \"ERROR: dir $conf_tpl_dir still not exist after unzip file $template_tar_file.\"\n      exit 1\n    fi\n    cd -\n  fi\n}\n\n# Get module name from command args, if not specify, will handle all modules.\nfvote_need_execute=0\nfdir_need_execute=0\nfstore_need_execute=0\nfauth_need_execute=0\nfuseclient_need_execute=0\nhas_module_param=1\n\ncheck_module_param() {\n  if [ $# -gt 1 ]; then\n    module_name=$2\n    case \"$module_name\" in\n      'fvote')\n        fvote_need_execute=1\n      ;;\n      'fdir')\n        fdir_need_execute=1\n      ;;\n      'fstore')\n        fstore_need_execute=1\n      ;;\n      'fauth')\n        fauth_need_execute=1\n      ;;\n      'fuseclient')\n        fuseclient_need_execute=1\n      ;;\n      *)\n        has_module_param=0\n        if ! [[ $module_name =~ ^- ]]; then\n          echo \"ERROR: Module name invalid, $module_name.\"\n          echo \"       Allowed module names: fdir, fstore, fauth, fvote, fuseclient.\"\n          exit 1\n        else\n          fdir_need_execute=1\n          fstore_need_execute=1\n          fauth_need_execute=1\n          fvote_need_execute=1\n          fuseclient_need_execute=1\n        fi\n      ;;\n    esac\n  else\n    has_module_param=0\n    fdir_need_execute=1\n    fstore_need_execute=1\n    fauth_need_execute=1\n    fvote_need_execute=1\n    fuseclient_need_execute=1\n  fi\n}\n#---Settings and cluster info section end---#\ncheck_module_param $*\nload_fcfs_settings\nif [ -z $fastcfs_version ]; then\n  echo \"ERROR: Param fastcfs_version in $fcfs_settings_file cannot be empty.\"\n  exit 1\nfi\n\n# Check local template file, if not exist, download it from server.\ncheck_conf_template $fastcfs_version\ncopy_config_from_template() {\n  local config_file_array=$1\n  local src_path=$2\n  local dest_path=$3\n\n  for CONF_FILE in ${config_file_array[@]}; do\n    local tmp_src_file=$src_path/$CONF_FILE\n    if [ -f $tmp_src_file ]; then\n      echo \"INFO: Copy template file $CONF_FILE to $dest_path.\"\n      cp $tmp_src_file $dest_path\n    else\n      echo \"WARN: File $tmp_src_file does not exist.\"\n    fi\n  done\n}\n\ncreate_fdir_conf_files() {\n  if [ ${#fdir_ip_array[@]} -gt 0 ]; then\n    echo \"INFO: Begin create fdir config files.\"\n    local fdir_path=\"$LOCAL_CONF_PATH/fdir\"\n    local fdir_tpl_path=\"$config_file_template_path/fdir\"\n    create_path_not_exist $fdir_path\n    copy_config_from_template \"${FDIR_CONF_FILES[*]}\" $fdir_tpl_path $fdir_path\n    local fdir_cluster_file=\"$fdir_path/cluster.conf\"\n    if ! [ -f $fdir_cluster_file ]; then\n      echo \"ERROR: fdir cluster file not exist, $fdir_cluster_file, can't append server to it\"\n      exit 1\n    fi\n    \n    # Trancate cluster.conf with mark line\n    sed_replace \"/$TRUNCATE_MARK_LINE/,$ d\" $fdir_cluster_file\n\n    # Replace server ip\n    let server_id=1\n    for fdir_server_ip in ${fdir_ip_array[@]}; do\n      echo \"[server-$server_id]\" >> $fdir_cluster_file\n      echo \"host = $fdir_server_ip\" >> $fdir_cluster_file\n      echo \"\" >> $fdir_cluster_file\n      let server_id=$server_id+1\n    done\n  else\n    echo \"ERROR: Param fdir_ips in $fcfs_settings_file cannot be empty.\"\n    exit 1\n  fi\n}\n\ncreate_fstore_conf_files() {\n  if [ -z $data_group_count ]; then\n    echo \"ERROR: Param data_group_count in $fcfs_settings_file must not empty.\"\n    exit 1\n  fi\n  if [ -z $fstore_group_count ]; then\n    echo \"ERROR: Param fstore_group_count in $fcfs_settings_file must not empty.\"\n    exit 1\n  fi\n  let data_group_count_int=$data_group_count\n  let fstore_group_count_int=$fstore_group_count\n\n  if [ $fstore_group_count_int -gt 0 ]; then\n    # Check params setting valid.\n    if [ $data_group_count_int -lt $fstore_group_count_int ]; then\n      echo \"ERROR: Param data_group_count in $fcfs_settings_file must be great than fstore_group_count.\"\n      exit 1\n    fi\n    for (( i=1 ; i <= $fstore_group_count_int ; i++ )); do\n      local fstore_group=\"fstore_group_$i\"\n      if [ -z ${!fstore_group} ]; then\n        echo \"ERROR: Param $fstore_group in $fcfs_settings_file cannot be empty.\"\n        exit 1\n      fi\n    done\n    \n    echo \"INFO: Begin create fstore config files.\"\n    local fstore_path=\"$LOCAL_CONF_PATH/fstore\"\n    local fstore_tpl_path=\"$config_file_template_path/fstore\"\n    create_path_not_exist $fstore_path\n    copy_config_from_template \"${STORE_CONF_FILES[*]}\" $fstore_tpl_path $fstore_path\n    local fstore_cluster_file=\"$fstore_path/cluster.conf\"\n    if ! [ -f $fstore_cluster_file ]; then\n      echo \"ERROR: fstore cluster file not exist, $fstore_cluster_file, can't append server to it\"\n      exit 1\n    fi\n\n    # Trancate cluster.conf with mark line\n    sed_replace \"/$TRUNCATE_MARK_LINE/,$ d\" $fstore_cluster_file\n\n    # Replace server_group_count and data_group_count, and append server ips\n    let data_groups_per_fstore_group=$data_group_count_int/$fstore_group_count_int\n    let remainder_count=$data_group_count_int-$data_groups_per_fstore_group*$fstore_group_count_int\n    let data_group_ids_start=1\n    let data_group_ids_end=$data_groups_per_fstore_group\n\n    sed_replace \"s#^server_group_count *=.*#server_group_count = $fstore_group_count_int#g\" $fstore_cluster_file\n    sed_replace \"s#^data_group_count *=.*#data_group_count = $data_group_count_int#g\" $fstore_cluster_file\n\n    let server_id_start=1\n    let store_server_id=1\n    # for fstore_group in ${fstore_group_array[@]}; do\n    for (( i=1 ; i <= $fstore_group_count_int ; i++ )); do\n      local fstore_group=\"fstore_group_$i\"\n      split_to_array ${!fstore_group} fstore_ips_array\n      let server_ids=${#fstore_ips_array[@]}\n      if [ $server_ids -gt 0 ]; then\n        # Append server group and server ids\n        echo \"[server-group-$i]\" >> $fstore_cluster_file\n        if [ $server_ids -eq 1 ]; then\n          echo \"server_ids = $server_id_start\" >> $fstore_cluster_file\n        else\n          let server_id_end=$server_id_start+$server_ids-1\n          echo \"server_ids = [$server_id_start, $server_id_end]\" >> $fstore_cluster_file\n        fi\n        let server_id_start=$server_id_start+$server_ids\n\n        let next_start_add_one=0\n        # Append data group ids\n        if [ $remainder_count -gt 0 ]; then\n          let data_group_ids_end=$data_group_ids_end+1\n          let remainder_count=$remainder_count-1\n          let next_start_add_one=1\n        fi\n        echo \"data_group_ids = [$data_group_ids_start, $data_group_ids_end]\" >> $fstore_cluster_file\n        echo \"\" >> $fstore_cluster_file\n\n        let data_group_ids_start=$data_group_ids_end+1\n        let data_group_ids_end=$data_group_ids_end+$data_groups_per_fstore_group\n\n        # Append server hosts\n        for fstore_server_ip in ${fstore_ips_array[@]}; do\n          echo \"[server-$store_server_id]\" >> $fstore_cluster_file\n          echo \"host = $fstore_server_ip\" >> $fstore_cluster_file\n          echo \"\" >> $fstore_cluster_file\n          let store_server_id=$store_server_id+1\n        done\n      else\n        echo \"ERROR: Param $fstore_group in $fcfs_settings_file cannot be empty.\"\n        exit 1 \n      fi\n    done\n  else\n    echo \"ERROR: Param fstore_group_count in $fcfs_settings_file must be great than 0.\"\n    exit 1\n  fi\n}\n\ncreate_fauth_conf_files() {\n  if [ ${#auth_ip_array[@]} -gt 0 ]; then\n    echo \"INFO: Begin create fauth config files.\"\n    local auth_path=\"$LOCAL_CONF_PATH/auth\"\n    local auth_tpl_path=\"$config_file_template_path/auth\"\n    create_path_not_exist $auth_path\n    copy_config_from_template \"${AUTH_CONF_FILES[*]}\" $auth_tpl_path $auth_path\n    local auth_cluster_file=\"$auth_path/cluster.conf\"\n    if ! [ -f $auth_cluster_file ]; then\n      echo \"ERROR: auth cluster file not exist, $auth_cluster_file, can't append server to it\"\n      exit 1\n    fi\n\n    # Trancate cluster.conf with mark line\n    sed_replace \"/$TRUNCATE_MARK_LINE/,$ d\" $auth_cluster_file\n\n    # Replace server ip\n    let server_id=1\n    for auth_server_ip in ${auth_ip_array[@]}; do\n      echo \"[server-$server_id]\" >> $auth_cluster_file\n      echo \"host = $auth_server_ip\" >> $auth_cluster_file\n      echo \"\" >> $auth_cluster_file\n      let server_id=$server_id+1\n    done\n    # Copy auth keys\n    local auth_keys_path=\"$LOCAL_CONF_PATH/auth/keys\"\n    local auth_keys_tpl_path=\"$config_file_template_path/auth/keys\"\n    create_path_not_exist $auth_keys_path\n    copy_config_from_template \"${AUTH_KEYS_FILES[*]}\" $auth_keys_tpl_path $auth_keys_path\n  else\n    echo \"ERROR: Param auth_ips in $fcfs_settings_file cannot be empty.\"\n    exit 1\n  fi\n}\n\ncreate_fvote_conf_files() {\n  if [ ${#vote_ip_array[@]} -gt 0 ]; then\n    echo \"INFO: Begin create vote config files.\"\n    local vote_path=\"$LOCAL_CONF_PATH/vote\"\n    local vote_tpl_path=\"$config_file_template_path/vote\"\n    create_path_not_exist $vote_path\n    copy_config_from_template \"${VOTE_CONF_FILES[*]}\" $vote_tpl_path $vote_path\n    local vote_cluster_file=\"$vote_path/cluster.conf\"\n    if ! [ -f $vote_cluster_file ]; then\n      echo \"ERROR: vote cluster file not exist, $vote_cluster_file, can't append server to it\"\n      exit 1\n    fi\n    \n    # Trancate cluster.conf with mark line\n    sed_replace \"/$TRUNCATE_MARK_LINE/,$ d\" $vote_cluster_file\n\n    # Replace server ip\n    let server_id=1\n    for vote_server_ip in ${vote_ip_array[@]}; do\n      echo \"[server-$server_id]\" >> $vote_cluster_file\n      echo \"host = $vote_server_ip\" >> $vote_cluster_file\n      echo \"\" >> $vote_cluster_file\n      let server_id=$server_id+1\n    done\n  else\n    echo \"ERROR: Param vote_ips in $fcfs_settings_file cannot be empty.\"\n    exit 1\n  fi\n}\n\ncreate_fuseclient_conf_files() {\n  echo \"INFO: Begin create fuseclient config files.\"\n  local fuseclient_path=\"$LOCAL_CONF_PATH/fcfs\"\n  local fuseclient_tpl_path=\"$config_file_template_path/fcfs\"\n  create_path_not_exist $fuseclient_path\n  copy_config_from_template \"${FUSE_CONF_FILES[*]}\" $fuseclient_tpl_path $fuseclient_path\n}\n\ncreate_config_files() {\n  if [ -d $LOCAL_CONF_PATH/fdir -a -d $LOCAL_CONF_PATH/fstore ]; then\n      printf 'config files already exist, make sure to overwrite [y/N]: '\n      read var\n      if ! [ \"$var\" = \"y\" -o \"$var\" = \"Y\" -o \"$var\" = \"yes\" -o \"$var\" = \"YES\" ]; then\n        echo -e '\\ncreating config files aborted!\\n'\n        exit 1\n      fi\n  fi\n\n  create_path_not_exist $LOCAL_CONF_PATH\n\n  if [ $fdir_need_execute -eq 1 ]; then\n    create_fdir_conf_files\n  fi\n  if [ $fstore_need_execute -eq 1 ]; then\n    create_fstore_conf_files\n  fi\n  if [ $fauth_need_execute -eq 1 ]; then\n    create_fauth_conf_files\n  fi\n  if [ $fvote_need_execute -eq 1 ]; then\n    create_fvote_conf_files\n  fi\n  if [ $fuseclient_need_execute -eq 1 ]; then\n    create_fuseclient_conf_files\n  fi\n}\n\ncase \"$shell_command\" in\n  'create')\n    create_config_files\n  ;;\n  *)\n    print_usage\n    exit 1\n  ;;\nesac\n\nexit 0\n"
  },
  {
    "path": "shell/template/dependency.2.0.1.settings",
    "content": "# FastCFS cluster's dependent libs version settings.\n# Ops shell fcfs.sh will use it to install appropriate version libs FastCFS depends.\n# This file download from http://www.fastcfs.com/ops/dependency.[FastCFS-version].settings.\n# Do not change libs and versions below.\n\nlibfastcommon=1.0.50\nlibserverframe=1.1.7\nfauth=2.0.1\nfdir=2.0.1\nfstore=2.0.1"
  },
  {
    "path": "src/api/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I../common -I../include\nLIB_PATH = $(LIBS) -lfsclient -lfsapi -lfdirclient -lfastcommon -lserverframe -lfcfsauthclient\nTARGET_LIB = $(TARGET_PREFIX)/$(LIB_VERSION)\n\nFAST_SHARED_OBJS = ../common/fcfs_global.lo fcfs_api.lo fcfs_api_file.lo    \\\n                   fcfs_api_util.lo fcfs_api_allocator.lo async_reporter.lo \\\n                   inode_htable.lo std/posix_api.lo std/fd_manager.lo  \\\n\t\t\t\t   std/papi.lo std/capi.lo\n\nFAST_STATIC_OBJS = ../common/fcfs_global.o fcfs_api.o fcfs_api_file.o    \\\n                   fcfs_api_util.o fcfs_api_allocator.o async_reporter.o \\\n                   inode_htable.o std/posix_api.o std/fd_manager.o  \\\n\t\t\t\t   std/papi.o std/capi.o\n\nAPI_HEADER_FILES = ../common/fcfs_global.h fcfs_api.h fcfs_api_types.h  \\\n               fcfs_api_file.h fcfs_api_util.h fcfs_api_allocator.h \\\n               async_reporter.h inode_htable.h\n\nSTD_HEADER_FILES = std/posix_api.h std/api_types.h std/fd_manager.h \\\n\t\t\t\t   std/papi.h std/capi.h\n\nALL_OBJS = $(FAST_STATIC_OBJS) $(FAST_SHARED_OBJS)\n\nALL_PRGS = \nSHARED_LIBS = libfcfsapi.so\nSTATIC_LIBS = libfcfsapi.a\nALL_LIBS = $(SHARED_LIBS) $(STATIC_LIBS)\n\nall: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)\nlibfcfsapi.so: $(FAST_SHARED_OBJS)\n\t$(COMPILE) -o $@ -shared $(FAST_SHARED_OBJS) $(LIB_PATH)\nlibfcfsapi.a: $(FAST_STATIC_OBJS)\n\tar rcs $@ $(FAST_STATIC_OBJS)\n.o:\n\t$(COMPILE) -o $@ $<  $(FAST_STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c:\n\t$(COMPILE) -o $@ $<  $(FAST_STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n.c.lo:\n\t$(COMPILE) -c -fPIC -o $@ $<  $(INC_PATH)\ninstall:\n\tmkdir -p $(TARGET_LIB)\n\tmkdir -p $(TARGET_PREFIX)/lib\n\tmkdir -p $(TARGET_PREFIX)/include/fastcfs/api/std\n\n\tinstall -m 755 $(SHARED_LIBS) $(TARGET_LIB)\n\tinstall -m 644 $(API_HEADER_FILES) $(TARGET_PREFIX)/include/fastcfs/api/\n\tinstall -m 644 $(STD_HEADER_FILES) $(TARGET_PREFIX)/include/fastcfs/api/std/\n\t@BUILDROOT=$$(echo \"$(TARGET_PREFIX)\" | grep BUILDROOT); \\\n\tif [ -z \"$$BUILDROOT\" ] && [ \"$(TARGET_LIB)\" != \"$(TARGET_PREFIX)/lib\" ]; then ln -sf $(TARGET_LIB)/libfcfsapi.so $(TARGET_PREFIX)/lib/libfcfsapi.so; fi\nclean:\n\trm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)\n\n"
  },
  {
    "path": "src/api/async_reporter.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdlib.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"sf/sf_global.h\"\n#include \"sf/sf_func.h\"\n#include \"async_reporter.h\"\n\nAsyncReporterContext g_async_reporter_ctx;\n\n#define FCFS_API_CTX  g_async_reporter_ctx.fcfs_api_ctx\n#define SORTED_EVENT_PTR_ARRAY g_async_reporter_ctx.event_ptr_arrays.sorted\n#define MERGED_EVENT_PTR_ARRAY g_async_reporter_ctx.event_ptr_arrays.merged\n#define NOTIFY_EVENT_COUNT g_async_reporter_ctx.event_ptr_arrays.notify_count\n\nstatic inline int batch_set_dentry_size(const int count)\n{\n    int result;\n\n    while (1) {\n        result = fdir_client_batch_set_dentry_size(FCFS_API_CTX->contexts.\n                fdir, &FCFS_API_CTX->ns, g_async_reporter_ctx.dsizes, count);\n        if (result == 0 || result == ENOENT || result == EINVAL) {\n            break;\n        }\n        sleep(1);\n    }\n\n    return result;\n}\n\nstatic int int_event_ptr_array(FCFSAPIAsyncReportEventPtrArray *event_ptr_array)\n{\n    event_ptr_array->alloc = 1024;\n    event_ptr_array->events = (FCFSAPIAsyncReportEvent **)\n        fc_malloc(sizeof(FCFSAPIAsyncReportEvent *) * event_ptr_array->alloc);\n    if (event_ptr_array->events == NULL) {\n        return ENOMEM;\n    }\n    return 0;\n}\n\nstatic FCFSAPIAsyncReportEvent **realloc_event_ptrs(\n        FCFSAPIAsyncReportEventPtrArray *event_ptr_array, const int count)\n{\n    int alloc;\n    FCFSAPIAsyncReportEvent **events;\n\n    alloc = event_ptr_array->alloc * 2;\n    events = (FCFSAPIAsyncReportEvent **)fc_malloc(\n            sizeof(FCFSAPIAsyncReportEvent *) * alloc);\n    if (events == NULL) {\n        return NULL;\n    }\n\n    if (count > 0) {\n        memcpy(events, event_ptr_array->events,\n                sizeof(FCFSAPIAsyncReportEvent *) * count);\n    }\n    free(event_ptr_array->events);\n\n    event_ptr_array->alloc = alloc;\n    event_ptr_array->events = events;\n    return events + count;\n}\n\nstatic int to_event_ptr_array(FCFSAPIAsyncReportEvent *head,\n        FCFSAPIAsyncReportEventPtrArray *event_ptr_array)\n{\n    FCFSAPIAsyncReportEvent **event;\n    int result;\n    int event_count;\n\n    result = 0;\n    event_count = 0;\n    event = event_ptr_array->events;\n    do {\n        if (event_count >= event_ptr_array->alloc) {\n            if ((event=realloc_event_ptrs(event_ptr_array,\n                            event_count)) == NULL)\n            {\n                result = ENOMEM;\n                break;\n            }\n        }\n\n        head->id = ++event_count;\n        *event++ = head;\n        head = head->next;\n    } while (head != NULL);\n\n    event_ptr_array->count = event_count;\n    return result;\n}\n\nstatic int compare_event_ptr(const FCFSAPIAsyncReportEvent **t1,\n        const FCFSAPIAsyncReportEvent **t2)\n{\n    int sub;\n    if ((sub=fc_compare_int64((*t1)->dsize.inode, (*t2)->dsize.inode)) != 0) {\n        return sub;\n    }\n\n    return (int)(*t1)->id - (int)(*t2)->id;\n}\n\nstatic inline void merge_event(FCFSAPIAsyncReportEvent *dest,\n        const FCFSAPIAsyncReportEvent *src)\n{\n    if ((src->dsize.flags & (FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE |\n                FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END)) != 0)\n    {\n        if ((dest->dsize.flags & (FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE |\n                        FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END)) == 0)\n        {\n            dest->dsize.file_size = src->dsize.file_size;\n        } else if (dest->dsize.file_size < src->dsize.file_size) {\n            dest->dsize.file_size = src->dsize.file_size;\n        }\n    }\n\n    if ((src->dsize.flags & FDIR_DENTRY_FIELD_MODIFIED_FLAG_INC_ALLOC) != 0) {\n        dest->dsize.inc_alloc += src->dsize.inc_alloc;\n    }\n\n    dest->dsize.flags |= src->dsize.flags;\n}\n\nstatic int merge_events(FCFSAPIAsyncReportEvent *head)\n{\n    int result;\n    //int merge_count;\n    FCFSAPIAsyncReportEvent **ts;\n    FCFSAPIAsyncReportEvent **send;\n    FCFSAPIAsyncReportEvent **merge;\n    FCFSAPIAsyncReportEvent *current;\n\n    if ((result=to_event_ptr_array(head, &SORTED_EVENT_PTR_ARRAY)) != 0) {\n        return result;\n    }\n\n    if (SORTED_EVENT_PTR_ARRAY.count > 1) {\n        qsort(SORTED_EVENT_PTR_ARRAY.events, SORTED_EVENT_PTR_ARRAY.count,\n                sizeof(FCFSAPIAsyncReportEvent *), (int (*)(const void *,\n                        const void *))compare_event_ptr);\n    }\n\n    NOTIFY_EVENT_COUNT = 0;\n    send = SORTED_EVENT_PTR_ARRAY.events + SORTED_EVENT_PTR_ARRAY.count;\n    ts = SORTED_EVENT_PTR_ARRAY.events;\n    merge = MERGED_EVENT_PTR_ARRAY.events;\n    while (ts < send) {\n        if ((*ts)->type == fcfs_api_event_type_notify) {\n            NOTIFY_EVENT_COUNT++;\n            ts++;\n            continue;\n        }\n\n        if (merge - MERGED_EVENT_PTR_ARRAY.events >=\n                MERGED_EVENT_PTR_ARRAY.alloc)\n        {\n            if ((merge=realloc_event_ptrs(&MERGED_EVENT_PTR_ARRAY,\n                            merge - MERGED_EVENT_PTR_ARRAY.events)) == NULL)\n            {\n                result = ENOMEM;\n                break;\n            }\n        }\n\n        //merge_count = 1;\n        current = *ts;\n        *merge++ = *ts++;\n        if (current->dsize.force) {\n            continue;\n        }\n        if (ts == send) {\n            break;\n        }\n\n        while ((ts < send) && (!(*ts)->dsize.force) &&\n                ((*ts)->dsize.inode == current->dsize.inode))\n        {\n            if ((*ts)->type == fcfs_api_event_type_notify) {\n                NOTIFY_EVENT_COUNT++;\n            } else {\n                //merge_count++;\n                merge_event(current, *ts);\n            }\n            ts++;\n        }\n\n        /*\n        logInfo(\"event oid: %\"PRId64\", merged count: %d\",\n                current->dsize.inode, merge_count);\n         */\n    }\n\n    MERGED_EVENT_PTR_ARRAY.count = merge - MERGED_EVENT_PTR_ARRAY.events;\n    return 0;\n}\n\nstatic inline void notify_waiting_tasks(FCFSAPIAsyncReportEvent *event)\n{\n    FCFSAPIWaitingTask *task;\n\n    if (event->type == fcfs_api_event_type_report) {\n        PTHREAD_MUTEX_LOCK(&event->inode_hentry->hentry.sharding->lock);\n    }\n    while (event->waitings.head != NULL) {\n        task = event->waitings.head;\n        event->waitings.head = event->waitings.head->next;\n\n        fcfs_api_notify_waiting_task(task);\n    }\n    if (event->type == fcfs_api_event_type_report) {\n        fc_list_del_init(&event->dlink);\n        PTHREAD_MUTEX_UNLOCK(&event->inode_hentry->hentry.sharding->lock);\n    }\n}\n\nstatic inline void notify_waiting_tasks_and_free_events(\n        FCFSAPIAsyncReportEvent *head)\n{\n    FCFSAPIAsyncReportEvent *event;\n    do {\n        event = head;\n        head = head->next;\n\n        notify_waiting_tasks(event);\n        fast_mblock_free_object(event->allocator, event);\n    } while (head != NULL);\n}\n\nstatic inline int deal_events(FCFSAPIAsyncReportEvent *head)\n{\n    FCFSAPIAsyncReportEvent **event;\n    FCFSAPIAsyncReportEvent **tend;\n    FDIRSetDEntrySizeInfo *dsize;\n    int result;\n    int current_count;\n\n    if ((result=merge_events(head)) != 0) {\n        notify_waiting_tasks_and_free_events(head);\n        return result;\n    }\n\n    dsize = g_async_reporter_ctx.dsizes;\n    tend = MERGED_EVENT_PTR_ARRAY.events + MERGED_EVENT_PTR_ARRAY.count;\n    for (event=MERGED_EVENT_PTR_ARRAY.events; event<tend; event++) {\n        *dsize++ = (*event)->dsize;\n        if ((current_count=dsize - g_async_reporter_ctx.dsizes) ==\n                FDIR_BATCH_SET_MAX_DENTRY_COUNT)\n        {\n            batch_set_dentry_size(current_count);\n            dsize = g_async_reporter_ctx.dsizes;\n        }\n    }\n\n    if ((current_count=dsize - g_async_reporter_ctx.dsizes) > 0) {\n        batch_set_dentry_size(current_count);\n    }\n\n    __sync_fetch_and_sub(&g_async_reporter_ctx.waiting_count,\n            SORTED_EVENT_PTR_ARRAY.count - NOTIFY_EVENT_COUNT);\n    notify_waiting_tasks_and_free_events(head);\n\n    /*\n    logInfo(\"total (input) event count: %d, report (output) count: %d, \"\n            \"notify event count: %d\", SORTED_EVENT_PTR_ARRAY.count,\n            MERGED_EVENT_PTR_ARRAY.count, NOTIFY_EVENT_COUNT);\n            */\n    return 0;\n}\n\nvoid async_reporter_terminate()\n{\n    FCFSAPIAsyncReportEvent *head;\n    int count;\n\n    if (!g_async_reporter_ctx.fcfs_api_ctx->async_report.enabled) {\n        return;\n    }\n\n    head = (FCFSAPIAsyncReportEvent *)fc_queue_try_pop_all(\n            &g_async_reporter_ctx.queue);\n    if (head != NULL) {\n        count = deal_events(head);\n    } else {\n        count = 0;\n    }\n\n    logInfo(\"file: \"__FILE__\", line: %d, \"\n            \"async_reporter_terminate, remain count: %d\",\n            __LINE__, count);\n}\n\nstatic inline void check_and_set_stage()\n{\n    int old_stage;\n\n    if (__sync_bool_compare_and_swap(&g_async_reporter_ctx.stage,\n                ASYNC_REPORTER_STAGE_DEALING, ASYNC_REPORTER_STAGE_SLEEPING))\n    {\n        fc_timedwait_ms(&g_async_reporter_ctx.lcp.lock,\n                &g_async_reporter_ctx.lcp.cond, g_async_reporter_ctx.\n                fcfs_api_ctx->async_report.interval_ms);\n        if (__sync_bool_compare_and_swap(\n                    &g_async_reporter_ctx.stage,\n                    ASYNC_REPORTER_STAGE_SLEEPING,\n                    ASYNC_REPORTER_STAGE_DEALING))\n        {\n            return;\n        }\n    }\n\n    old_stage = __sync_fetch_and_add(&g_async_reporter_ctx.stage, 0);\n    __sync_bool_compare_and_swap(&g_async_reporter_ctx.stage,\n            old_stage, ASYNC_REPORTER_STAGE_DEALING);\n}\n\nstatic void *async_reporter_thread_func(void *arg)\n{\n    FCFSAPIAsyncReportEvent *head;\n\n#ifdef OS_LINUX\n    prctl(PR_SET_NAME, \"dir-async-reporter\");\n#endif\n\n    while (SF_G_CONTINUE_FLAG) {\n        head = (FCFSAPIAsyncReportEvent *)fc_queue_pop_all(\n                &g_async_reporter_ctx.queue);\n        if (head != NULL) {\n            deal_events(head);\n        }\n\n        if (g_async_reporter_ctx.fcfs_api_ctx->async_report.interval_ms > 0) {\n            check_and_set_stage();\n        }\n    }\n\n    return NULL;\n}\n\nint async_reporter_init(FCFSAPIContext *fcfs_api_ctx)\n{\n    int result;\n    pthread_t tid;\n\n    g_async_reporter_ctx.fcfs_api_ctx = fcfs_api_ctx;\n    g_async_reporter_ctx.dsizes = (FDIRSetDEntrySizeInfo *)\n        fc_malloc(sizeof(FDIRSetDEntrySizeInfo) *\n                FDIR_BATCH_SET_MAX_DENTRY_COUNT);\n    if (g_async_reporter_ctx.dsizes == NULL) {\n        return ENOMEM;\n    }\n\n    if ((result=int_event_ptr_array(&SORTED_EVENT_PTR_ARRAY)) != 0) {\n        return result;\n    }\n    if ((result=int_event_ptr_array(&MERGED_EVENT_PTR_ARRAY)) != 0) {\n        return result;\n    }\n\n    if ((result=init_pthread_lock_cond_pair(&g_async_reporter_ctx.lcp)) != 0) {\n        return result;\n    }\n\n    if ((result=fc_queue_init(&g_async_reporter_ctx.queue, (long)\n                    (&((FCFSAPIAsyncReportEvent *)NULL)->next))) != 0)\n    {\n        return result;\n    }\n\n    g_async_reporter_ctx.stage = ASYNC_REPORTER_STAGE_DEALING;\n    return fc_create_thread(&tid, async_reporter_thread_func, NULL,\n            SF_G_THREAD_STACK_SIZE);\n}\n"
  },
  {
    "path": "src/api/async_reporter.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_API_ASYNC_REPORTER_H\n#define _FCFS_API_ASYNC_REPORTER_H\n\n#include \"fastcommon/fc_queue.h\"\n#include \"fastcommon/fc_atomic.h\"\n#include \"fcfs_api_types.h\"\n#include \"fcfs_api_allocator.h\"\n#include \"inode_htable.h\"\n\n#define ASYNC_REPORTER_STAGE_DEALING   0\n#define ASYNC_REPORTER_STAGE_SLEEPING  1\n#define ASYNC_REPORTER_STAGE_KEEPING   2\n\ntypedef struct {\n    FCFSAPIContext *fcfs_api_ctx;\n    struct fc_queue queue;\n    volatile int waiting_count;\n    volatile int stage;\n    pthread_lock_cond_pair_t lcp;  //for timed wait\n    FDIRSetDEntrySizeInfo *dsizes;\n    struct {\n        int notify_count;   //notify event count\n        FCFSAPIAsyncReportEventPtrArray sorted;  //for sort\n        FCFSAPIAsyncReportEventPtrArray merged;\n    } event_ptr_arrays;\n} AsyncReporterContext;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern AsyncReporterContext g_async_reporter_ctx;\n\n    int async_reporter_init(FCFSAPIContext *fcfs_api_ctx);\n\n    void async_reporter_terminate();\n\n    static inline int async_reporter_push(const FDIRSetDEntrySizeInfo *dsize)\n    {\n        int result;\n        FCFSAPIAsyncReportEvent *event;\n        FCFSAPIAllocatorContext *allocator_ctx;\n\n        allocator_ctx = fcfs_api_allocator_get(dsize->inode);\n        event = (FCFSAPIAsyncReportEvent *)fast_mblock_alloc_object(\n                &allocator_ctx->async_report_event);\n        if (event == NULL) {\n            return ENOMEM;\n        }\n\n        event->type = fcfs_api_event_type_report;\n        event->dsize = *dsize;\n        result = inode_htable_insert(event);\n        __sync_fetch_and_add(&g_async_reporter_ctx.waiting_count, 1);\n        fc_queue_push(&g_async_reporter_ctx.queue, event);\n        return result;\n    }\n\n    static inline int async_reporter_push_notify(const uint64_t inode,\n            FCFSAPIWaitingTask **waiting_task)\n    {\n        FCFSAPIAllocatorContext *allocator_ctx;\n        FCFSAPIAsyncReportEvent *event;\n\n        allocator_ctx = fcfs_api_allocator_get(inode);\n        event = (FCFSAPIAsyncReportEvent *)fast_mblock_alloc_object(\n                &allocator_ctx->async_report_event);\n        if (event == NULL) {\n            return ENOMEM;\n        }\n\n        *waiting_task = (FCFSAPIWaitingTask *)fast_mblock_alloc_object(\n                &allocator_ctx->waiting_task);\n        if (*waiting_task == NULL) {\n            return ENOMEM;\n        }\n\n        (*waiting_task)->next = event->waitings.head;\n        event->waitings.head = *waiting_task;\n        event->type = fcfs_api_event_type_notify;\n        event->dsize.inode = inode;\n        event->dsize.flags = 0;\n        event->dsize.force = false;\n        fc_queue_push(&g_async_reporter_ctx.queue, event);\n        return 0;\n    }\n\n    static inline void async_reporter_notify()\n    {\n        if (g_async_reporter_ctx.fcfs_api_ctx->async_report.interval_ms <= 0) {\n            return;\n        }\n\n        switch (__sync_fetch_and_add(&g_async_reporter_ctx.stage, 0)) {\n            case ASYNC_REPORTER_STAGE_DEALING:\n                __sync_bool_compare_and_swap(\n                        &g_async_reporter_ctx.stage,\n                        ASYNC_REPORTER_STAGE_DEALING,\n                        ASYNC_REPORTER_STAGE_KEEPING);\n                break;\n            case ASYNC_REPORTER_STAGE_SLEEPING:\n                pthread_cond_signal(&g_async_reporter_ctx.lcp.cond);\n                break;\n            case ASYNC_REPORTER_STAGE_KEEPING:\n                //do nothing\n                break;\n            default:\n                break;\n        }\n    }\n\n    static inline int async_reporter_wait_all(const uint64_t inode)\n    {\n        FCFSAPIWaitingTask *waiting_task;\n        int result;\n\n        if (FC_ATOMIC_GET(g_async_reporter_ctx.waiting_count) == 0) {\n            return 0;\n        }\n\n        if ((result=async_reporter_push_notify(inode, &waiting_task)) != 0) {\n            return result;\n        }\n        async_reporter_notify();\n\n        fcfs_api_wait_report_done_and_release(waiting_task);\n        return 0;\n    }\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/fcfs_api.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <sys/param.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <limits.h>\n#include <grp.h>\n#include <pwd.h>\n#include \"fastcommon/common_define.h\"\n\n#ifdef OS_LINUX\n#include <sys/vfs.h>\n#include <sys/statfs.h>\n#include <linux/magic.h>\n#endif\n\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"sf/idempotency/client/client_channel.h\"\n#include \"sf/idempotency/client/receipt_handler.h\"\n#include \"async_reporter.h\"\n#include \"fcfs_api.h\"\n\n#define FCFS_API_MIN_SHARED_ALLOCATOR_COUNT           1\n#define FCFS_API_MAX_SHARED_ALLOCATOR_COUNT        1000\n#define FCFS_API_DEFAULT_SHARED_ALLOCATOR_COUNT      11\n\n#define FCFS_API_MIN_HASHTABLE_SHARDING_COUNT           1\n#define FCFS_API_MAX_HASHTABLE_SHARDING_COUNT       10000\n#define FCFS_API_DEFAULT_HASHTABLE_SHARDING_COUNT      17\n\n#define FCFS_API_MIN_HASHTABLE_TOTAL_CAPACITY          10949\n#define FCFS_API_MAX_HASHTABLE_TOTAL_CAPACITY      100000000\n#define FCFS_API_DEFAULT_HASHTABLE_TOTAL_CAPACITY    1403641\n\n#define FCFS_API_INI_IDEMPOTENCY_SECTION_NAME      \"idempotency\"\n#define FCFS_API_IDEMPOTENCY_DEFAULT_WORK_THREADS  1\n\n#ifndef FUSE_SUPER_MAGIC\n#define FUSE_SUPER_MAGIC 0x65735546\n#endif\n\nFCFSAPIContext g_fcfs_api_ctx;\n\nstatic int fcfs_api_load_owner_config(IniFullContext *ini_ctx,\n        FCFSAPIContext *ctx);\n\nstatic int opendir_session_alloc_init(void *element, void *args)\n{\n    int result;\n    FCFSAPIOpendirSession *session;\n\n    session = (FCFSAPIOpendirSession *)element;\n    if ((result=fdir_client_dentry_array_init(&session->array)) != 0) {\n        return result;\n    }\n\n    if ((result=fast_buffer_init1(&session->buffer, 64 * 1024)) != 0) {\n        return result;\n    }\n    return 0;\n}\n\nint fcfs_api_client_session_create(FCFSAPIContext *ctx, const bool publish)\n{\n    FCFSAuthClientFullContext *auth;\n\n    if (ctx->contexts.fdir->auth.enabled) {\n        auth = &ctx->contexts.fdir->auth;\n    } else if (ctx->contexts.fsapi->fs->auth.enabled) {\n        auth = &ctx->contexts.fsapi->fs->auth;\n    } else {\n        return 0;\n    }\n\n    return fcfs_auth_client_session_create_ex(auth, &ctx->ns, publish);\n}\n\nstatic inline bool fcfs_api_rdma_enabled(FCFSAPIContext *ctx)\n{\n    FCServerGroupInfo *server_group;\n\n    server_group = fc_server_get_group_by_index(\n            &ctx->contexts.fdir->cluster.server_cfg,\n            ctx->contexts.fdir->cluster.service_group_index);\n    if (server_group->comm_type != fc_comm_type_sock) {\n        return true;\n    }\n\n    server_group = fc_server_get_group_by_index(\n            &FS_CLUSTER_SERVER_CFG(ctx->contexts.fsapi->fs),\n            FS_CFG_SERVICE_INDEX(ctx->contexts.fsapi->fs));\n    return (server_group->comm_type != fc_comm_type_sock);\n}\n\nstatic int fcfs_api_common_init(FCFSAPIContext *ctx, FDIRClientContext *fdir,\n        FSAPIContext *fsapi, const char *ns, IniFullContext *ini_ctx,\n        const char *fdir_section_name, const char *fs_section_name,\n        const bool need_lock, const bool persist_additional_gids)\n{\n    int64_t element_limit = 1000 * 1000;\n    const int64_t min_ttl_sec = 600;\n    const int64_t max_ttl_sec = 86400;\n    int result;\n\n    ctx->persist_additional_gids = persist_additional_gids;\n    ctx->use_sys_lock_for_append = iniGetBoolValue(fdir_section_name,\n            \"use_sys_lock_for_append\", ini_ctx->context, false);\n    ctx->async_report.enabled = iniGetBoolValue(fdir_section_name,\n            \"async_report_enabled\", ini_ctx->context, true);\n    ctx->async_report.interval_ms = iniGetIntValue(fdir_section_name,\n            \"async_report_interval_ms\", ini_ctx->context, 10);\n\n    ini_ctx->section_name = fdir_section_name;\n    ctx->async_report.shared_allocator_count = iniGetIntCorrectValueEx(\n            ini_ctx, \"shared_allocator_count\",\n            FCFS_API_DEFAULT_SHARED_ALLOCATOR_COUNT,\n            FCFS_API_MIN_SHARED_ALLOCATOR_COUNT,\n            FCFS_API_MAX_SHARED_ALLOCATOR_COUNT, true);\n\n    ctx->async_report.hashtable_sharding_count = iniGetIntCorrectValue(\n            ini_ctx, \"hashtable_sharding_count\",\n            FCFS_API_DEFAULT_HASHTABLE_SHARDING_COUNT,\n            FCFS_API_MIN_HASHTABLE_SHARDING_COUNT,\n            FCFS_API_MAX_HASHTABLE_SHARDING_COUNT);\n\n    ctx->async_report.hashtable_total_capacity = iniGetInt64CorrectValue(\n            ini_ctx, \"hashtable_total_capacity\",\n            FCFS_API_DEFAULT_HASHTABLE_TOTAL_CAPACITY,\n            FCFS_API_MIN_HASHTABLE_TOTAL_CAPACITY,\n            FCFS_API_MAX_HASHTABLE_TOTAL_CAPACITY);\n\n    if (ctx->async_report.enabled) {\n        if ((result=fcfs_api_allocator_init(ctx)) != 0) {\n            return result;\n        }\n\n        if ((result=inode_htable_init(ctx->async_report.\n                        hashtable_sharding_count,\n                        ctx->async_report.hashtable_total_capacity,\n                        ctx->async_report.shared_allocator_count,\n                        element_limit, min_ttl_sec, max_ttl_sec)) != 0)\n        {\n            return result;\n        }\n    }\n\n    ini_ctx->section_name = fs_section_name;\n    if ((result=fs_api_init_ex(fsapi, ini_ctx,\n                    fcfs_api_file_write_done_callback,\n                    sizeof(FCFSAPIWriteDoneCallbackExtraData))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fast_mblock_init_ex1(&ctx->opendir_session_pool,\n                    \"opendir_session\", sizeof(FCFSAPIOpendirSession), 64,\n                    0, opendir_session_alloc_init, NULL, need_lock)) != 0)\n    {\n        return result;\n    }\n\n    fcfs_api_set_contexts_ex1(ctx, fdir, fsapi, ns);\n\n    if ((ctx->rdma.enabled=fcfs_api_rdma_enabled(ctx))) {\n        ctx->rdma.busy_polling = iniGetBoolValue(NULL,\n                \"busy_polling\", ini_ctx->context, false);\n        G_RDMA_CONNECTION_CALLBACKS.set_busy_polling(ctx->rdma.busy_polling);\n    } else {\n        ctx->rdma.busy_polling = false;\n    }\n\n    ini_ctx->section_name = fdir_section_name;\n    return fcfs_api_load_owner_config(ini_ctx, ctx);\n}\n\nint fcfs_api_init_ex1(FCFSAPIContext *ctx, FDIRClientContext *fdir,\n        FSAPIContext *fsapi, const char *ns, IniFullContext *ini_ctx,\n        const char *fdir_section_name, const char *fs_section_name,\n        const SFConnectionManager *fdir_conn_manager,\n        const SFConnectionManager *fs_conn_manager,\n        const bool need_lock, const bool persist_additional_gids)\n{\n    const bool bg_thread_enabled = true;\n    int result;\n\n    ini_ctx->section_name = fdir_section_name;\n    if ((result=fdir_client_init_ex1(fdir, &g_fcfs_auth_client_vars.\n                    client_ctx, ini_ctx, fdir_conn_manager)) != 0)\n    {\n        return result;\n    }\n\n    ini_ctx->section_name = fs_section_name;\n    if ((result=fs_client_init_ex1(fsapi->fs, &g_fcfs_auth_client_vars.\n                    client_ctx, ini_ctx, fs_conn_manager,\n                    bg_thread_enabled)) != 0)\n    {\n        return result;\n    }\n\n    return fcfs_api_common_init(ctx, fdir, fsapi, ns, ini_ctx,\n            fdir_section_name, fs_section_name, need_lock,\n            persist_additional_gids);\n}\n\nint fcfs_api_init_ex(FCFSAPIContext *ctx, const char *ns,\n        const char *config_filename, const char *fdir_section_name,\n        const char *fs_section_name)\n{\n    int result;\n    IniContext iniContext;\n    IniFullContext ini_ctx;\n\n    if ((result=iniLoadFromFile(config_filename, &iniContext)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load conf file \\\"%s\\\" fail, error no: %d\",\n                __LINE__, config_filename, result);\n        return result;\n    }\n\n    g_fs_api_ctx.fs = &g_fs_client_vars.client_ctx;\n    FAST_INI_SET_FULL_CTX_EX(ini_ctx, config_filename,\n            fdir_section_name, &iniContext);\n    result = fcfs_api_init_ex1(ctx, &g_fdir_client_vars.client_ctx,\n            &g_fs_api_ctx, ns, &ini_ctx, fdir_section_name,\n            fs_section_name, NULL, NULL, false, true);\n    iniFreeContext(&iniContext);\n    return result;\n}\n\nint fcfs_api_pooled_init_ex(FCFSAPIContext *ctx, const char *ns,\n        const char *config_filename, const char *fdir_section_name,\n        const char *fs_section_name, const bool need_lock,\n        const bool persist_additional_gids)\n{\n    int result;\n    IniContext iniContext;\n    IniFullContext ini_ctx;\n\n    if ((result=iniLoadFromFile(config_filename, &iniContext)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load conf file \\\"%s\\\" fail, error no: %d\",\n                __LINE__, config_filename, result);\n        return result;\n    }\n\n    FAST_INI_SET_FULL_CTX_EX(ini_ctx, config_filename,\n            fdir_section_name, &iniContext);\n    result = fcfs_api_pooled_init_ex1(ctx, ns, &ini_ctx,\n            fdir_section_name, fs_section_name, need_lock,\n            persist_additional_gids);\n    iniFreeContext(&iniContext);\n    return result;\n}\n\nint fcfs_api_init_ex2(FCFSAPIContext *ctx, FDIRClientContext *fdir,\n        FSAPIContext *fsapi, const char *ns, IniFullContext *ini_ctx,\n        const char *fdir_section_name, const char *fs_section_name,\n        const FDIRClientConnManagerType conn_manager_type,\n        const SFConnectionManager *fs_conn_manager,\n        const bool need_lock, const bool persist_additional_gids)\n{\n    const bool bg_thread_enabled = true;\n    const int max_count_per_entry = 0;\n    const int max_idle_time = 3600;\n    int result;\n\n    ini_ctx->section_name = fdir_section_name;\n    if (conn_manager_type == conn_manager_type_simple) {\n        result = fdir_client_simple_init_ex1(fdir,\n                &g_fcfs_auth_client_vars.client_ctx, ini_ctx);\n    } else if (conn_manager_type == conn_manager_type_pooled) {\n        result = fdir_client_pooled_init_ex1(fdir,\n                &g_fcfs_auth_client_vars.client_ctx, ini_ctx,\n                max_count_per_entry, max_idle_time, bg_thread_enabled);\n    } else {\n        result = EINVAL;\n    }\n    if (result != 0) {\n        return result;\n    }\n\n    ini_ctx->section_name = fs_section_name;\n    if ((result=fs_client_init_ex1(fsapi->fs, &g_fcfs_auth_client_vars.\n                    client_ctx, ini_ctx, fs_conn_manager,\n                    bg_thread_enabled)) != 0)\n    {\n        return result;\n    }\n\n    return fcfs_api_common_init(ctx, fdir, fsapi, ns, ini_ctx,\n            fdir_section_name, fs_section_name, need_lock,\n            persist_additional_gids);\n}\n\nvoid fcfs_api_destroy_ex(FCFSAPIContext *ctx)\n{\n    if (ctx->contexts.fdir != NULL) {\n        fdir_client_destroy_ex(ctx->contexts.fdir);\n        ctx->contexts.fdir = NULL;\n    }\n\n    if (ctx->contexts.fsapi != NULL) {\n        if (ctx->contexts.fsapi->fs != NULL) {\n            fs_client_destroy_ex(ctx->contexts.fsapi->fs);\n            ctx->contexts.fsapi->fs = NULL;\n        }\n\n        fs_api_destroy_ex(ctx->contexts.fsapi);\n        ctx->contexts.fsapi = NULL;\n    }\n}\n\nstatic int check_create_root_path(FCFSAPIContext *ctx)\n{\n    int result;\n    int64_t inode;\n    FDIRClientOperFnamePair fname;\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, ctx->owner.oper, \"/\");\n    if ((result=fcfs_api_lookup_inode_by_fullname_ex(ctx,\n                    &fname, LOG_DEBUG, &inode)) != 0)\n    {\n        if (result == ENOENT) {\n            FDIRDEntryInfo dentry;\n\n            FCFS_API_SET_OPERATOR(fname.oper, ctx->owner,\n                    geteuid(), getegid());\n            if ((result=fdir_client_create_dentry(ctx->contexts.fdir, &fname,\n                            ACCESSPERMS | S_IFDIR, &dentry)) == EEXIST)\n            {\n                /* check again */\n                result = fcfs_api_lookup_inode_by_fullname_ex(ctx,\n                        &fname, LOG_DEBUG, &inode);\n            }\n        }\n    }\n\n    return result;\n}\n\nint fcfs_api_start_ex(FCFSAPIContext *ctx)\n{\n    int result;\n\n    if ((result=fs_api_start_ex(ctx->contexts.fsapi)) != 0) {\n        return result;\n    }\n\n    if ((result=sf_connection_manager_start(&ctx->\n                    contexts.fdir->cm)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=sf_connection_manager_start(&ctx->\n                    contexts.fsapi->fs->cm)) != 0)\n    {\n        return result;\n    }\n\n    if (ctx->contexts.fdir->idempotency_enabled ||\n            ctx->contexts.fsapi->fs->idempotency_enabled)\n    {\n        FCServerGroupInfo *server_group;\n        FCAddressPtrArray *address_array;\n        FCServerInfo *first_server;\n\n        address_array = NULL;\n        if (ctx->contexts.fdir->idempotency_enabled) {\n            server_group = fc_server_get_group_by_index(\n                    &ctx->contexts.fdir->cluster.server_cfg,\n                    ctx->contexts.fdir->cluster.service_group_index);\n            if (server_group->comm_type != fc_comm_type_sock) {\n                first_server = FC_SID_SERVERS(ctx->contexts.\n                        fdir->cluster.server_cfg);\n                address_array = &first_server->group_addrs[ctx->contexts.\n                    fdir->cluster.service_group_index].address_array;\n            }\n        }\n        if (ctx->contexts.fsapi->fs->idempotency_enabled &&\n                address_array == NULL)\n        {\n            server_group = fc_server_get_group_by_index(\n                    &FS_CLUSTER_SERVER_CFG(ctx->contexts.fsapi->fs),\n                    FS_CFG_SERVICE_INDEX(ctx->contexts.fsapi->fs));\n            if (server_group->comm_type != fc_comm_type_sock) {\n                first_server = FC_SID_SERVERS(FS_CLUSTER_SERVER_CFG(\n                            ctx->contexts.fsapi->fs));\n                address_array = &first_server->group_addrs[\n                    FS_CFG_SERVICE_INDEX(ctx->contexts.fsapi->fs)].\n                        address_array;\n            }\n        }\n\n        if ((result=receipt_handler_init(address_array)) != 0) {\n            return result;\n        }\n    }\n\n    if ((result=check_create_root_path(ctx)) != 0) {\n        return result;\n    }\n\n    if ((result=fdir_client_init_node_id(ctx->contexts.fdir)) != 0) {\n        return result;\n    }\n\n    if (ctx->async_report.enabled) {\n        return async_reporter_init(ctx);\n    } else {\n        return 0;\n    }\n}\n\nvoid fcfs_api_terminate_ex(FCFSAPIContext *ctx)\n{\n    fs_api_terminate_ex(ctx->contexts.fsapi);\n    if (ctx->async_report.enabled) {\n        async_reporter_terminate();\n    }\n}\n\nvoid fcfs_api_async_report_config_to_string_ex(FCFSAPIContext *ctx,\n        char *output, const int size)\n{\n    int len;\n\n    len = snprintf(output, size, \"use_sys_lock_for_append: %d, \"\n            \"async_report { enabled: %d\", ctx->use_sys_lock_for_append,\n            ctx->async_report.enabled);\n    if (ctx->async_report.enabled) {\n        len += snprintf(output + len, size - len, \", \"\n                \"async_report_interval_ms: %d, \"\n                \"shared_allocator_count: %d, \"\n                \"hashtable_sharding_count: %d, \"\n                \"hashtable_total_capacity: %\"PRId64,\n                ctx->async_report.interval_ms,\n                ctx->async_report.shared_allocator_count,\n                ctx->async_report.hashtable_sharding_count,\n                ctx->async_report.hashtable_total_capacity);\n        if (len > size) {\n            len = size;\n        }\n    }\n    snprintf(output + len, size - len, \" } \");\n}\n\nstatic int fcfs_api_setgroups(FCFSAPIOwnerInfo *owner_info,\n        const gid_t *groups, const int count)\n{\n    int index;\n    const gid_t *group;\n    const gid_t *end;\n    char *buff;\n\n    if (count == 0 || (count == 1 && groups[0] ==\n                owner_info->oper.gid))\n    {\n        owner_info->oper.additional_gids.count = 0;\n        owner_info->oper.additional_gids.list = NULL;\n        return 0;\n    }\n\n    owner_info->oper.additional_gids.count = count;\n    owner_info->oper.additional_gids.list = fc_malloc(\n            FDIR_ADDITIONAL_GROUP_BYTES(owner_info->oper));\n    if (owner_info->oper.additional_gids.list == NULL) {\n        return ENOMEM;\n    }\n\n    index = 0;\n    end = groups + count;\n    for (group=groups; group<end; group++) {\n        if (*group != owner_info->oper.gid) {\n            buff = (char *)(owner_info->oper.additional_gids.\n                    list + 4 * index++);\n            int2buff(*group, buff);\n        }\n    }\n\n    owner_info->oper.additional_gids.count = index;\n    return 0;\n}\n\nstatic int fcfs_api_getgroups(FCFSAPIOwnerInfo *owner_info)\n{\n    int result;\n    int count;\n    gid_t groups[FDIR_MAX_USER_GROUP_COUNT];\n\n    count = getgroups(FDIR_MAX_USER_GROUP_COUNT, groups);\n    if (count < 0) {\n        result = errno != 0 ? errno : ENOENT;\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"getgroups fail, errno: %d, error info: %s\",\n                __LINE__, result, STRERROR(result));\n        return result;\n    }\n\n    return fcfs_api_setgroups(owner_info, groups, count);\n}\n\nstatic int fcfs_api_getgrouplist(FCFSAPIOwnerInfo *owner_info)\n{\n    int result;\n    int count;\n    struct passwd *user;\n#ifdef OS_FREEBSD\n    int i;\n    int groups[FDIR_MAX_USER_GROUP_COUNT];\n    gid_t _groups[FDIR_MAX_USER_GROUP_COUNT];\n#else\n    gid_t groups[FDIR_MAX_USER_GROUP_COUNT];\n#endif\n    gid_t *ptr;\n\n    errno = ENOENT;\n    if ((user=getpwuid(owner_info->oper.uid)) == NULL) {\n        result = errno != 0 ? errno : ENOENT;\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"getpwuid fail, errno: %d, error info: %s\",\n                __LINE__, result, STRERROR(result));\n        return result;\n    }\n\n    count = FDIR_MAX_USER_GROUP_COUNT;\n    if (getgrouplist(user->pw_name, owner_info->\n                oper.gid, groups, &count) < 0)\n    {\n        result = errno != 0 ? errno : ENOENT;\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"getgroups fail, errno: %d, error info: %s\",\n                __LINE__, result, STRERROR(result));\n        return result;\n    }\n\n#ifdef OS_FREEBSD\n    for (i=0; i<count; i++) {\n        _groups[i] = groups[i];\n    }\n    ptr = _groups;\n#else\n    ptr = groups;\n#endif\n\n    return fcfs_api_setgroups(owner_info, ptr, count);\n}\n\nint fcfs_api_set_owner(FCFSAPIContext *ctx)\n{\n    if (ctx->owner.type == fcfs_api_owner_type_fixed) {\n        return EINVAL;\n    }\n\n    ctx->owner.oper.uid = geteuid();\n    ctx->owner.oper.gid = getegid();\n    return fcfs_api_getgroups(&ctx->owner);\n}\n\nstatic int fcfs_api_load_owner_config(IniFullContext *ini_ctx,\n        FCFSAPIContext *ctx)\n{\n    int result;\n    char *owner_type;\n    char *owner_user;\n    char *owner_group;\n    struct group *group;\n    struct passwd *user;\n\n    owner_type = iniGetStrValue(ini_ctx->section_name,\n            \"owner_type\", ini_ctx->context);\n    if (owner_type == NULL || *owner_type == '\\0') {\n        ctx->owner.type = fcfs_api_owner_type_caller;\n    } else if (strcasecmp(owner_type, FCFS_API_OWNER_TYPE_CALLER_STR) == 0) {\n        ctx->owner.type = fcfs_api_owner_type_caller;\n    } else if (strcasecmp(owner_type, FCFS_API_OWNER_TYPE_FIXED_STR) == 0) {\n        ctx->owner.type = fcfs_api_owner_type_fixed;\n    } else {\n        ctx->owner.type = fcfs_api_owner_type_caller;\n    }\n\n    if (ctx->owner.type == fcfs_api_owner_type_caller) {\n        return fcfs_api_set_owner(ctx);\n    }\n\n    owner_user = iniGetStrValue(ini_ctx->section_name,\n            \"owner_user\", ini_ctx->context);\n    if (owner_user == NULL || *owner_user == '\\0') {\n        ctx->owner.oper.uid = geteuid();\n    } else {\n        user = getpwnam(owner_user);\n        if (user == NULL) {\n            result = errno != 0 ? errno : ENOENT;\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"getpwnam %s fail, errno: %d, error info: %s\",\n                    __LINE__, owner_user, result, STRERROR(result));\n            return result;\n        }\n        ctx->owner.oper.uid = user->pw_uid;\n    }\n\n    owner_group = iniGetStrValue(ini_ctx->section_name,\n            \"owner_group\", ini_ctx->context);\n    if (owner_group == NULL || *owner_group == '\\0') {\n        ctx->owner.oper.gid = getegid();\n    } else {\n        group = getgrnam(owner_group);\n        if (group == NULL) {\n            result = errno != 0 ? errno : ENOENT;\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"getgrnam %s fail, errno: %d, error info: %s\",\n                    __LINE__, owner_group, result, STRERROR(result));\n            return result;\n        }\n\n        ctx->owner.oper.gid = group->gr_gid;\n    }\n\n    if (ctx->owner.oper.uid == geteuid() &&\n            ctx->owner.oper.gid == getegid())\n    {\n        return fcfs_api_getgroups(&ctx->owner);\n    } else {\n        return fcfs_api_getgrouplist(&ctx->owner);\n    }\n}\n\nint fcfs_api_load_idempotency_config_ex(const char *log_prefix_name,\n        IniFullContext *ini_ctx, const char *fdir_section_name,\n        const char *fs_section_name)\n{\n#define MIN_THREAD_STACK_SIZE  (320 * 1024)\n    const int task_buffer_extra_size = 0;\n    const bool need_set_run_by = true;\n    int fixed_buffer_size;\n    int result;\n    SFContextIniConfig config;\n    FCServerGroupInfo *server_group;\n    FCCommunicationType comm_type;\n\n    ini_ctx->section_name = FCFS_API_INI_IDEMPOTENCY_SECTION_NAME;\n    if ((result=client_channel_init(ini_ctx)) != 0) {\n        return result;\n    }\n\n    g_sf_context.is_client = true;\n    g_fdir_client_vars.client_ctx.idempotency_enabled =\n        iniGetBoolValue(fdir_section_name, \"idempotency_enabled\",\n                ini_ctx->context, g_idempotency_client_cfg.enabled);\n    g_fs_client_vars.client_ctx.idempotency_enabled =\n        iniGetBoolValue(fs_section_name, \"idempotency_enabled\",\n                ini_ctx->context, g_idempotency_client_cfg.enabled);\n\n    fixed_buffer_size = 0;\n    comm_type = fc_comm_type_sock;\n    if (g_fdir_client_vars.client_ctx.idempotency_enabled) {\n        server_group = fc_server_get_group_by_index(\n                &g_fdir_client_vars.client_ctx.cluster.server_cfg,\n                g_fdir_client_vars.client_ctx.cluster.service_group_index);\n        if (comm_type != server_group->comm_type) {\n            comm_type = server_group->comm_type;\n            fixed_buffer_size = g_fdir_client_vars.client_ctx.\n                cluster.server_cfg.buffer_size;\n        }\n    }\n    if (g_fs_client_vars.client_ctx.idempotency_enabled) {\n        server_group = fc_server_get_group_by_index(\n                &FS_CLUSTER_SERVER_CFG(&g_fs_client_vars.client_ctx),\n                FS_CFG_SERVICE_INDEX(&g_fs_client_vars.client_ctx));\n        if (comm_type != server_group->comm_type) {\n            comm_type = g_fdir_client_vars.client_ctx.idempotency_enabled ?\n                fc_comm_type_both : server_group->comm_type;\n        }\n        if (fixed_buffer_size == 0) {\n            fixed_buffer_size = FS_CLUSTER_SERVER_CFG(&g_fs_client_vars.\n                    client_ctx).buffer_size;\n        } else if (FS_CLUSTER_SERVER_CFG(&g_fs_client_vars.\n                    client_ctx).buffer_size > 0)\n        {\n            fixed_buffer_size = FC_MIN(fixed_buffer_size,\n                    FS_CLUSTER_SERVER_CFG(&g_fs_client_vars.\n                        client_ctx).buffer_size);\n        }\n    }\n\n    SF_SET_CONTEXT_INI_CONFIG(config, comm_type, ini_ctx->filename,\n            ini_ctx->context, FCFS_API_INI_IDEMPOTENCY_SECTION_NAME,\n            0, 0, FCFS_API_IDEMPOTENCY_DEFAULT_WORK_THREADS);\n    if ((result=sf_load_config_ex(log_prefix_name, &config,\n                    fixed_buffer_size, task_buffer_extra_size,\n                    need_set_run_by)) != 0)\n    {\n        return result;\n    }\n    if (SF_G_THREAD_STACK_SIZE < MIN_THREAD_STACK_SIZE) {\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"config file: %s, thread_stack_size: %d is too small, \"\n                \"set to %d\", __LINE__, ini_ctx->filename,\n                SF_G_THREAD_STACK_SIZE, MIN_THREAD_STACK_SIZE);\n        SF_G_THREAD_STACK_SIZE = MIN_THREAD_STACK_SIZE;\n    }\n\n    return 0;\n}\n\nstatic int load_mountpoint(IniFullContext *ini_ctx,\n        string_t *mountpoint, const bool fuse_check)\n{\n    struct statfs buf;\n    int result;\n    int ret;\n\n    if (mountpoint->str == NULL) {\n        mountpoint->str = iniGetStrValueEx(ini_ctx->section_name,\n                \"mountpoint\", ini_ctx->context, true);\n        if (mountpoint->str == NULL || *mountpoint->str == '\\0') {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"config file: %s, section: %s, item: mountpoint \"\n                    \"not exist or is empty\", __LINE__, ini_ctx->filename,\n                    ini_ctx->section_name);\n            return ENOENT;\n        }\n        mountpoint->len = strlen(mountpoint->str);\n    }\n\n    if (*mountpoint->str != '/') {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"config file: %s, mountpoint: %s must start with \\\"/\\\"\",\n                __LINE__, ini_ctx->filename, mountpoint->str);\n        return ENOENT;\n    }\n\n    while (mountpoint->len > 0 && mountpoint->\n            str[mountpoint->len - 1] == '/')\n    {\n        mountpoint->len--;\n    }\n\n    if (fuse_check && !fileExists(mountpoint->str)) {\n        result = errno != 0 ? errno : ENOENT;\n        if (result == ENOTCONN) {\n#ifdef OS_LINUX\n            ret = umount2(mountpoint->str, MNT_FORCE);\n#else\n            ret = unmount(mountpoint->str, 0);\n#endif\n            if (ret == 0) {\n                result = 0;\n            } else {\n                result = errno != 0 ? errno : EBUSY;\n                if (result == EPERM) {\n                    logError(\"file: \"__FILE__\", line: %d, \"\n                            \"unmount %s fail, you should run \"\n                            \"\\\"sudo umount -f %s\\\" manually\", __LINE__,\n                            mountpoint->str, mountpoint->str);\n                } else {\n                    logError(\"file: \"__FILE__\", line: %d, \"\n                            \"unmount %s fail, errno: %d, error info: %s\",\n                            __LINE__, mountpoint->str,\n                            result, STRERROR(result));\n                }\n                return result;\n            }\n        } else if (result != 0) {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"mountpoint: %s can't be accessed, errno: %d, \"\n                    \"error info: %s\", __LINE__, mountpoint->str,\n                    result, STRERROR(result));\n            return result;\n        }\n    }\n\n    if (!isDir(mountpoint->str)) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"mountpoint: %s is not a directory!\",\n                __LINE__, mountpoint->str);\n        return ENOTDIR;\n    }\n\n    if (fuse_check) {\n        if (statfs(mountpoint->str, &buf) != 0) {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"statfs mountpoint: %s fail, error info: %s\",\n                    __LINE__, mountpoint->str, STRERROR(errno));\n            return errno != 0 ? errno : ENOENT;\n        }\n\n        if ((buf.f_type & FUSE_SUPER_MAGIC) == FUSE_SUPER_MAGIC) {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"mountpoint: %s already mounted by FUSE\",\n                    __LINE__, mountpoint->str);\n            return EEXIST;\n        }\n    }\n\n    return 0;\n}\n\nint fcfs_api_check_mountpoint(const char *config_filename,\n        const string_t *mountpoint)\n{\n    int result;\n    string_t base_path;\n\n    FC_SET_STRING(base_path, SF_G_BASE_PATH_STR);\n    if (fc_path_contains(&base_path, mountpoint, &result)) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"config file: %s, base path: %s contains mountpoint: %.*s, \"\n                \"this case is not allowed\", __LINE__, config_filename,\n                SF_G_BASE_PATH_STR, mountpoint->len, mountpoint->str);\n        return EINVAL;\n    } else if (result != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"config file: %s, base path: %s or mountpoint: %.*s \"\n                \"is invalid\", __LINE__, config_filename,\n                SF_G_BASE_PATH_STR, mountpoint->len, mountpoint->str);\n    }\n\n    return result;\n}\n\nint fcfs_api_load_ns_mountpoint(IniFullContext *ini_ctx,\n        const char *fdir_section_name, FCFSAPINSMountpointHolder *nsmp,\n        string_t *mountpoint, const bool fuse_check)\n{\n    string_t ns;\n    int result;\n\n    if ((result=load_mountpoint(ini_ctx, mountpoint, fuse_check)) != 0) {\n        return result;\n    }\n\n    if (nsmp->ns == NULL) {\n        ns.str = iniGetStrValue(fdir_section_name,\n                \"namespace\", ini_ctx->context);\n        if (ns.str == NULL || *ns.str == '\\0') {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"config file: %s, section: %s, item: namespace \"\n                    \"not exist or is empty\", __LINE__, ini_ctx->\n                    filename, fdir_section_name);\n            return ENOENT;\n        }\n    } else {\n        ns.str = nsmp->ns;\n    }\n\n    ns.len = strlen(ns.str);\n    nsmp->ns = fc_malloc(ns.len + mountpoint->len + 2);\n    if (nsmp->ns == NULL) {\n        return ENOMEM;\n    }\n    memcpy(nsmp->ns, ns.str, ns.len + 1);\n    nsmp->mountpoint = nsmp->ns + ns.len + 1;\n    memcpy(nsmp->mountpoint, mountpoint->str, mountpoint->len);\n    *(nsmp->mountpoint + mountpoint->len) = '\\0';\n    mountpoint->str = nsmp->mountpoint;\n    return 0;\n}\n\nvoid fcfs_api_free_ns_mountpoint(FCFSAPINSMountpointHolder *nsmp)\n{\n    if (nsmp->ns != NULL) {\n        free(nsmp->ns);\n        nsmp->ns = NULL;\n    }\n}\n\nvoid fcfs_api_log_client_common_configs(FCFSAPIContext *ctx,\n        const char *fdir_section_name, const char *fs_section_name,\n        BufferInfo *sf_idempotency_config, char *owner_config)\n{\n    char auth_config[512];\n    char fsapi_config[1024];\n    char async_report_config[512];\n    int len;\n\n    if (ctx->contexts.fdir->idempotency_enabled ||\n            ctx->contexts.fsapi->fs->idempotency_enabled)\n    {\n        len = snprintf(sf_idempotency_config->buff,\n                sf_idempotency_config->alloc_size,\n                \"%s idempotency_enabled=%d, \"\n                \"%s idempotency_enabled=%d, \",\n                fdir_section_name, ctx->contexts.\n                fdir->idempotency_enabled,\n                fs_section_name, ctx->contexts.\n                fsapi->fs->idempotency_enabled);\n\n        idempotency_client_channel_config_to_string_ex(\n                sf_idempotency_config->buff + len,\n                sf_idempotency_config->alloc_size - len, true);\n        sf_idempotency_config->length = strlen(sf_idempotency_config->buff);\n    } else {\n        *sf_idempotency_config->buff = '\\0';\n        sf_idempotency_config->length = 0;\n    }\n\n    len = sprintf(owner_config, \"owner_type: %s\",\n            fcfs_api_get_owner_type_caption(ctx->owner.type));\n    if (ctx->owner.type == fcfs_api_owner_type_fixed) {\n        struct passwd *user;\n        struct group *group;\n\n        user = getpwuid(ctx->owner.oper.uid);\n        group = getgrgid(ctx->owner.oper.gid);\n        sprintf(owner_config + len, \", owner_user: %s, owner_group: %s\",\n                user->pw_name, group->gr_name);\n    }\n\n    fcfs_api_async_report_config_to_string_ex(ctx, async_report_config,\n            sizeof(async_report_config));\n    fcfs_auth_config_to_string(&ctx->contexts.fdir->auth,\n            auth_config, sizeof(auth_config));\n    snprintf(async_report_config + strlen(async_report_config),\n            sizeof(async_report_config) - strlen(async_report_config),\n            \", %s\", auth_config);\n    fdir_client_log_config_ex(ctx->contexts.fdir,\n            async_report_config, false);\n\n    fs_api_config_to_string(fsapi_config, sizeof(fsapi_config));\n    fcfs_auth_config_to_string(&ctx->contexts.fsapi->fs->auth,\n            auth_config, sizeof(auth_config));\n    snprintf(fsapi_config + strlen(fsapi_config),\n            sizeof(fsapi_config) - strlen(fsapi_config),\n            \", %s\", auth_config);\n    fs_client_log_config_ex(ctx->contexts.fsapi->fs,\n            fsapi_config, false);\n}\n"
  },
  {
    "path": "src/api/fcfs_api.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_API_H\n#define _FCFS_API_H\n\n#include \"fastcommon/shared_func.h\"\n#include \"fcfs_api_types.h\"\n#include \"fcfs_api_file.h\"\n#include \"fcfs_api_util.h\"\n\n#define FCFS_API_DEFAULT_FASTDIR_SECTION_NAME    \"FastDIR\"\n#define FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME  \"FastStore\"\n\n#define FCFS_API_OWNER_TYPE_CALLER_STR  \"caller\"\n#define FCFS_API_OWNER_TYPE_FIXED_STR   \"fixed\"\n\n#define fcfs_api_set_contexts(ns)  fcfs_api_set_contexts_ex(&g_fcfs_api_ctx, ns)\n\n#define fcfs_api_init(ns, config_filename)   \\\n    fcfs_api_init_ex(&g_fcfs_api_ctx, ns, config_filename,  \\\n            FCFS_API_DEFAULT_FASTDIR_SECTION_NAME,   \\\n            FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME)\n\n#define fcfs_api_pooled_init(ns, config_filename)   \\\n    fcfs_api_pooled_init_ex(&g_fcfs_api_ctx, ns, config_filename, \\\n            FCFS_API_DEFAULT_FASTDIR_SECTION_NAME,   \\\n            FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME, \\\n            true, true)\n\n#define fcfs_api_pooled_init1(ns, ini_ctx) \\\n    fcfs_api_pooled_init_ex1(&g_fcfs_api_ctx, ns, ini_ctx, \\\n            FCFS_API_DEFAULT_FASTDIR_SECTION_NAME,   \\\n            FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME, \\\n            true, true)\n\n#define fcfs_api_destroy()  fcfs_api_destroy_ex(&g_fcfs_api_ctx)\n\n#define fcfs_api_start() fcfs_api_start_ex(&g_fcfs_api_ctx)\n#define fcfs_api_terminate() fcfs_api_terminate_ex(&g_fcfs_api_ctx)\n\n#define fcfs_api_async_report_config_to_string(output, size) \\\n    fcfs_api_async_report_config_to_string_ex(&g_fcfs_api_ctx, output, size)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    static inline void fcfs_api_set_contexts_ex1(FCFSAPIContext *ctx,\n            FDIRClientContext *fdir, FSAPIContext *fsapi, const char *ns)\n    {\n        ctx->contexts.fdir = fdir;\n        ctx->contexts.fsapi = fsapi;\n\n        ctx->ns.str = ctx->ns_holder;\n        ctx->ns.len = fc_safe_strcpy(ctx->ns_holder, ns);\n    }\n\n    static inline void fcfs_api_set_contexts_ex(FCFSAPIContext *ctx,\n            const char *ns)\n    {\n        g_fs_api_ctx.fs = &g_fs_client_vars.client_ctx;\n        return fcfs_api_set_contexts_ex1(ctx, &g_fdir_client_vars.\n                client_ctx, &g_fs_api_ctx, ns);\n    }\n\n    int fcfs_api_init_ex1(FCFSAPIContext *ctx, FDIRClientContext *fdir,\n            FSAPIContext *fsapi, const char *ns, IniFullContext *ini_ctx,\n            const char *fdir_section_name, const char *fs_section_name,\n            const SFConnectionManager *fdir_conn_manager,\n            const SFConnectionManager *fs_conn_manager,\n            const bool need_lock, const bool persist_additional_gids);\n\n    int fcfs_api_init_ex(FCFSAPIContext *ctx, const char *ns,\n            const char *config_filename, const char *fdir_section_name,\n            const char *fs_section_name);\n\n    int fcfs_api_init_ex2(FCFSAPIContext *ctx, FDIRClientContext *fdir,\n            FSAPIContext *fsapi, const char *ns, IniFullContext *ini_ctx,\n            const char *fdir_section_name, const char *fs_section_name,\n            const FDIRClientConnManagerType conn_manager_type,\n            const SFConnectionManager *fs_conn_manager,\n            const bool need_lock, const bool persist_additional_gids);\n\n    static inline int fcfs_api_pooled_init_ex1(FCFSAPIContext *ctx,\n            const char *ns, IniFullContext *ini_ctx, const char *\n            fdir_section_name, const char *fs_section_name,\n            const bool need_lock, const bool persist_additional_gids)\n    {\n        g_fs_api_ctx.fs = &g_fs_client_vars.client_ctx;\n        return fcfs_api_init_ex2(ctx, &g_fdir_client_vars.client_ctx,\n                &g_fs_api_ctx, ns, ini_ctx, fdir_section_name,\n                fs_section_name, conn_manager_type_pooled,\n                NULL, need_lock, persist_additional_gids);\n    }\n\n    int fcfs_api_pooled_init_ex(FCFSAPIContext *ctx, const char *ns,\n            const char *config_filename, const char *fdir_section_name,\n            const char *fs_section_name, const bool need_lock,\n            const bool persist_additional_gids);\n\n    void fcfs_api_destroy_ex(FCFSAPIContext *ctx);\n\n    int fcfs_api_start_ex(FCFSAPIContext *ctx);\n\n    void fcfs_api_terminate_ex(FCFSAPIContext *ctx);\n\n    int fcfs_api_client_session_create(FCFSAPIContext *ctx, const bool publish);\n\n    static inline int fcfs_api_init_with_auth(const char *ns,\n            const char *config_filename, const bool publish)\n    {\n        int result;\n        if ((result=fcfs_api_init(ns, config_filename)) != 0) {\n            return result;\n        }\n\n        return fcfs_api_client_session_create(&g_fcfs_api_ctx, publish);\n    }\n\n    static inline int fcfs_api_pooled_init_with_auth(const char *ns,\n            const char *config_filename, const bool publish)\n    {\n        int result;\n        if ((result=fcfs_api_pooled_init(ns, config_filename)) != 0) {\n            return result;\n        }\n\n        return fcfs_api_client_session_create(&g_fcfs_api_ctx, publish);\n    }\n\n    static inline int fcfs_api_pooled_init1_with_auth(const char *ns,\n            IniFullContext *ini_ctx, const bool publish)\n    {\n        int result;\n        if ((result=fcfs_api_pooled_init1(ns, ini_ctx)) != 0) {\n            return result;\n        }\n\n        return fcfs_api_client_session_create(&g_fcfs_api_ctx, publish);\n    }\n\n    int fcfs_api_load_idempotency_config_ex(const char *log_prefix_name,\n            IniFullContext *ini_ctx, const char *fdir_section_name,\n            const char *fs_section_name);\n\n    static inline int fcfs_api_load_idempotency_config(const char\n            *log_prefix_name, IniFullContext *ini_ctx)\n    {\n        return fcfs_api_load_idempotency_config_ex(log_prefix_name,\n                ini_ctx, FCFS_API_DEFAULT_FASTDIR_SECTION_NAME,\n                FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME);\n    }\n\n    int fcfs_api_load_ns_mountpoint(IniFullContext *ini_ctx,\n            const char *fdir_section_name, FCFSAPINSMountpointHolder *nsmp,\n            string_t *mountpoint, const bool fuse_check);\n\n    void fcfs_api_free_ns_mountpoint(FCFSAPINSMountpointHolder *nsmp);\n\n    int fcfs_api_check_mountpoint(const char *config_filename,\n            const string_t *mountpoint);\n\n    static inline int fcfs_api_check_mountpoint1(const char\n            *config_filename, const char *mpoint)\n    {\n        string_t mountpoint;\n        FC_SET_STRING(mountpoint, (char *)mpoint);\n        return fcfs_api_check_mountpoint(config_filename, &mountpoint);\n    }\n\n    void fcfs_api_async_report_config_to_string_ex(FCFSAPIContext *ctx,\n            char *output, const int size);\n\n    void fcfs_api_log_client_common_configs(FCFSAPIContext *ctx,\n            const char *fdir_section_name, const char *fs_section_name,\n            BufferInfo *sf_idempotency_config, char *owner_config);\n\n    int fcfs_api_set_owner(FCFSAPIContext *ctx);\n\n    static inline const char *fcfs_api_get_owner_type_caption(\n            const FCFSAPIOwnerType owner_type)\n    {\n        switch (owner_type) {\n            case fcfs_api_owner_type_caller:\n                return FCFS_API_OWNER_TYPE_CALLER_STR;\n            case fcfs_api_owner_type_fixed:\n                return FCFS_API_OWNER_TYPE_FIXED_STR;\n            default:\n                return \"\";\n        }\n    }\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/fcfs_api_allocator.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdlib.h>\n#include \"fcfs_api_allocator.h\"\n\nFCFSAPIAllocatorCtxArray g_fcfs_api_allocator_array;\n\nstatic int async_report_event_alloc_init(FCFSAPIAsyncReportEvent *event,\n        struct fast_mblock_man *allocator)\n{\n    event->allocator = allocator;\n    return 0;\n}\n\nstatic int waiting_task_alloc_init(FCFSAPIWaitingTask *task,\n        struct fast_mblock_man *allocator)\n{\n    int result;\n\n    if ((result=init_pthread_lock_cond_pair(&task->lcp)) != 0) {\n        return result;\n    }\n\n    task->allocator = allocator;\n    return 0;\n}\n\nstatic int init_allocator_context(FCFSAPIAllocatorContext *ctx)\n{\n    int result;\n    if ((result=fast_mblock_init_ex1(&ctx->async_report_event,\n                    \"async_report_event\", sizeof(FCFSAPIAsyncReportEvent),\n                    4096, 0, (fast_mblock_object_init_func)\n                    async_report_event_alloc_init,\n                    &ctx->async_report_event, true)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fast_mblock_init_ex1(&ctx->waiting_task,\n                    \"waiting_task\", sizeof(FCFSAPIWaitingTask), 1024, 0,\n                    (fast_mblock_object_init_func)waiting_task_alloc_init,\n                    &ctx->waiting_task, true)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nint fcfs_api_allocator_init(FCFSAPIContext *api_ctx)\n{\n    int result;\n    int bytes;\n    FCFSAPIAllocatorContext *ctx;\n    FCFSAPIAllocatorContext *end;\n\n    g_fcfs_api_allocator_array.count = api_ctx->\n        async_report.shared_allocator_count;\n    bytes = sizeof(FCFSAPIAllocatorContext) * g_fcfs_api_allocator_array.count;\n    g_fcfs_api_allocator_array.allocators = (FCFSAPIAllocatorContext *)\n        fc_malloc(bytes);\n    if (g_fcfs_api_allocator_array.allocators == NULL) {\n        return ENOMEM;\n    }\n\n    end = g_fcfs_api_allocator_array.allocators +\n        g_fcfs_api_allocator_array.count;\n    for (ctx=g_fcfs_api_allocator_array.allocators; ctx<end; ctx++) {\n        if ((result=init_allocator_context(ctx)) != 0) {\n            return result;\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "src/api/fcfs_api_allocator.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_API_ALLOCATOR_H\n#define _FCFS_API_ALLOCATOR_H\n\n#include \"fastcommon/pthread_func.h\"\n#include \"fcfs_api_types.h\"\n\ntypedef struct fcfs_api_allocator_context {\n    struct fast_mblock_man async_report_event; //element: FCFSAPIAsyncReportEvent\n    struct fast_mblock_man waiting_task;       //element: FCFSAPIWaitingTask\n} FCFSAPIAllocatorContext;\n\ntypedef struct fcfs_api_allocator_ctx_array {\n    int count;\n    FCFSAPIAllocatorContext *allocators;\n} FCFSAPIAllocatorCtxArray;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern FCFSAPIAllocatorCtxArray g_fcfs_api_allocator_array;\n\n    int fcfs_api_allocator_init(FCFSAPIContext *api_ctx);\n\n    static inline FCFSAPIAllocatorContext *fcfs_api_allocator_get(\n            const uint64_t id)\n    {\n        return g_fcfs_api_allocator_array.allocators +\n            id % g_fcfs_api_allocator_array.count;\n    }\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/fcfs_api_file.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"sf/sf_iov.h\"\n#include \"fcfs_api_util.h\"\n#include \"async_reporter.h\"\n#include \"fcfs_api_file.h\"\n\n#define FCFS_API_MAGIC_NUMBER    1588076578\n\nstatic int file_truncate(FCFSAPIContext *ctx, const int64_t oid,\n        const int64_t new_size, const FDIRDentryOperator *oper,\n        const int64_t tid);\n\nstatic int deal_open_flags(FCFSAPIFileInfo *fi, FDIRClientOperFnamePair *path,\n        const FDIRDentryOperator *oper, const mode_t mode,\n        const int64_t tid, int result)\n{\n    int bytes;\n\n    fi->tid = tid;\n    fi->oper = *oper;\n    if (fi->ctx->persist_additional_gids) {\n        /* persist fi->oper.additional_gids.list for future usage */\n        if (oper->additional_gids.count > 0) {\n            bytes = FDIR_ADDITIONAL_GROUP_BYTES(*oper);\n            if (bytes <= sizeof(fi->fixed_groups_buff)) {\n                fi->oper.additional_gids.list = fi->fixed_groups_buff;\n            } else {\n                fi->oper.additional_gids.list = fc_malloc(bytes);\n                if (fi->oper.additional_gids.list == NULL) {\n                    return ENOMEM;\n                }\n            }\n            memcpy((void *)fi->oper.additional_gids.list,\n                    oper->additional_gids.list, bytes);\n        } else {\n            fi->oper.additional_gids.list = fi->fixed_groups_buff;\n        }\n    }\n\n    if (!((fi->flags & O_WRONLY) || (fi->flags & O_RDWR))) {\n        fi->offset = 0;\n        return (fi->flags & O_TRUNC) ? EACCES : result;\n    }\n\n    if ((fi->flags & O_CREAT)) {\n        if (result == 0) {\n            if ((fi->flags & O_EXCL)) {\n                return EEXIST;\n            }\n        } else if (result == ENOENT) {\n            if ((result=fdir_client_create_dentry(fi->ctx->contexts.fdir,\n                            path, mode, &fi->dentry)) != 0)\n            {\n                if (result == EEXIST) {\n                    if ((fi->flags & O_EXCL)) {\n                        return EEXIST;\n                    }\n\n                    if ((result=fcfs_api_access_dentry_by_path_ex(fi->ctx,\n                                    path, FCFS_API_GET_ACCESS_MASK(fi->flags),\n                                    FCFS_API_GET_ACCESS_FLAGS(fi->flags),\n                                    &fi->dentry)) != 0)\n                    {\n                        return result;\n                    }\n                } else {\n                    return result;\n                }\n            }\n        } else {\n            return result;\n        }\n    } else if (result != 0) {\n        return result;\n    }\n\n    if (S_ISDIR(fi->dentry.stat.mode)) {\n        return EISDIR;  //flags O_WRONLY or O_RDWR are forbidden for directory\n    }\n    if ((fi->flags & O_NOFOLLOW) && S_ISLNK(fi->dentry.stat.mode)) {\n        return ELOOP;\n    }\n\n    fi->write_notify.last_modified_time = fi->dentry.stat.mtime;\n    if ((fi->flags & O_TRUNC)) {\n        if (fi->dentry.stat.size > 0) {\n            if (S_ISLNK(fi->dentry.stat.mode)) {\n                return EPERM;\n            }\n            if ((result=file_truncate(fi->ctx, fi->dentry.\n                            inode, 0, &fi->oper, tid)) != 0)\n            {\n                return result;\n            }\n\n            fi->dentry.stat.size = 0;\n            fi->dentry.stat.space_end = 0;\n        }\n    }\n\n    if ((fi->flags & O_APPEND)) {\n        fi->offset = fi->dentry.stat.size;\n    } else {\n        fi->offset = 0;\n    }\n    return 0;\n}\n\n#define SET_FILE_COMMON_FIELDS(fi, _ctx, _flags) \\\n    fi->ctx = _ctx;     \\\n    fi->flags = _flags; \\\n    fi->sessions.flock.mconn = NULL\n\nint fcfs_api_open_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi,\n        const char *path, const int flags,\n        const FCFSAPIFileContext *fctx)\n{\n    FDIRClientOperFnamePair fname;\n    mode_t mode;\n    int result;\n\n    if ((fctx->mode & S_IFMT)) {\n        mode = (fctx->mode & (~S_IFMT)) | S_IFREG;\n    } else {\n        mode = fctx->mode | S_IFREG;\n    }\n\n    SET_FILE_COMMON_FIELDS(fi, ctx, flags);\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, fctx->oper, path);\n    result = fcfs_api_access_dentry_by_path_ex(ctx,\n            &fname, FCFS_API_GET_ACCESS_MASK(flags),\n            FCFS_API_GET_ACCESS_FLAGS(flags), &fi->dentry);\n    if ((result=deal_open_flags(fi, &fname, &fctx->oper,\n                    mode, fctx->tid, result)) != 0)\n    {\n        return result;\n    }\n\n    fi->magic = FCFS_API_MAGIC_NUMBER;\n    return 0;\n}\n\nint fcfs_api_open_by_dentry_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi,\n        const FDIRDEntryInfo *dentry, const int flags,\n        const FCFSAPIFileContext *fctx)\n{\n    int result;\n\n    SET_FILE_COMMON_FIELDS(fi, ctx, flags);\n    fi->dentry = *dentry;\n    result = 0;\n    if ((result=deal_open_flags(fi, NULL, &fctx->oper,\n                    fctx->mode, fctx->tid, result)) != 0)\n    {\n        return result;\n    }\n\n    fi->magic = FCFS_API_MAGIC_NUMBER;\n    return 0;\n}\n\nint fcfs_api_open_by_inode_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi,\n        const int64_t inode, const int flags, const FCFSAPIFileContext *fctx)\n{\n    FDIRClientOperInodePair oino;\n    int result;\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, fctx->oper, inode);\n    if ((result=fcfs_api_access_dentry_by_inode_ex(ctx, &oino,\n                    FCFS_API_GET_ACCESS_MASK(flags),\n                    FCFS_API_GET_ACCESS_FLAGS(flags),\n                    &fi->dentry)) != 0)\n    {\n        return result;\n    }\n\n    SET_FILE_COMMON_FIELDS(fi, ctx, flags);\n    if ((result=deal_open_flags(fi, NULL, &fctx->oper,\n                    fctx->mode, fctx->tid, result)) != 0)\n    {\n        return result;\n    }\n\n    fi->magic = FCFS_API_MAGIC_NUMBER;\n    return 0;\n}\n\nint fcfs_api_close(FCFSAPIFileInfo *fi)\n{\n    if (fi->magic != FCFS_API_MAGIC_NUMBER) {\n        return EBADF;\n    }\n\n    if (fi->sessions.flock.mconn != NULL) {\n        /* force close connection to unlock */\n        fdir_client_close_session(&fi->sessions.flock, true);\n    }\n\n    if (fi->ctx->persist_additional_gids) {\n        if (fi->oper.additional_gids.list != fi->fixed_groups_buff) {\n            free((void *)fi->oper.additional_gids.list);\n            fi->oper.additional_gids.list = NULL;\n        }\n    }\n\n    fi->ctx = NULL;\n    fi->magic = 0;\n    return 0;\n}\n\nstatic int report_size_and_time(FCFSAPIContext *ctx,\n        const FDIRSetDEntrySizeInfo *dsize, FDIRDEntryInfo *dentry)\n{\n    if (ctx->async_report.enabled) {\n        if (dentry != NULL) {\n            if ((dsize->flags & FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE)) {\n                dentry->stat.size = dsize->file_size;\n            }\n            if ((dsize->flags & FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END)) {\n                dentry->stat.space_end = dsize->file_size;\n            }\n            if ((dsize->flags & FDIR_DENTRY_FIELD_MODIFIED_FLAG_MTIME)) {\n                dentry->stat.mtime = get_current_time();\n            }\n        }\n        return async_reporter_push(dsize);\n    } else {\n        FDIRDEntryInfo tmp;\n        if (dentry == NULL) {\n            dentry = &tmp;\n        }\n        return fdir_client_set_dentry_size(ctx->contexts.fdir,\n                &ctx->ns, dsize, dentry);\n    }\n}\n\nstatic int fcfs_api_file_report_size_and_time(\n        FCFSAPIWriteDoneCallbackArg *callback_arg, int *flags,\n        FDIRDEntryInfo *dentry)\n{\n    FDIRSetDEntrySizeInfo dsize;\n    int current_time;\n\n    dsize.file_size = callback_arg->arg.bs_key->block.offset +\n        callback_arg->arg.bs_key->slice.offset +\n        callback_arg->arg.write_bytes;\n    if (dsize.file_size > callback_arg->extra.file_size) {\n        *flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE |\n            FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END;\n    } else {\n        if (dsize.file_size > callback_arg->extra.space_end) {\n            *flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END;\n        } else {\n            *flags = 0;\n        }\n\n        current_time = get_current_time();\n        if (current_time > callback_arg->extra.last_modified_time) {\n            *flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_MTIME;\n        }\n    }\n\n    if (callback_arg->arg.inc_alloc != 0)  {\n        if (!(*flags & (FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE |\n                        FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END)))\n        {\n            *flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE |\n                FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END |\n                FDIR_DENTRY_FIELD_MODIFIED_FLAG_INC_ALLOC;\n        } else {\n            *flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_INC_ALLOC;\n        }\n    }\n\n    if (*flags == 0) {\n        return 0;\n    } else {\n        dsize.inode = callback_arg->arg.bs_key->block.oid;\n        dsize.inc_alloc = callback_arg->arg.inc_alloc;\n        dsize.force = false;\n        dsize.flags = *flags;\n        return report_size_and_time(callback_arg->extra.ctx, &dsize, dentry);\n    }\n}\n\nvoid fcfs_api_file_write_done_callback(FSAPIWriteDoneCallbackArg *callback_arg)\n{\n    int flags;\n    fcfs_api_file_report_size_and_time((FCFSAPIWriteDoneCallbackArg *)\n            callback_arg, &flags, NULL);\n}\n\n/*\nstatic inline void print_block_slice_key(FSBlockSliceKeyInfo *bs_key)\n{\n    logInfo(\"block {oid: %\"PRId64\", offset: %\"PRId64\"}, \"\n            \"slice {offset: %d, length: %d}\", bs_key->block.oid,\n            bs_key->block.offset, bs_key->slice.offset, bs_key->slice.length);\n}\n*/\n\nstatic int do_pwrite(FCFSAPIFileInfo *fi, FSAPIWriteBuffer *wbuffer,\n        const int size, const int64_t offset, int *written_bytes,\n        int *total_inc_alloc, const bool need_report_modified,\n        const int64_t tid)\n{\n    FSAPIOperationContext op_ctx;\n    FCFSAPIWriteDoneCallbackArg callback_arg;\n    SFDynamicIOVArray iova;\n    int64_t new_offset;\n    int loop_flags;\n    int result;\n    int remain;\n\n    FS_API_SET_CTX_AND_TID_EX(op_ctx, fi->ctx->contexts.fsapi, tid);\n    wbuffer->extra_data = &callback_arg.extra;\n    callback_arg.extra.ctx = fi->ctx;\n\n    *total_inc_alloc = *written_bytes = 0;\n    new_offset = offset;\n    fs_set_block_slice(&op_ctx.bs_key, fi->dentry.inode, offset, size);\n\n    loop_flags = 1;\n    if (wbuffer->is_writev) {\n        sf_iova_init(iova, wbuffer->iov, wbuffer->iovcnt);\n        if (op_ctx.bs_key.slice.length < size) {\n            if ((result=sf_iova_first_slice(&iova, op_ctx.\n                            bs_key.slice.length)) != 0)\n            {\n                loop_flags = 0;\n            }\n        }\n    }\n\n    while (loop_flags) {\n        //print_block_slice_key(&op_ctx.bs_key);\n        callback_arg.arg.bs_key = &op_ctx.bs_key;\n        callback_arg.extra.file_size = fi->dentry.stat.size;\n        callback_arg.extra.space_end = fi->dentry.stat.space_end;\n        callback_arg.extra.last_modified_time = fi->write_notify.last_modified_time;\n        if ((result=fs_api_slice_write(&op_ctx, wbuffer, &callback_arg.\n                        arg.write_bytes, &callback_arg.arg.inc_alloc)) != 0)\n        {\n            if (callback_arg.arg.write_bytes == 0) {\n                break;\n            }\n        }\n\n        new_offset += callback_arg.arg.write_bytes;\n        *written_bytes += callback_arg.arg.write_bytes;\n        *total_inc_alloc += callback_arg.arg.inc_alloc;\n        if (need_report_modified) {\n            int flags;\n            fcfs_api_file_report_size_and_time(&callback_arg,\n                    &flags, &fi->dentry);\n            if ((flags & FDIR_DENTRY_FIELD_MODIFIED_FLAG_MTIME) != 0) {\n                fi->write_notify.last_modified_time = get_current_time();\n            }\n        }\n\n        remain = size - *written_bytes;\n        if (remain <= 0) {\n            break;\n        }\n\n        if (callback_arg.arg.write_bytes == op_ctx.bs_key.slice.length) {\n            /* fully completed */\n            fs_next_block_slice_key(&op_ctx.bs_key, remain);\n        } else {  //partially completed, try again the remain part\n            fs_set_slice_size(&op_ctx.bs_key, new_offset, remain);\n        }\n\n        if (wbuffer->is_writev) {\n            if ((result=sf_iova_next_slice(&iova, *written_bytes,\n                            op_ctx.bs_key.slice.length)) != 0)\n            {\n                break;\n            }\n        } else {\n            wbuffer->buff += *written_bytes;\n        }\n    }\n\n    if (wbuffer->is_writev) {\n        sf_iova_destroy(iova);\n    }\n    return (*written_bytes > 0) ? 0 : EIO;\n}\n\nstatic inline int check_writable(FCFSAPIFileInfo *fi)\n{\n    if (fi->magic != FCFS_API_MAGIC_NUMBER || !((fi->flags & O_WRONLY) ||\n                (fi->flags & O_RDWR)))\n    {\n        return EBADF;\n    }\n\n    if (S_ISDIR(fi->dentry.stat.mode)) {\n        return EISDIR;\n    }\n    return (S_ISREG(fi->dentry.stat.mode) ? 0 : EPERM);\n}\n\nstatic inline int check_readable(FCFSAPIFileInfo *fi)\n{\n    if (fi->magic != FCFS_API_MAGIC_NUMBER || (fi->flags & O_WRONLY)) {\n        return EBADF;\n    }\n\n    if (S_ISDIR(fi->dentry.stat.mode)) {\n        return EISDIR;\n    }\n    return (S_ISREG(fi->dentry.stat.mode) ? 0 : EPERM);\n}\n\nstatic int pwrite_wrapper(FCFSAPIFileInfo *fi, FSAPIWriteBuffer *wbuffer,\n        const int size, const int64_t offset, int *written_bytes,\n        const int64_t tid)\n{\n    int total_inc_alloc;\n    int result;\n\n    if (size == 0) {\n        return 0;\n    } else if (size < 0) {\n        return EINVAL;\n    }\n\n    if ((result=check_writable(fi)) != 0) {\n        return result;\n    }\n\n    return do_pwrite(fi, wbuffer, size, offset, written_bytes,\n            &total_inc_alloc, true, tid);\n}\n\nint fcfs_api_pwrite_ex(FCFSAPIFileInfo *fi, const char *buff,\n        const int size, const int64_t offset, int *written_bytes,\n        const int64_t tid)\n{\n    FSAPIWriteBuffer wbuffer;\n\n    FS_API_SET_WBUFFER_BUFF(wbuffer, buff);\n    return pwrite_wrapper(fi, &wbuffer, size, offset, written_bytes, tid);\n}\n\nstatic int do_write(FCFSAPIFileInfo *fi, FSAPIWriteBuffer *wbuffer,\n        const int size, int *written_bytes, const int64_t tid)\n{\n    FDIRClientSession session;\n    FDIRSetDEntrySizeInfo dsize;\n    bool use_sys_lock;\n    int result;\n    int total_inc_alloc;\n    int64_t old_size;\n    int64_t space_end;\n\n    if (size == 0) {\n        return 0;\n    } else if (size < 0) {\n        return EINVAL;\n    }\n\n    if ((result=check_writable(fi)) != 0) {\n        return result;\n    }\n\n    use_sys_lock = fi->ctx->use_sys_lock_for_append &&\n        !fi->ctx->async_report.enabled && (fi->flags & O_APPEND);\n    if (use_sys_lock) {\n        if ((result=fcfs_api_dentry_sys_lock(&session, fi->dentry.inode,\n                        0, &old_size, &space_end)) != 0)\n        {\n            return result;\n        }\n\n        fi->offset = old_size;\n    } else {\n        old_size = fi->dentry.stat.size;\n    }\n\n    if ((result=do_pwrite(fi, wbuffer, size, fi->offset, written_bytes,\n                    &total_inc_alloc, !use_sys_lock, tid)) == 0)\n    {\n        fi->offset += *written_bytes;\n    }\n\n    if (use_sys_lock) {\n        string_t *ns;\n        if (fi->offset > old_size) {\n            ns = &fi->ctx->ns; //set ns for update file_size, space_end etc.\n        } else {\n            ns = NULL;  //do NOT update\n        }\n\n        dsize.inode = fi->dentry.inode;\n        dsize.file_size = fi->offset;\n        dsize.inc_alloc = total_inc_alloc;\n        dsize.force = false;\n        dsize.flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE |\n            FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END;\n        fcfs_api_dentry_sys_unlock(&session, ns, old_size, &dsize);\n    }\n\n    return result;\n}\n\nint fcfs_api_write_ex(FCFSAPIFileInfo *fi, const char *buff,\n        const int size, int *written_bytes, const int64_t tid)\n{\n    FSAPIWriteBuffer wbuffer;\n\n    FS_API_SET_WBUFFER_BUFF(wbuffer, buff);\n    return do_write(fi, &wbuffer, size, written_bytes, tid);\n}\n\nint fcfs_api_pwritev_ex(FCFSAPIFileInfo *fi, const struct iovec *iov,\n        const int iovcnt, const int64_t offset, int *written_bytes,\n        const int64_t tid)\n{\n    FSAPIWriteBuffer wbuffer;\n\n    FS_API_SET_WBUFFER_IOV(wbuffer, iov, iovcnt);\n    return pwrite_wrapper(fi, &wbuffer, fc_iov_get_bytes(iov, iovcnt),\n            offset, written_bytes, tid);\n}\n\nint fcfs_api_writev_ex(FCFSAPIFileInfo *fi, const struct iovec *iov,\n        const int iovcnt, int *written_bytes, const int64_t tid)\n{\n    FSAPIWriteBuffer wbuffer;\n\n    FS_API_SET_WBUFFER_IOV(wbuffer, iov, iovcnt);\n    return do_write(fi, &wbuffer, fc_iov_get_bytes(iov, iovcnt),\n            written_bytes, tid);\n}\n\nstatic int do_pread(FCFSAPIFileInfo *fi, const bool is_readv,\n        const void *data, const int iovcnt, const int size,\n        const int64_t offset, int *read_bytes, const int64_t tid)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    FSAPIOperationContext op_ctx;\n    SFDynamicIOVArray iova;\n    FDIRClientOperInodePair oino;\n    int result;\n    int current_read;\n    int remain;\n    int64_t current_offset;\n    int64_t hole_bytes;\n    int fill_bytes;\n\n    *read_bytes = 0;\n    if (size == 0) {\n        return 0;\n    } else if (size < 0) {\n        return EINVAL;\n    }\n\n    if ((result=check_readable(fi)) != 0) {\n        return result;\n    }\n\n    FS_API_SET_CTX_AND_TID_EX(op_ctx, fi->ctx->contexts.fsapi, tid);\n    fs_set_block_slice(&op_ctx.bs_key, fi->dentry.inode, offset, size);\n    if (is_readv) {\n        sf_iova_init(iova, (struct iovec *)data, iovcnt);\n    }\n    while (1) {\n        //print_block_slice_key(&op_ctx.bs_key);\n        if (is_readv) {\n            result = fs_api_slice_readv(&op_ctx, iova.iov,\n                    iova.cnt, &current_read);\n        } else {\n            result = fs_api_slice_read(&op_ctx, (char *)data +\n                    (*read_bytes), &current_read);\n        }\n\n        if (result != 0) {\n            if (result == ENODATA) {\n                result = 0;\n            } else {\n                break;\n            }\n        }\n\n        while (current_read < op_ctx.bs_key.slice.length) {\n            /* deal file hole caused by ftruncate and lseek */\n            current_offset = offset + *read_bytes + current_read;\n            if (current_offset == fi->dentry.stat.size) {\n                break;\n            }\n\n            if (current_offset > fi->dentry.stat.size) {\n                FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode);\n                if ((result=fcfs_api_stat_dentry_by_inode_ex(fi->ctx,\n                                &oino, flags, &fi->dentry)) != 0)\n                {\n                    break;\n                }\n            }\n\n            hole_bytes = fi->dentry.stat.size - current_offset;\n            if (hole_bytes > 0) {\n                if ((int64_t)current_read + hole_bytes > (int64_t)\n                        op_ctx.bs_key.slice.length)\n                {\n                    fill_bytes = op_ctx.bs_key.slice.length - current_read;\n                } else {\n                    fill_bytes = hole_bytes;\n                }\n\n                /*\n                logInfo(\"=====offset: %\"PRId64\", current_read: %d, \"\n                        \"hole_bytes: %\"PRId64\", fill_bytes: %d, \"\n                        \"buff offset: %d =====\", offset, current_read,\n                        hole_bytes, fill_bytes, *read_bytes + current_read);\n                        */\n\n                if (is_readv) {\n                    if ((result=sf_iova_memset(iova, 0, current_read,\n                                    fill_bytes)) != 0)\n                    {\n                        break;\n                    }\n                } else {\n                    memset((char *)data + (*read_bytes) +\n                            current_read, 0, fill_bytes);\n                }\n                current_read += fill_bytes;\n            }\n\n            break;\n        }\n\n        /*\n        logInfo(\"current read: %d, total read: %d, slice length: %d\",\n                current_read, *read_bytes, op_ctx.bs_key.slice.length);\n                */\n\n        *read_bytes += current_read;\n        remain = size - *read_bytes;\n        if (remain <= 0 || current_read < op_ctx.bs_key.slice.length) {\n            break;\n        }\n\n        if (is_readv) {\n            if ((result=sf_iova_consume(&iova, current_read)) != 0) {\n                break;\n            }\n        }\n        fs_next_block_slice_key(&op_ctx.bs_key, remain);\n    }\n\n    if (is_readv) {\n        sf_iova_destroy(iova);\n    }\n    return result;\n}\n\nint fcfs_api_pread_ex(FCFSAPIFileInfo *fi, char *buff, const int size,\n        const int64_t offset, int *read_bytes, const int64_t tid)\n{\n    const bool is_readv = false;\n    const int iovcnt = 0;\n\n    return do_pread(fi, is_readv, buff, iovcnt,\n            size, offset, read_bytes, tid);\n}\n\nint fcfs_api_read_ex(FCFSAPIFileInfo *fi, char *buff, const int size,\n        int *read_bytes, const int64_t tid)\n{\n    const bool is_readv = false;\n    const int iovcnt = 0;\n    int result;\n\n    if ((result=do_pread(fi, is_readv, buff, iovcnt, size,\n                    fi->offset, read_bytes, tid)) != 0)\n    {\n        return result;\n    }\n\n    fi->offset += *read_bytes;\n    return 0;\n}\n\nint fcfs_api_preadv_ex(FCFSAPIFileInfo *fi, const struct iovec *iov,\n        const int iovcnt, const int64_t offset, int *read_bytes,\n        const int64_t tid)\n{\n    const bool is_readv = true;\n\n    return do_pread(fi, is_readv, iov, iovcnt,\n            fc_iov_get_bytes(iov, iovcnt),\n            offset, read_bytes, tid);\n}\n\nint fcfs_api_readv_ex(FCFSAPIFileInfo *fi, const struct iovec *iov,\n        const int iovcnt, int *read_bytes, const int64_t tid)\n{\n    const bool is_readv = true;\n    int result;\n\n    if ((result=do_pread(fi, is_readv, iov, iovcnt,\n                    fc_iov_get_bytes(iov, iovcnt),\n                    fi->offset, read_bytes, tid)) != 0)\n    {\n        return result;\n    }\n\n    fi->offset += *read_bytes;\n    return 0;\n}\n\nstatic int do_truncate(FCFSAPIContext *ctx, const int64_t oid,\n        const int64_t old_space_end, const int64_t offset,\n        const int64_t length, int64_t *total_dec_alloc, const int64_t tid)\n{\n    FSAPIOperationContext op_ctx;\n    int64_t remain;\n    int dec_alloc;\n    int result;\n\n    *total_dec_alloc = 0;\n    if (offset >= old_space_end) {\n        return 0;\n    }\n\n    if (offset + length > old_space_end) {\n        remain = old_space_end - offset;\n    } else {\n        remain = length;\n    }\n\n    FS_API_SET_CTX_AND_TID_EX(op_ctx, ctx->contexts.fsapi, tid);\n    fs_set_block_slice(&op_ctx.bs_key, oid, offset, remain);\n    while (1) {\n        //print_block_slice_key(&op_ctx.bs_key);\n        if (op_ctx.bs_key.slice.length == FS_FILE_BLOCK_SIZE) {\n            result = fs_api_block_delete(&op_ctx,\n                    &dec_alloc);\n        } else {\n            result = fs_api_slice_delete(&op_ctx,\n                    &dec_alloc);\n        }\n        if (result == 0) {\n            *total_dec_alloc -= dec_alloc;\n        } else if (result == ENOENT) {\n            result = 0;\n        } else if (result != 0) {\n            break;\n        }\n\n        remain -= op_ctx.bs_key.slice.length;\n        if (remain <= 0) {\n            break;\n        }\n\n        fs_next_block_slice_key(&op_ctx.bs_key, remain);\n    }\n\n    return result;\n}\n\nstatic int do_allocate(FCFSAPIContext *ctx, const int64_t oid,\n        const int64_t offset, const int64_t length,\n        int64_t *total_inc_alloc, const int64_t tid)\n{\n    FSAPIOperationContext op_ctx;\n    int64_t remain;\n    int inc_alloc;\n    int result;\n\n    *total_inc_alloc = 0;\n    if (length <= 0) {\n        return 0;\n    }\n\n    FS_API_SET_CTX_AND_TID_EX(op_ctx, ctx->contexts.fsapi, tid);\n    remain = length;\n    fs_set_block_slice(&op_ctx.bs_key, oid, offset, remain);\n    while (1) {\n        //print_block_slice_key(&op_ctx.bs_key);\n        if ((result=fs_api_slice_allocate(&op_ctx, &inc_alloc)) != 0) {\n            break;\n        }\n        *total_inc_alloc += inc_alloc;\n\n        remain -= op_ctx.bs_key.slice.length;\n        if (remain <= 0) {\n            break;\n        }\n\n        fs_next_block_slice_key(&op_ctx.bs_key, remain);\n    }\n\n    return result;\n}\n\nstatic inline int check_and_sys_lock(FCFSAPIContext *ctx,\n        FDIRClientSession *session, const int64_t oid,\n        const FDIRDentryOperator *oper,\n        int64_t *old_size, int64_t *space_end)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    FDIRClientOperInodePair oino;\n    FDIRDEntryInfo dentry;\n    int result;\n\n    if (ctx->use_sys_lock_for_append && !ctx->async_report.enabled) {\n        result = fcfs_api_dentry_sys_lock(session,\n                oid, 0, old_size, space_end);\n    } else {\n        FCFSAPI_SET_OPER_INODE_PAIR(oino, *oper, oid);\n        if ((result=fcfs_api_stat_dentry_by_inode_ex(ctx,\n                        &oino, flags, &dentry)) == 0)\n        {\n            *old_size = dentry.stat.size;\n            *space_end = dentry.stat.space_end;\n        }\n    }\n\n    return result;\n}\n\nstatic inline int check_and_sys_unlock(FCFSAPIContext *ctx,\n        FDIRClientSession *session, const int64_t old_size,\n        const FDIRSetDEntrySizeInfo *dsize, const int result)\n{\n    if (ctx->use_sys_lock_for_append && !ctx->async_report.enabled) {\n        string_t *ns;\n        int unlock_res;\n\n        if (result == 0) {\n            ns = &ctx->ns;  //set ns for update file_size, space_end etc.\n        } else {\n            ns = NULL;  //do NOT update\n        }\n        unlock_res = fcfs_api_dentry_sys_unlock(session, ns, old_size, dsize);\n        return result == 0 ? unlock_res : result;\n    } else {\n        if (result == 0) {\n            return report_size_and_time(ctx, dsize, NULL);\n        } else {\n            return result;\n        }\n    }\n}\n\nstatic int file_truncate(FCFSAPIContext *ctx, const int64_t oid,\n        const int64_t new_size, const FDIRDentryOperator *oper,\n        const int64_t tid)\n{\n    FDIRClientSession session;\n    FDIRSetDEntrySizeInfo dsize;\n    int64_t old_size;\n    int64_t space_end;\n    int result;\n\n    if (new_size < 0) {\n        return EINVAL;\n    }\n\n    if ((result=check_and_sys_lock(ctx, &session, oid,\n                    oper, &old_size, &space_end)) != 0)\n    {\n        return result;\n    }\n\n    dsize.inode = oid;\n    dsize.file_size = new_size;\n    dsize.force = true;\n    if (new_size >= old_size) {\n        result = 0;\n        dsize.inc_alloc = 0;\n        if (new_size == old_size) {\n            dsize.flags = 0;\n        } else {\n            dsize.flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE;\n        }\n    } else {\n        result = do_truncate(ctx, oid, space_end, new_size,\n                old_size - new_size, &dsize.inc_alloc, tid);\n\n        dsize.flags = FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE;\n        if (new_size < space_end) {\n            dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END;\n        }\n        if (dsize.inc_alloc != 0)  {\n            dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_INC_ALLOC;\n        }\n    }\n\n    return check_and_sys_unlock(ctx, &session, old_size, &dsize, result);\n}\n\nint fcfs_api_ftruncate_ex(FCFSAPIFileInfo *fi, const int64_t new_size,\n        const int64_t tid)\n{\n    int result;\n\n    if ((result=check_writable(fi)) != 0) {\n        return result;\n    }\n\n    return file_truncate(fi->ctx, fi->dentry.inode, new_size,\n            &fi->oper, tid);\n}\n\nstatic int get_regular_file_inode(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *fname, int64_t *inode)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    FDIRDEntryInfo dentry;\n    int result;\n\n    if ((result=fcfs_api_stat_dentry_by_fullname_ex(ctx, fname,\n                    flags, LOG_DEBUG, &dentry)) != 0)\n    {\n        return result;\n    }\n    if (S_ISDIR(dentry.stat.mode)) {\n        return EISDIR;\n    }\n\n    *inode = dentry.inode;\n    return 0;\n}\n\nint fcfs_api_truncate_ex(FCFSAPIContext *ctx, const char *path,\n        const int64_t new_size, const FCFSAPIFileContext *fctx)\n{\n    int result;\n    FDIRClientOperFnamePair fname;\n    int64_t inode;\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, fctx->oper, path);\n    if ((result=get_regular_file_inode(ctx, &fname, &inode)) != 0) {\n        return result;\n    }\n\n    return file_truncate(ctx, inode, new_size, &fname.oper, fctx->tid);\n}\n\nint fcfs_api_file_truncate_ex(FCFSAPIContext *ctx,\n       const FDIRClientOperInodePair *oino,\n       const int64_t new_size, const int64_t tid,\n       FDIRDEntryInfo *dentry)\n{\n    const int flags = FDIR_FLAGS_OUTPUT_DENTRY;\n    int result;\n\n    if ((result=fcfs_api_access_dentry_by_inode_ex(ctx,\n                    oino, W_OK, flags, dentry)) != 0)\n    {\n        return result;\n    }\n    if (S_ISDIR(dentry->stat.mode)) {\n        return EISDIR;\n    }\n\n    return file_truncate(ctx, oino->inode, new_size, &oino->oper, tid);\n}\n\n#define calc_file_offset(fi, offset, whence, new_offset)  \\\n    calc_file_offset_ex(fi, offset, whence, false, new_offset)\n\nstatic inline int calc_file_offset_ex(FCFSAPIFileInfo *fi,\n        const int64_t offset, const int whence,\n        const bool refresh_fsize, int64_t *new_offset)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    int result;\n    FDIRClientOperInodePair oino;\n\n    switch (whence) {\n        case SEEK_SET:\n            if (offset < 0) {\n                return EINVAL;\n            }\n            *new_offset = offset;\n            break;\n        case SEEK_CUR:\n            *new_offset = fi->offset + offset;\n            if (*new_offset < 0) {\n                return EINVAL;\n            }\n            break;\n        case SEEK_END:\n            if (refresh_fsize) {\n                FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode);\n                if ((result=fcfs_api_stat_dentry_by_inode_ex(fi->ctx,\n                                &oino, flags, &fi->dentry)) != 0)\n                {\n                    return result;\n                }\n            }\n\n            *new_offset = fi->dentry.stat.size + offset;\n            if (*new_offset < 0) {\n                return EINVAL;\n            }\n            break;\n        default:\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"invalid whence: %d\", __LINE__, whence);\n            return EINVAL;\n    }\n\n    return 0;\n}\n\nint fcfs_api_lseek(FCFSAPIFileInfo *fi, const int64_t offset, const int whence)\n{\n    int64_t new_offset;\n    int result;\n\n    if (fi->magic != FCFS_API_MAGIC_NUMBER) {\n        return EBADF;\n    }\n\n    if ((result=calc_file_offset_ex(fi, offset, whence,\n                    true, &new_offset)) != 0)\n    {\n        return result;\n    }\n\n    fi->offset = new_offset;\n    return 0;\n}\n\nvoid fcfs_api_fill_stat(const FDIRDEntryInfo *dentry, struct stat *stat)\n{\n    stat->st_ino = dentry->inode;\n    stat->st_rdev = dentry->stat.rdev;\n    stat->st_mode = dentry->stat.mode;\n    stat->st_size = dentry->stat.size;\n    stat->st_atime = (uint32_t)dentry->stat.atime;\n    stat->st_mtime = (uint32_t)dentry->stat.mtime;\n    stat->st_ctime = (uint32_t)dentry->stat.ctime;\n    stat->st_uid = dentry->stat.uid;\n    stat->st_gid = dentry->stat.gid;\n    stat->st_nlink = dentry->stat.nlink;\n\n    stat->st_blksize = 512;\n    if (dentry->stat.alloc > 0) {\n        stat->st_blocks = (dentry->stat.alloc + stat->st_blksize - 1) /\n            stat->st_blksize;\n    }\n}\n\nint fcfs_api_fstat(FCFSAPIFileInfo *fi, struct stat *buf)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    int result;\n    FDIRClientOperInodePair oino;\n\n    if (fi->magic != FCFS_API_MAGIC_NUMBER) {\n        return EBADF;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode);\n    if ((result=fcfs_api_stat_dentry_by_inode_ex(fi->ctx,\n                    &oino, flags, &fi->dentry)) != 0)\n    {\n        return result;\n    }\n\n    memset(buf, 0, sizeof(struct stat));\n    fcfs_api_fill_stat(&fi->dentry, buf);\n    return 0;\n}\n\nstatic inline int fapi_stat(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path,\n        struct stat *buf, const int flags)\n{\n    int result;\n    FDIRDEntryInfo dentry;\n\n    if ((result=fcfs_api_stat_dentry_by_fullname_ex(ctx,\n                    path, flags, LOG_DEBUG, &dentry)) != 0)\n    {\n        return result;\n    }\n\n    memset(buf, 0, sizeof(struct stat));\n    fcfs_api_fill_stat(&dentry, buf);\n    return 0;\n}\n\nint fcfs_api_lstat_ex(FCFSAPIContext *ctx, const char *path,\n        const FDIRDentryOperator *oper, struct stat *buf)\n{\n    const int flags = 0;\n    FDIRClientOperFnamePair fname;\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path);\n    return fapi_stat(ctx, &fname, buf, flags);\n}\n\nint fcfs_api_stat_ex(FCFSAPIContext *ctx, const char *path,\n        const FDIRDentryOperator *oper, struct stat *buf,\n        const int flags)\n{\n    FDIRClientOperFnamePair fname;\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path);\n    return fapi_stat(ctx, &fname, buf, flags);\n}\n\nstatic inline int fcntl_lock(FCFSAPIFileInfo *fi, const int operation,\n        const int64_t offset, const int64_t length,\n        const int64_t owner_id, const pid_t pid)\n{\n    int result;\n    FDIRFlockOwner owner;\n    FDIRClientOperInodePair oino;\n\n    if (fi->sessions.flock.mconn == NULL) {\n        if ((result=fdir_client_init_session(fi->ctx->contexts.\n                        fdir, &fi->sessions.flock)) != 0)\n        {\n            return result;\n        }\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode);\n    owner.id = owner_id;\n    owner.pid = pid;\n    if ((result=fdir_client_flock_dentry_ex(&fi->sessions.flock,\n                    &fi->ctx->ns, &oino, operation,\n                    offset, length, &owner)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nstatic inline int fcntl_unlock(FCFSAPIFileInfo *fi, const int operation,\n        const int64_t offset, const int64_t length,\n        const int64_t owner_id, const pid_t pid)\n{\n    int result;\n    FDIRFlockOwner owner;\n    FDIRClientOperInodePair oino;\n\n    if (fi->sessions.flock.mconn == NULL) {\n        return 0;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode);\n    owner.id = owner_id;\n    owner.pid = pid;\n    if ((result=fdir_client_flock_dentry_ex(&fi->sessions.flock,\n            &fi->ctx->ns, &oino, operation,\n            offset, length, &owner)) != 0)\n    {\n        return (result == ENOENT ? 0 : result);\n    }\n\n    return 0;\n}\n\nint fcfs_api_flock_ex2(FCFSAPIFileInfo *fi, const int operation,\n        const int64_t owner_id, const pid_t pid)\n{\n    const int64_t offset = 0;\n    const int64_t length = 0;\n\n    if (fi->magic != FCFS_API_MAGIC_NUMBER) {\n        return EBADF;\n    }\n\n    if ((operation & LOCK_UN)) {\n        return fcntl_unlock(fi, operation, offset, length, owner_id, pid);\n    } else if ((operation & LOCK_SH) || (operation & LOCK_EX)) {\n        return fcntl_lock(fi, operation, offset, length, owner_id, pid);\n    } else {\n        return EINVAL;\n    }\n}\n\nstatic inline int fcntl_type_to_flock_op(const short type, int *operation)\n{\n    switch (type) {\n        case F_RDLCK:\n            *operation = LOCK_SH;\n            break;\n        case F_WRLCK:\n            *operation = LOCK_EX;\n            break;\n        case F_UNLCK:\n            *operation = LOCK_UN;\n            break;\n        default:\n            return EINVAL;\n    }\n\n    return 0;\n}\n\nstatic inline int flock_op_to_fcntl_type(const int operation, short *type)\n{\n    switch (operation) {\n        case LOCK_SH:\n            *type = F_RDLCK;\n            break;\n        case LOCK_EX:\n            *type = F_WRLCK;\n            break;\n        case LOCK_UN:\n            *type = F_UNLCK;\n            break;\n        default:\n            return EINVAL;\n    }\n\n    return 0;\n}\n\nint fcfs_api_setlk_ex(FCFSAPIFileInfo *fi, const struct flock *lock,\n        const int64_t owner_id, const bool blocked)\n{\n    int operation;\n    int result;\n    int64_t offset;\n\n    if (fi->magic != FCFS_API_MAGIC_NUMBER) {\n        return EBADF;\n    }\n\n    if ((result=fcntl_type_to_flock_op(lock->l_type, &operation)) != 0) {\n        return result;\n    }\n\n    if ((result=calc_file_offset(fi, lock->l_start,\n                    lock->l_whence, &offset)) != 0)\n    {\n        return result;\n    }\n\n    if (operation == LOCK_UN) {\n        return fcntl_unlock(fi, operation, offset,\n                lock->l_len, owner_id, lock->l_pid);\n    } else {\n        if (!blocked) {\n            operation |= LOCK_NB;\n        }\n        return fcntl_lock(fi, operation, offset, lock->l_len,\n                owner_id, lock->l_pid);\n    }\n}\n\nint fcfs_api_getlk_ex(FCFSAPIFileInfo *fi,\n        struct flock *lock, int64_t *owner_id)\n{\n    int operation;\n    int result;\n    int64_t offset;\n    int64_t length;\n    FDIRClientOperInodePair oino;\n    FDIRFlockOwner owner;\n\n    if (fi->magic != FCFS_API_MAGIC_NUMBER) {\n        return EBADF;\n    }\n\n    if ((result=fcntl_type_to_flock_op(lock->l_type, &operation)) != 0) {\n        return result;\n    }\n    if (operation == LOCK_UN) {\n        return EINVAL;\n    }\n\n    if ((result=calc_file_offset(fi, lock->l_start,\n                    lock->l_whence, &offset)) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, fi->oper, fi->dentry.inode);\n    length = lock->l_len;\n    owner.id = *owner_id;\n    owner.pid = lock->l_pid;\n    if ((result=fdir_client_getlk_dentry(fi->ctx->contexts.fdir,\n                    &fi->ctx->ns, &oino, &operation,\n                    &offset, &length, &owner)) == 0)\n    {\n        flock_op_to_fcntl_type(operation, &lock->l_type);\n        lock->l_whence = SEEK_SET;\n        lock->l_start = offset;\n        lock->l_len = length;\n    }\n\n    return result;\n}\n\nint fcfs_api_fallocate_ex(FCFSAPIFileInfo *fi, const int mode,\n        const int64_t offset, const int64_t length, const int64_t tid)\n{\n    FDIRClientSession session;\n    FDIRSetDEntrySizeInfo dsize;\n    int64_t old_size;\n    int64_t space_end;\n    int op;\n    int result;\n\n    if (offset < 0 || length < 0) {\n        return EINVAL;\n    }\n\n    if ((result=check_writable(fi)) != 0) {\n        return result;\n    }\n\n    op = mode & (~FALLOC_FL_KEEP_SIZE);\n    if (!(op == 0 || op == FALLOC_FL_PUNCH_HOLE)) {\n        return EOPNOTSUPP;\n    }\n\n    if (length == 0) {\n        return 0;\n    }\n\n    if (fi->ctx->use_sys_lock_for_append && !fi->ctx->async_report.enabled) {\n        if ((result=fcfs_api_dentry_sys_lock(&session, fi->dentry.\n                        inode, 0, &old_size, &space_end)) != 0)\n        {\n            return result;\n        }\n    } else {\n        old_size = fi->dentry.stat.size;\n        space_end = fi->dentry.stat.space_end;\n    }\n\n    dsize.inode = fi->dentry.inode;\n    dsize.force = true;\n    dsize.flags = 0;\n    if (op == 0) {   //allocate space\n        result = do_allocate(fi->ctx, fi->dentry.inode,\n                offset, length, &dsize.inc_alloc, tid);\n        dsize.file_size = offset + length;\n        if (dsize.file_size > space_end) {\n            dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END;\n        }\n        if (dsize.file_size > old_size) {\n            dsize.flags |= (mode & FALLOC_FL_KEEP_SIZE) ? 0 :\n                FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE;\n        }\n    } else {  //deallocate space\n        result = do_truncate(fi->ctx, fi->dentry.inode, space_end,\n                offset, length, &dsize.inc_alloc, tid);\n        if (offset + length >= old_size) {\n            dsize.file_size = offset;\n            dsize.flags |= (mode & FALLOC_FL_KEEP_SIZE) ? 0 :\n                FDIR_DENTRY_FIELD_MODIFIED_FLAG_FILE_SIZE;\n            if (dsize.file_size < space_end) {\n                dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END;\n            }\n        } else {\n            if (offset < space_end && offset + length >= space_end) {\n                dsize.file_size = offset;\n                dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_SPACE_END;\n            } else {\n                dsize.file_size = old_size;\n            }\n        }\n    }\n    if (dsize.inc_alloc != 0)  {\n        dsize.flags |= FDIR_DENTRY_FIELD_MODIFIED_FLAG_INC_ALLOC;\n    }\n    return check_and_sys_unlock(fi->ctx, &session, old_size, &dsize, result);\n}\n\nint fcfs_api_rename_ex(FCFSAPIContext *ctx, const char *old_path,\n        const char *new_path, const int flags,\n        const FCFSAPIFileContext *fctx)\n{\n    FDIRDEntryFullName src_fullname;\n    FDIRDEntryFullName dest_fullname;\n    FDIRDEntryInfo dentry;\n    FDIRDEntryInfo *pe;\n    int result;\n\n    src_fullname.ns = ctx->ns;\n    FC_SET_STRING(src_fullname.path, (char *)old_path);\n    dest_fullname.ns = ctx->ns;\n    FC_SET_STRING(dest_fullname.path, (char *)new_path);\n\n    pe = &dentry;\n    if ((result=fdir_client_rename_dentry_ex(ctx->contexts.fdir,\n                    &src_fullname, &dest_fullname, &fctx->oper,\n                    flags, &pe)) != 0)\n    {\n        return result;\n    }\n\n    if (pe != NULL && S_ISREG(pe->stat.mode) && pe->stat.nlink == 0\n            && !ctx->contexts.fdir->trash_bin_enabled)\n    {\n        fs_api_unlink_file(ctx->contexts.fsapi, pe->inode,\n                pe->stat.space_end, fctx->tid);\n    }\n\n    return result;\n}\n\nint fcfs_api_symlink_ex(FCFSAPIContext *ctx, const char *target,\n        const char *path, const FDIRDentryOperator *oper,\n        const mode_t mode)\n{\n    FDIRClientOperFnamePair fname;\n    string_t link;\n    FDIRDEntryInfo dentry;\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path);\n    FC_SET_STRING(link, (char *)target);\n    return fdir_client_symlink_dentry(ctx->contexts.fdir,\n            &link, &fname, mode, &dentry);\n}\n\nint fcfs_api_readlink(FCFSAPIContext *ctx, const char *path,\n        const FDIRDentryOperator *oper, char *buff, const int size)\n{\n    FDIRClientOperFnamePair fname;\n    string_t link;\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path);\n    link.str = buff;\n    return fdir_client_readlink_by_path(ctx->contexts.fdir,\n        &fname, &link, size);\n}\n\nint fcfs_api_link_ex(FCFSAPIContext *ctx, const char *old_path,\n        const char *new_path, const FDIRDentryOperator *oper,\n        const mode_t mode, const int flags)\n{\n    FDIRDEntryFullName src_fullname;\n    FDIRDEntryFullName dest_fullname;\n    FDIRDEntryInfo dentry;\n\n    src_fullname.ns = ctx->ns;\n    FC_SET_STRING(src_fullname.path, (char *)old_path);\n    dest_fullname.ns = ctx->ns;\n    FC_SET_STRING(dest_fullname.path, (char *)new_path);\n\n    return fdir_client_link_dentry(ctx->contexts.fdir, &src_fullname,\n            &dest_fullname, oper, mode, flags, &dentry);\n}\n\nint fcfs_api_mknod_ex(FCFSAPIContext *ctx, const char *path,\n        const FDIRDentryOperator *oper, const mode_t mode,\n        const dev_t dev)\n{\n    FDIRClientOperFnamePair fname;\n    FDIRDEntryInfo dentry;\n\n    if (!(S_ISCHR(mode) || S_ISBLK(mode))) {\n        return EINVAL;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path);\n    return fdir_client_create_dentry_ex(ctx->contexts.fdir,\n            &fname, mode, dev, &dentry);\n}\n\nstatic inline int do_make_dentry(FCFSAPIContext *ctx, const char *path,\n        FDIRDentryOperator *oper, const mode_t mode, const int mtype)\n{\n    FDIRClientOperFnamePair fname;\n    FDIRDEntryInfo dentry;\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path);\n    return fdir_client_create_dentry(ctx->contexts.fdir,\n            &fname, ((mode & (~S_IFMT)) | mtype), &dentry);\n}\n\nint fcfs_api_mkfifo_ex(FCFSAPIContext *ctx, const char *path,\n        FDIRDentryOperator *oper, const mode_t mode)\n{\n    return do_make_dentry(ctx, path, oper, mode, S_IFIFO);\n}\n\nint fcfs_api_mkdir_ex(FCFSAPIContext *ctx, const char *path,\n        FDIRDentryOperator *oper, const mode_t mode)\n{\n    return do_make_dentry(ctx, path, oper, mode, S_IFDIR);\n}\n\nint fcfs_api_statvfs_ex(FCFSAPIContext *ctx, const char *path,\n        struct statvfs *stbuf)\n{\n    int result;\n    FCFSAuthClientFullContext *auth;\n    int64_t quota;\n    int64_t total;\n    int64_t avail;\n    FDIRClientNamespaceStat nstat;\n\n    if (ctx->contexts.fdir->auth.enabled) {\n        auth = &ctx->contexts.fdir->auth;\n    } else if (ctx->contexts.fsapi->fs->auth.enabled) {\n        auth = &ctx->contexts.fsapi->fs->auth;\n    } else {\n        auth = NULL;\n    }\n\n    if (auth != NULL) {\n        if ((result=fcfs_auth_client_spool_get_quota(\n                        auth->ctx, &ctx->ns, &quota)) != 0)\n        {\n            return result;\n        }\n    } else {\n        quota = FCFS_AUTH_UNLIMITED_QUOTA_VAL;\n    }\n\n    if ((result=fdir_client_namespace_stat(ctx->contexts.\n                    fdir, &ctx->ns, &nstat)) != 0)\n    {\n        return result;\n    }\n\n    if (quota == FCFS_AUTH_UNLIMITED_QUOTA_VAL) {\n        FSClusterSpaceStat sstat;\n        if ((result=fs_api_cluster_space_stat(ctx->\n                        contexts.fsapi, &sstat)) != 0)\n        {\n            return result;\n        }\n\n        total = sstat.total;\n        avail = total - sstat.used;\n    } else {\n        total = quota;\n        avail = total - nstat.space.used;\n    }\n    if (avail < 0) {\n        avail = 0;\n    }\n\n    stbuf->f_bsize = stbuf->f_frsize = 512;\n    stbuf->f_blocks = total / stbuf->f_frsize;\n    stbuf->f_bavail = stbuf->f_bfree = avail / stbuf->f_frsize;\n\n    stbuf->f_files = nstat.inode.total;\n    stbuf->f_ffree = nstat.inode.total - nstat.inode.used;\n    stbuf->f_favail = nstat.inode.avail;\n\n    stbuf->f_namemax = NAME_MAX;\n    stbuf->f_fsid = 0;\n    stbuf->f_flag = 0;\n    return 0;\n}\n\nint fcfs_api_access_ex(FCFSAPIContext *ctx, const char *path,\n        const int mask, const FDIRDentryOperator *oper,\n        const int flags)\n{\n    FDIRClientOperFnamePair fname;\n    FDIRDEntryInfo dentry;\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path);\n    return fcfs_api_access_dentry_by_path_ex(ctx,\n            &fname, mask, flags, &dentry);\n}\n\nint fcfs_api_euidaccess_ex(FCFSAPIContext *ctx, const char *path,\n        const int mask, const FDIRDentryOperator *oper,\n        const int flags)\n{\n    FDIRClientOperFnamePair fname;\n    FDIRDEntryInfo dentry;\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path);\n    return fcfs_api_access_dentry_by_path_ex(ctx,\n            &fname, mask, flags, &dentry);\n}\n\nint fcfs_api_set_file_flags(FCFSAPIFileInfo *fi, const int flags)\n{\n    /*\n    if (flags & (O_ASYNC | O_DIRECT | O_NOATIME | O_NONBLOCK)) {\n        logInfo(\"set flags: %d, is O_DIRECT: %d, is O_NOATIME: %d, \"\n                \"is O_NONBLOCK: %d\", flags, (flags & O_DIRECT),\n                (flags & O_NOATIME), (flags & O_NONBLOCK));\n    }\n    */\n\n    if ((flags & O_APPEND) == 0) {\n        return 0;\n    }\n\n    if (!((fi->flags & O_WRONLY) || (fi->flags & O_RDWR))) {\n        return EBADF;\n    }\n\n    fi->flags |= O_APPEND;\n    fi->offset = fi->dentry.stat.size;\n    return 0;\n}\n"
  },
  {
    "path": "src/api/fcfs_api_file.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_API_FILE_H\n#define _FCFS_API_FILE_H\n\n#include <sys/types.h>\n#include <sys/statvfs.h>\n#include <sys/stat.h>\n#include <limits.h>\n#include <fcntl.h>\n#include <utime.h>\n#include \"fcfs_api_types.h\"\n#include \"fcfs_api_util.h\"\n\n#ifndef FALLOC_FL_KEEP_SIZE\n#define FALLOC_FL_KEEP_SIZE  0x01\n#endif\n\n#ifndef FALLOC_FL_PUNCH_HOLE\n#define FALLOC_FL_PUNCH_HOLE 0x02\n#endif\n\n#ifdef O_SYMLINK\n#define FCFS_API_NOFOLLOW_SYMLINK_FLAGS  (O_NOFOLLOW | O_SYMLINK)\n#else\n#define FCFS_API_NOFOLLOW_SYMLINK_FLAGS  (O_NOFOLLOW)\n#endif\n\n#define FCFS_API_GET_ACCESS_FLAGS(open_flags) \\\n    ((((open_flags) & FCFS_API_NOFOLLOW_SYMLINK_FLAGS) != 0) ? \\\n     FDIR_FLAGS_OUTPUT_DENTRY : (FDIR_FLAGS_FOLLOW_SYMLINK | \\\n         FDIR_FLAGS_OUTPUT_DENTRY))\n\n#define FCFS_API_GET_ACCESS_MASK(open_flags) \\\n    (((open_flags) & O_WRONLY) ? W_OK : (((open_flags) & O_RDWR) ? \\\n        (R_OK | W_OK) : R_OK))\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define fcfs_api_create(fi, path, fctx) \\\n    fcfs_api_create_ex(&g_fcfs_api_ctx, fi, path, fctx)\n\n#define fcfs_api_open(fi, path, flags, fctx) \\\n    fcfs_api_open_ex(&g_fcfs_api_ctx, fi, path, flags, fctx)\n\n#define fcfs_api_open_by_inode(fi, inode, flags, fctx) \\\n    fcfs_api_open_by_inode_ex(&g_fcfs_api_ctx, fi, inode, flags, fctx)\n\n#define fcfs_api_open_by_dentry(fi, dentry, flags, fctx) \\\n    fcfs_api_open_by_dentry_ex(&g_fcfs_api_ctx, fi, dentry, flags, fctx)\n\n#define fcfs_api_write(fi, buff, size, written_bytes)  \\\n    fcfs_api_write_ex(fi, buff, size, written_bytes, (fi)->tid)\n\n#define fcfs_api_pwrite(fi, buff, size, offset, written_bytes)  \\\n    fcfs_api_pwrite_ex(fi, buff, size, offset, written_bytes, (fi)->tid)\n\n#define fcfs_api_read(fi, buff, size, read_bytes)  \\\n    fcfs_api_read_ex(fi, buff, size, read_bytes, (fi)->tid)\n\n#define fcfs_api_pread(fi, buff, size, offset, read_bytes)  \\\n    fcfs_api_pread_ex(fi, buff, size, offset, read_bytes, (fi)->tid)\n\n#define fcfs_api_writev(fi, iov, iovcnt, written_bytes)  \\\n    fcfs_api_writev_ex(fi, iov, iovcnt, written_bytes, (fi)->tid)\n\n#define fcfs_api_pwritev(fi, iov, iovcnt, offset, written_bytes)  \\\n    fcfs_api_pwritev_ex(fi, iov, iovcnt, offset, written_bytes, (fi)->tid)\n\n#define fcfs_api_readv(fi, iov, iovcnt, read_bytes)  \\\n    fcfs_api_readv_ex(fi, iov, iovcnt, read_bytes, (fi)->tid)\n\n#define fcfs_api_preadv(fi, iov, iovcnt, offset, read_bytes)  \\\n    fcfs_api_preadv_ex(fi, iov, iovcnt, offset, read_bytes, (fi)->tid)\n\n#define fcfs_api_ftruncate(fi, new_size) \\\n    fcfs_api_ftruncate_ex(fi, new_size, getpid())\n\n#define fcfs_api_truncate(path, new_size, fctx) \\\n    fcfs_api_truncate_ex(&g_fcfs_api_ctx, path, new_size, fctx)\n\n#define fcfs_api_file_truncate(oino, new_size, tid, dentry) \\\n    fcfs_api_file_truncate_ex(&g_fcfs_api_ctx, oino, new_size, tid, dentry)\n\n#define fcfs_api_unlink(path, tid)  \\\n    fcfs_api_unlink_ex(&g_fcfs_api_ctx, path, tid)\n\n#define fcfs_api_stat(path, oper, buf, flags)  \\\n    fcfs_api_stat_ex(&g_fcfs_api_ctx, path, oper, buf, flags)\n\n#define fcfs_api_lstat(path, oper, buf)  \\\n    fcfs_api_lstat_ex(&g_fcfs_api_ctx, path, oper, buf)\n\n#define fcfs_api_rename(old_path, new_path, fctx)  \\\n    fcfs_api_rename_ex(&g_fcfs_api_ctx, old_path, new_path, 0, fctx)\n\n#define fcfs_api_statvfs(path, stbuf)  \\\n    fcfs_api_statvfs_ex(&g_fcfs_api_ctx, path, stbuf)\n\n    int fcfs_api_open_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi,\n            const char *path, const int flags,\n            const FCFSAPIFileContext *fctx);\n\n    int fcfs_api_open_by_inode_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi,\n            const int64_t inode, const int flags,\n            const FCFSAPIFileContext *fctx);\n\n    int fcfs_api_open_by_dentry_ex(FCFSAPIContext *ctx, FCFSAPIFileInfo *fi,\n            const FDIRDEntryInfo *dentry, const int flags,\n            const FCFSAPIFileContext *fctx);\n\n    static inline int fcfs_api_create_ex(FCFSAPIContext *ctx,\n            FCFSAPIFileInfo *fi, const char *path,\n            const FCFSAPIFileContext *fctx)\n    {\n        const int flags = O_CREAT | O_TRUNC | O_WRONLY;\n        return fcfs_api_open_ex(ctx, fi, path, flags, fctx);\n    }\n\n    int fcfs_api_close(FCFSAPIFileInfo *fi);\n\n    void fcfs_api_file_write_done_callback(\n            FSAPIWriteDoneCallbackArg *callback_arg);\n\n    int fcfs_api_pwrite_ex(FCFSAPIFileInfo *fi, const char *buff,\n            const int size, const int64_t offset, int *written_bytes,\n            const int64_t tid);\n\n    int fcfs_api_write_ex(FCFSAPIFileInfo *fi, const char *buff,\n            const int size, int *written_bytes, const int64_t tid);\n\n    int fcfs_api_pread_ex(FCFSAPIFileInfo *fi, char *buff, const int size,\n            const int64_t offset, int *read_bytes, const int64_t tid);\n\n    int fcfs_api_read_ex(FCFSAPIFileInfo *fi, char *buff,\n            const int size, int *read_bytes, const int64_t tid);\n\n    int fcfs_api_pwritev_ex(FCFSAPIFileInfo *fi, const struct iovec *iov,\n            const int iovcnt, const int64_t offset, int *written_bytes,\n            const int64_t tid);\n\n    int fcfs_api_writev_ex(FCFSAPIFileInfo *fi, const struct iovec *iov,\n            const int iovcnt, int *written_bytes, const int64_t tid);\n\n    int fcfs_api_preadv_ex(FCFSAPIFileInfo *fi, const struct iovec *iov,\n            const int iovcnt, const int64_t offset, int *read_bytes,\n            const int64_t tid);\n\n    int fcfs_api_readv_ex(FCFSAPIFileInfo *fi, const struct iovec *iov,\n            const int iovcnt, int *read_bytes, const int64_t tid);\n\n    int fcfs_api_file_truncate_ex(FCFSAPIContext *ctx,\n            const FDIRClientOperInodePair *oino,\n            const int64_t new_size, const int64_t tid,\n            FDIRDEntryInfo *dentry);\n\n    int fcfs_api_ftruncate_ex(FCFSAPIFileInfo *fi, const int64_t new_size,\n            const int64_t tid);\n\n    int fcfs_api_truncate_ex(FCFSAPIContext *ctx, const char *path,\n            const int64_t new_size, const FCFSAPIFileContext *fctx);\n\n    int fcfs_api_fallocate_ex(FCFSAPIFileInfo *fi, const int mode,\n            const int64_t offset, const int64_t len, const int64_t tid);\n\n    static inline int fcfs_api_unlink_ex(FCFSAPIContext *ctx,\n            const FDIRClientOperFnamePair *path, const int64_t tid)\n    {\n        const int flags = FDIR_UNLINK_FLAGS_MATCH_FILE;\n        return fcfs_api_remove_dentry_ex(ctx, path, flags, tid);\n    }\n\n    int fcfs_api_lseek(FCFSAPIFileInfo *fi, const int64_t offset,\n            const int whence);\n\n    void fcfs_api_fill_stat(const FDIRDEntryInfo *dentry, struct stat *stat);\n\n    int fcfs_api_fstat(FCFSAPIFileInfo *fi, struct stat *buf);\n\n    int fcfs_api_lstat_ex(FCFSAPIContext *ctx, const char *path,\n            const FDIRDentryOperator *oper, struct stat *buf);\n\n    int fcfs_api_stat_ex(FCFSAPIContext *ctx, const char *path,\n            const FDIRDentryOperator *oper, struct stat *buf,\n            const int flags);\n\n    int fcfs_api_flock_ex2(FCFSAPIFileInfo *fi, const int operation,\n            const int64_t owner_id, const pid_t pid);\n\n    static inline int fcfs_api_flock_ex(FCFSAPIFileInfo *fi,\n            const int operation, const int64_t owner_id)\n    {\n        return fcfs_api_flock_ex2(fi, operation, owner_id, getpid());\n    }\n\n    static inline int fcfs_api_flock(FCFSAPIFileInfo *fi, const int operation)\n    {\n        return fcfs_api_flock_ex2(fi, operation,\n                (long)pthread_self(), getpid());\n    }\n\n    int fcfs_api_getlk_ex(FCFSAPIFileInfo *fi, struct flock *lock,\n            int64_t *owner_id);\n\n    static inline int fcfs_api_getlk(FCFSAPIFileInfo *fi, struct flock *lock)\n    {\n        int64_t owner_id;\n        return fcfs_api_getlk_ex(fi, lock, &owner_id);\n    }\n\n    int fcfs_api_setlk_ex(FCFSAPIFileInfo *fi, const struct flock *lock,\n            const int64_t owner_id, const bool blocked);\n\n    static inline int fcfs_api_setlk(FCFSAPIFileInfo *fi,\n            const struct flock *lock)\n    {\n        const bool blocked = false;\n        int64_t owner_id;\n\n        owner_id = fc_gettid();\n        return fcfs_api_setlk_ex(fi, lock, owner_id, blocked);\n    }\n\n    static inline int fcfs_api_setlkw(FCFSAPIFileInfo *fi,\n            const struct flock *lock)\n    {\n        const bool blocked = true;\n        int64_t owner_id;\n\n        owner_id = fc_gettid();\n        return fcfs_api_setlk_ex(fi, lock, owner_id, blocked);\n    }\n\n    int fcfs_api_rename_ex(FCFSAPIContext *ctx, const char *old_path,\n            const char *new_path, const int flags,\n            const FCFSAPIFileContext *fctx);\n\n    int fcfs_api_symlink_ex(FCFSAPIContext *ctx, const char *target,\n            const char *path, const FDIRDentryOperator *oper,\n            const mode_t mode);\n\n    int fcfs_api_readlink(FCFSAPIContext *ctx, const char *path,\n            const FDIRDentryOperator *oper, char *buff, const int size);\n\n    int fcfs_api_link_ex(FCFSAPIContext *ctx, const char *old_path,\n            const char *new_path, const FDIRDentryOperator *oper,\n            const mode_t mode, const int flags);\n\n    int fcfs_api_mknod_ex(FCFSAPIContext *ctx, const char *path,\n            const FDIRDentryOperator *oper, const mode_t mode,\n            const dev_t dev);\n\n    int fcfs_api_mkfifo_ex(FCFSAPIContext *ctx, const char *path,\n            FDIRDentryOperator *oper, const mode_t mode);\n\n    int fcfs_api_mkdir_ex(FCFSAPIContext *ctx, const char *path,\n            FDIRDentryOperator *oper, const mode_t mode);\n\n    int fcfs_api_access_ex(FCFSAPIContext *ctx, const char *path,\n            const int mask, const FDIRDentryOperator *oper,\n            const int flags);\n\n    int fcfs_api_euidaccess_ex(FCFSAPIContext *ctx, const char *path,\n            const int mask, const FDIRDentryOperator *oper,\n            const int flags);\n\n    static inline int fcfs_api_eaccess_ex(FCFSAPIContext *ctx,\n            const char *path, const int mask,\n            const FDIRDentryOperator *oper,\n            const int flags)\n    {\n        return fcfs_api_euidaccess_ex(ctx, path, mask, oper, flags);\n    }\n\n    int fcfs_api_statvfs_ex(FCFSAPIContext *ctx, const char *path,\n            struct statvfs *stbuf);\n\n    int fcfs_api_set_file_flags(FCFSAPIFileInfo *fi, const int flags);\n\n    static inline int fcfs_api_fdatasync(FCFSAPIFileInfo *fi,\n            const int64_t tid)\n    {\n        fs_api_datasync(fi->ctx->contexts.fsapi, fi->dentry.inode, tid);\n        return 0;\n    }\n\n    static inline int fcfs_api_fsync(FCFSAPIFileInfo *fi, const int64_t tid)\n    {\n        fs_api_datasync(fi->ctx->contexts.fsapi, fi->dentry.inode, tid);\n        if (fi->ctx->async_report.enabled) {\n            inode_htable_check_conflict_and_wait(fi->dentry.inode);\n        }\n        return 0;\n    }\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/fcfs_api_types.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_API_TYPES_H\n#define _FCFS_API_TYPES_H\n\n#include <sys/types.h>\n#include <limits.h>\n#include <fcntl.h>\n#include \"fastcommon/fast_mblock.h\"\n#include \"fastcommon/fast_buffer.h\"\n#include \"fastdir/client/fdir_client.h\"\n#include \"fastsore/api/fs_api.h\"\n\n#define FCFS_FUSE_DEFAULT_CONFIG_FILENAME \"/etc/fastcfs/fcfs/fuse.conf\"\n\ntypedef enum {\n    fcfs_api_owner_type_caller,\n    fcfs_api_owner_type_fixed\n} FCFSAPIOwnerType;\n\ntypedef struct fcfs_api_owner_info {\n    FCFSAPIOwnerType type;\n    FDIRDentryOperator oper;\n} FCFSAPIOwnerInfo;\n\ntypedef struct fcfs_api_ns_mountpoint_holder {\n    char *ns;\n    char *mountpoint;\n} FCFSAPINSMountpointHolder;\n\ntypedef struct fcfs_api_opendir_session {\n    FDIRClientDentryArray array;\n    int btype;   //buffer type\n    FastBuffer buffer;\n} FCFSAPIOpendirSession;\n\ntypedef struct fcfs_api_context {\n    /* whether FCFSAPIFileInfo object persist additional gids */\n    bool persist_additional_gids;\n    bool use_sys_lock_for_append;\n    struct {\n        bool enabled;\n        bool busy_polling;  //for RDMA\n    } rdma;\n\n    struct {\n        bool enabled;\n        int interval_ms;\n        int shared_allocator_count;\n        int hashtable_sharding_count;\n        int64_t hashtable_total_capacity;\n    } async_report;\n\n    string_t ns;  //namespace\n    char ns_holder[NAME_MAX];\n    FCFSAPIOwnerInfo owner;\n\n    struct {\n        FDIRClientContext *fdir;\n        FSAPIContext *fsapi;\n    } contexts;\n\n    struct fast_mblock_man opendir_session_pool;\n} FCFSAPIContext;\n\ntypedef struct fcfs_api_file_info {\n    FCFSAPIContext *ctx;\n    FDIRDentryOperator oper;\n    int64_t tid;\n    struct {\n        FDIRClientSession flock;\n        FCFSAPIOpendirSession *opendir;\n    } sessions;\n    FDIRDEntryInfo dentry;\n    int flags;\n    int magic;\n    struct {\n        int last_modified_time;\n    } write_notify;\n    int64_t offset;  //current offset\n    char fixed_groups_buff[256];  //for additional gids\n} FCFSAPIFileInfo;\n\ntypedef struct fcfs_api_file_context {\n    FDIRDentryOperator oper;\n    mode_t mode;\n    int64_t tid;\n} FCFSAPIFileContext;\n\ntypedef struct fcfs_api_write_done_callback_extra_data {\n    FCFSAPIContext *ctx;\n    int64_t file_size;\n    int64_t space_end;\n    int last_modified_time;\n} FCFSAPIWriteDoneCallbackExtraData;\n\ntypedef struct fcfs_api_write_done_callback_arg {\n    FSAPIWriteDoneCallbackArg arg;  //must be the first\n    FCFSAPIWriteDoneCallbackExtraData extra;\n} FCFSAPIWriteDoneCallbackArg;\n\nstruct fcfs_api_inode_hentry;\nstruct fcfs_api_insert_event_context;\n\ntypedef struct fcfs_api_waiting_task {\n    pthread_lock_cond_pair_t lcp;  //for notify\n    bool finished;\n    struct fast_mblock_man *allocator;  //for free\n    struct fcfs_api_waiting_task *next; //for event waitings queue\n} FCFSAPIWaitingTask;\n\ntypedef enum {\n    fcfs_api_event_type_report,\n    fcfs_api_event_type_notify\n} FCFSAPIEventType;\n\ntypedef struct fcfs_api_async_report_event {\n    FCFSAPIEventType type;\n    int id;   //used by async_reporter for stable sort\n    FDIRSetDEntrySizeInfo dsize;\n    struct fcfs_api_inode_hentry *inode_hentry;\n    struct {\n        FCFSAPIWaitingTask *head; //use lock of inode sharding\n    } waitings;\n    struct fc_list_head dlink;  //for inode sharding htable\n\n    struct fast_mblock_man *allocator;  //for free\n    struct fcfs_api_async_report_event *next; //for async_reporter's queue\n} FCFSAPIAsyncReportEvent;\n\ntypedef struct fcfs_api_async_report_event_ptr_array {\n    int alloc;\n    int count;\n    FCFSAPIAsyncReportEvent **events;\n} FCFSAPIAsyncReportEventPtrArray;\n\n\n#define FCFS_API_SET_OPERATOR(_oper, _owner, _uid, _gid) \\\n    do {  \\\n        if ((_owner).type == fcfs_api_owner_type_fixed) { \\\n            _oper = (_owner).oper; \\\n        } else {  \\\n            FDIR_SET_OPERATOR(_oper, _uid, _gid, 0, NULL); \\\n        }  \\\n    } while (0)\n\n#define FCFS_API_SET_FCTX(fctx, _oper, _mode, _tid) \\\n    fctx.oper = _oper; \\\n    fctx.mode = _mode; \\\n    fctx.tid = _tid\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern FCFSAPIContext g_fcfs_api_ctx;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/fcfs_api_util.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"fcfs_api_util.h\"\n\nint fcfs_api_remove_dentry_by_pname_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperPnamePair *opname,\n        const int flags, const int64_t tid)\n{\n    FDIRDEntryInfo dentry;\n    int result;\n\n    if ((result=fdir_client_remove_dentry_by_pname_ex(\n            ctx->contexts.fdir, &ctx->ns, opname,\n            flags, &dentry)) != 0)\n    {\n        return result;\n    }\n\n    if (S_ISREG(dentry.stat.mode) && dentry.stat.nlink == 0\n            && !ctx->contexts.fdir->trash_bin_enabled)\n    {\n        result = fs_api_unlink_file(ctx->contexts.fsapi,\n                dentry.inode, dentry.stat.space_end, tid);\n    }\n    return result;\n}\n\nint fcfs_api_remove_dentry_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path,\n        const int flags, const int64_t tid)\n{\n    FDIRDEntryInfo dentry;\n    int result;\n\n    if ((result=fdir_client_remove_dentry_ex(ctx->contexts.fdir,\n                    path, flags, &dentry)) != 0)\n    {\n        return result;\n    }\n\n    if (S_ISREG(dentry.stat.mode) && dentry.stat.nlink == 0\n            && !ctx->contexts.fdir->trash_bin_enabled)\n    {\n        result = fs_api_unlink_file(ctx->contexts.fsapi,\n                dentry.inode, dentry.stat.space_end, tid);\n    }\n    return result;\n}\n\nint fcfs_api_rename_dentry_by_pname_ex(FCFSAPIContext *ctx,\n        const int64_t src_parent_inode, const string_t *src_name,\n        const int64_t dest_parent_inode, const string_t *dest_name,\n        const FDIRDentryOperator *oper, const int flags, const int64_t tid)\n{\n    FDIRDEntryPName src_pname;\n    FDIRDEntryPName dest_pname;\n    FDIRDEntryInfo dentry;\n    FDIRDEntryInfo *pe;\n    int result;\n\n    FDIR_SET_DENTRY_PNAME_PTR(&src_pname, src_parent_inode, src_name);\n    FDIR_SET_DENTRY_PNAME_PTR(&dest_pname, dest_parent_inode, dest_name);\n    pe = &dentry;\n    if ((result=fdir_client_rename_dentry_by_pname_ex(ctx->contexts.fdir,\n                    &ctx->ns, &src_pname, &ctx->ns, &dest_pname, oper,\n                    flags, &pe)) != 0)\n    {\n        return result;\n    }\n\n    if (pe != NULL && S_ISREG(pe->stat.mode) && pe->stat.nlink == 0\n            && !ctx->contexts.fdir->trash_bin_enabled)\n    {\n        fs_api_unlink_file(ctx->contexts.fsapi, pe->inode,\n                pe->stat.space_end, tid);\n    }\n    return result;\n}\n\nint fcfs_api_rename_dentry_ex(FCFSAPIContext *ctx, const char *path1,\n        const char *path2, const FDIRDentryOperator *oper,\n        const int flags, const int64_t tid)\n{\n    FDIRDEntryFullName src;\n    FDIRDEntryFullName dest;\n    FDIRDEntryInfo dentry;\n    FDIRDEntryInfo *pe;\n    int result;\n\n    FCFSAPI_SET_PATH_FULLNAME(src, ctx, path1);\n    FCFSAPI_SET_PATH_FULLNAME(dest, ctx, path2);\n    pe = &dentry;\n    if ((result=fdir_client_rename_dentry_ex(ctx->contexts.fdir,\n                    &src, &dest, oper, flags, &pe)) != 0)\n    {\n        return result;\n    }\n\n    if (pe != NULL && S_ISREG(pe->stat.mode) && pe->stat.nlink == 0\n            && !ctx->contexts.fdir->trash_bin_enabled)\n    {\n        fs_api_unlink_file(ctx->contexts.fsapi, pe->inode,\n                pe->stat.space_end, tid);\n    }\n    return result;\n}\n"
  },
  {
    "path": "src/api/fcfs_api_util.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_API_UTIL_H\n#define _FCFS_API_UTIL_H\n\n#include <utime.h>\n#include <sys/xattr.h>\n#include \"fastcommon/logger.h\"\n#include \"fcfs_api_types.h\"\n#include \"inode_htable.h\"\n#include \"async_reporter.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define FCFSAPI_SET_PATH_FULLNAME(fullname, ctx, path_str) \\\n    fullname.ns = (ctx)->ns;    \\\n    FC_SET_STRING(fullname.path, (char *)path_str)\n\n#define FCFSAPI_SET_PATH_OPER_FNAME_EX(fname, ctx, \\\n        _uid, _gid, _gcount, _glist, path_str) \\\n    FDIR_SET_OPERATOR(fname.oper, _uid, _gid, _gcount, _glist); \\\n    FCFSAPI_SET_PATH_FULLNAME(fname.fullname, ctx, path_str)\n\n#define FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, _oper, path_str) \\\n    fname.oper = _oper; \\\n    FCFSAPI_SET_PATH_FULLNAME(fname.fullname, ctx, path_str)\n\n#define FCFSAPI_SET_PATH_OPER_PNAME_EX(opname, _uid, _gid, \\\n        _gcount, _glist, parent_inode, name) \\\n    FDIR_SET_OPERATOR(opname.oper, _uid, _gid, _gcount, _glist); \\\n    FDIR_SET_DENTRY_PNAME_PTR(&opname.pname, parent_inode, name)\n\n#define FCFSAPI_SET_PATH_OPER_PNAME(opname, _oper, parent_inode, name) \\\n    opname.oper = _oper; \\\n    FDIR_SET_DENTRY_PNAME_PTR(&opname.pname, parent_inode, name)\n\n#define FCFSAPI_SET_PATH_OPER_PNAME1(opname, _uid, _gid, \\\n        _gcount, _glist, parent_inode, name) \\\n    FDIR_SET_OPERATOR(opname.oper, _uid, _gid, _gcount, _glist); \\\n    FDIR_SET_DENTRY_PNAME_STR(&opname.pname, parent_inode, name)\n\n#define FCFSAPI_SET_OPER_INODE_PAIR_EX(oino, \\\n        _uid, _gid, _gcount, _glist, _inode) \\\n    FDIR_SET_OPERATOR(fname.oper, _uid, _gid, _gcount, _glist); \\\n    oino.inode = _inode\n\n#define FCFSAPI_SET_OPER_INODE_PAIR(oino, _oper, _inode) \\\n    oino.oper = _oper; \\\n    oino.inode = _inode\n\n#define fcfs_api_lookup_inode_by_path(path, oper, inode) \\\n    fcfs_api_lookup_inode_by_path_ex(&g_fcfs_api_ctx, path,  \\\n            oper, LOG_DEBUG, inode)\n\n#define fcfs_api_stat_dentry_by_path(path, flags, dentry)  \\\n    fcfs_api_stat_dentry_by_path_ex(&g_fcfs_api_ctx, \\\n            path, flags, LOG_DEBUG, dentry)\n\n#define fcfs_api_stat_dentry_by_fullname(fullname, flags, dentry)  \\\n    fcfs_api_stat_dentry_by_fullname_ex(&g_fcfs_api_ctx,    \\\n            fullname, flags, LOG_DEBUG, dentry)\n\n#define fcfs_api_stat_dentry_by_inode(inode, flags, dentry)  \\\n    fcfs_api_stat_dentry_by_inode_ex(&g_fcfs_api_ctx, inode, flags, dentry)\n\n#define fcfs_api_stat_dentry_by_pname(opname, flags, dentry)  \\\n    fcfs_api_stat_dentry_by_pname_ex(&g_fcfs_api_ctx, opname, \\\n            flags, LOG_DEBUG, dentry)\n\n#define fcfs_api_access_dentry_by_path(path, mask, flags, dentry) \\\n    fcfs_api_access_dentry_by_path_ex(&g_fcfs_api_ctx, \\\n            path, mask, flags, dentry)\n\n#define fcfs_api_access_dentry_by_inode(oino, mask, flags, dentry) \\\n    fcfs_api_access_dentry_by_inode_ex(&g_fcfs_api_ctx, \\\n            oino, mask, flags, dentry)\n\n#define fcfs_api_access_dentry_by_pname(opname, mask, flags, dentry) \\\n    fcfs_api_access_dentry_by_pname_ex(&g_fcfs_api_ctx, \\\n            opname, mask, flags, dentry)\n\n#define fcfs_api_create_dentry_by_pname(parent_inode, name, oper, rdev, dentry) \\\n    fcfs_api_create_dentry_by_pname_ex(&g_fcfs_api_ctx, \\\n            parent_inode, name, oper, rdev, dentry)\n\n#define fcfs_api_symlink_dentry_by_pname(link, parent_inode, name, oper, dentry)\\\n    fcfs_api_symlink_dentry_by_pname_ex(&g_fcfs_api_ctx, link, \\\n            parent_inode, name, oper, dentry)\n\n#define fcfs_api_readlink_by_pname(parent_inode, name, link, size) \\\n    fcfs_api_readlink_by_pname_ex(&g_fcfs_api_ctx, parent_inode, \\\n            name, link, size)\n\n#define fcfs_api_readlink_by_inode(inode, link, size) \\\n    fcfs_api_readlink_by_inode_ex(&g_fcfs_api_ctx, inode, link, size)\n\n#define fcfs_api_link_dentry_by_pname(src_inode, dest_parent_inode, \\\n        dest_name, oper, flags, dentry)  \\\n    fcfs_api_link_dentry_by_pname_ex(&g_fcfs_api_ctx, src_inode, \\\n            dest_parent_inode, dest_name, oper, flags, dentry)\n\n#define fcfs_api_remove_dentry_by_pname(opname, flags, tid)  \\\n    fcfs_api_remove_dentry_by_pname_ex(&g_fcfs_api_ctx, \\\n            opname, flags, tid)\n\n#define fcfs_api_remove_dentry(path, flags, tid)  \\\n    fcfs_api_remove_dentry_ex(&g_fcfs_api_ctx, path, flags, tid)\n\n#define fcfs_api_rename_dentry_by_pname(src_parent_inode, src_name, \\\n        dest_parent_inode, dest_name, oper, flags, tid)  \\\n    fcfs_api_rename_dentry_by_pname_ex(&g_fcfs_api_ctx, src_parent_inode, \\\n            src_name, dest_parent_inode, dest_name, oper, flags, tid)\n\n#define fcfs_api_rename_dentry(path1, path2, flags, tid) \\\n    fcfs_api_rename_dentry_ex(&g_fcfs_api_ctx, \\\n            path1, path2, flags, tid)\n\n#define fcfs_api_modify_stat_by_inode(oino, attr, mflags, flags, dentry)  \\\n    fcfs_api_modify_stat_by_inode_ex(&g_fcfs_api_ctx, \\\n            oino, attr, mflags, flags, dentry)\n\n#define fcfs_api_set_xattr_by_inode(inode, xattr, flags) \\\n    fcfs_api_set_xattr_by_inode_ex(&g_fcfs_api_ctx, inode, xattr, flags)\n\n#define fcfs_api_set_xattr_by_path(path, xattr, flags) \\\n    fcfs_api_set_xattr_by_path_ex(&g_fcfs_api_ctx, path, xattr, flags)\n\n#define fcfs_api_remove_xattr_by_inode(inode, name, flags) \\\n    fcfs_api_remove_xattr_by_inode_ex(&g_fcfs_api_ctx, inode, name, flags)\n\n#define fcfs_api_remove_xattr_by_path(path, name, flags) \\\n    fcfs_api_remove_xattr_by_path_ex(&g_fcfs_api_ctx, path, name, flags)\n\n#define fcfs_api_get_xattr_by_inode(inode, name, value, size, flags) \\\n    fcfs_api_get_xattr_by_inode_ex(&g_fcfs_api_ctx, inode, \\\n            name, LOG_DEBUG, value, size, flags)\n\n#define fcfs_api_get_xattr_by_path(path, name, value, size, flags) \\\n    fcfs_api_get_xattr_by_path_ex(&g_fcfs_api_ctx, path, \\\n            name, LOG_DEBUG, value, size, flags)\n\n#define fcfs_api_list_xattr_by_inode(inode, list, size, flags) \\\n    fcfs_api_list_xattr_by_inode_ex(&g_fcfs_api_ctx, inode, list, size, flags)\n\n#define fcfs_api_list_xattr_by_path(path, list, size, flags) \\\n    fcfs_api_list_xattr_by_path_ex(&g_fcfs_api_ctx, path, list, size, flags)\n\n#define fcfs_api_list_dentry_by_inode(inode, array)  \\\n    fcfs_api_list_dentry_by_inode_ex(&g_fcfs_api_ctx, inode, array)\n\n#define fcfs_api_list_compact_dentry_by_path(path, array)  \\\n    fcfs_api_list_compact_dentry_by_path_ex(&g_fcfs_api_ctx, path, array)\n\n#define fcfs_api_list_compact_dentry_by_inode(inode, array)  \\\n    fcfs_api_list_compact_dentry_by_inode_ex(&g_fcfs_api_ctx, inode, array)\n\n#define fcfs_api_alloc_opendir_session()  \\\n    fcfs_api_alloc_opendir_session_ex(&g_fcfs_api_ctx)\n\n#define fcfs_api_free_opendir_session(session) \\\n        fcfs_api_free_opendir_session_ex(&g_fcfs_api_ctx, session)\n\n#define fcfs_api_dentry_sys_lock(session, inode, flags, file_size, space_end) \\\n    fcfs_api_dentry_sys_lock_ex(&g_fcfs_api_ctx, session, inode, \\\n            flags, file_size, space_end)\n\nstatic inline int fcfs_api_lookup_inode_by_fullname_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path, const int enoent_log_level,\n        int64_t *inode)\n{\n    return fdir_client_lookup_inode_by_path_ex(ctx->contexts.fdir,\n            path, enoent_log_level, inode);\n}\n\nstatic inline int fcfs_api_lookup_inode_by_path_ex(FCFSAPIContext *ctx,\n        const char *path, const FDIRDentryOperator *oper,\n        const int enoent_log_level, int64_t *inode)\n{\n    FDIRClientOperFnamePair fname;\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path);\n    return fdir_client_lookup_inode_by_path_ex(ctx->contexts.fdir,\n            &fname, enoent_log_level, inode);\n}\n\nstatic inline int fcfs_api_stat_dentry_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, const int flags,\n        FDIRDEntryInfo *dentry)\n{\n    if (ctx->async_report.enabled) {\n        inode_htable_check_conflict_and_wait(oino->inode);\n    }\n    return fdir_client_stat_dentry_by_inode(ctx->contexts.fdir,\n            &ctx->ns, oino, flags, dentry);\n}\n\nstatic inline int fcfs_api_stat_dentry_by_fullname_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path, const int flags,\n        const int enoent_log_level, FDIRDEntryInfo *dentry)\n{\n    if (ctx->async_report.enabled) {\n        int result;\n        FDIRClientOperInodePair oino;\n        if ((result=fcfs_api_lookup_inode_by_fullname_ex(ctx,\n                        path, enoent_log_level, &oino.inode)) != 0)\n        {\n            return result;\n        }\n\n        oino.oper = path->oper;\n        return fcfs_api_stat_dentry_by_inode_ex(ctx, &oino, flags, dentry);\n    } else {\n        return fdir_client_stat_dentry_by_path_ex(ctx->contexts.fdir,\n                path, flags, enoent_log_level, dentry);\n    }\n}\n\nstatic inline int fcfs_api_stat_dentry_by_path_ex(FCFSAPIContext *ctx,\n        const char *path, const FDIRDentryOperator *oper, const int flags,\n        const int enoent_log_level, FDIRDEntryInfo *dentry)\n{\n    FDIRClientOperFnamePair fname;\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, ctx, *oper, path);\n    return fcfs_api_stat_dentry_by_fullname_ex(ctx, &fname,\n            flags, enoent_log_level, dentry);\n}\n\nstatic inline int fcfs_api_stat_dentry_by_pname_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperPnamePair *opname, const int flags,\n        const int enoent_log_level, FDIRDEntryInfo *dentry)\n{\n    if (ctx->async_report.enabled) {\n        int result;\n        FDIRClientOperInodePair oino;\n        if ((result=fdir_client_lookup_inode_by_pname_ex(ctx->contexts.fdir,\n                        &ctx->ns, opname, enoent_log_level, &oino.inode)) != 0)\n        {\n            return result;\n        }\n\n        oino.oper = opname->oper;\n        return fcfs_api_stat_dentry_by_inode_ex(ctx, &oino, flags, dentry);\n    } else {\n        return fdir_client_stat_dentry_by_pname_ex(ctx->contexts.fdir,\n                &ctx->ns, opname, flags, enoent_log_level, dentry);\n    }\n}\n\nstatic inline int fcfs_api_access_dentry_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, const char mask,\n        const int flags, FDIRDEntryInfo *dentry)\n{\n    if (ctx->async_report.enabled && (flags & FDIR_FLAGS_OUTPUT_DENTRY)) {\n        inode_htable_check_conflict_and_wait(oino->inode);\n    }\n\n    return fdir_client_access_dentry_by_inode(ctx->contexts.fdir,\n            &ctx->ns, oino, mask, flags, dentry);\n}\n\nstatic inline int fcfs_api_access_dentry_by_path_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path, const char mask,\n        const int flags, FDIRDEntryInfo *dentry)\n{\n    if (ctx->async_report.enabled && (flags & FDIR_FLAGS_OUTPUT_DENTRY)) {\n        int result;\n        FDIRClientOperInodePair oino;\n\n        if ((result=fcfs_api_lookup_inode_by_fullname_ex(ctx,\n                        path, LOG_NOTHING, &oino.inode)) != 0)\n        {\n            return result;\n        }\n\n        oino.oper = path->oper;\n        return fcfs_api_access_dentry_by_inode_ex(\n                ctx, &oino, mask, flags, dentry);\n    }\n\n    return fdir_client_access_dentry_by_path(ctx->\n            contexts.fdir, path, mask, flags, dentry);\n}\n\nstatic inline int fcfs_api_access_dentry_by_pname_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperPnamePair *opname, const char mask,\n        const int flags, FDIRDEntryInfo *dentry)\n{\n    if (ctx->async_report.enabled && (flags & FDIR_FLAGS_OUTPUT_DENTRY)) {\n        int result;\n        FDIRClientOperInodePair oino;\n\n        if ((result=fdir_client_lookup_inode_by_pname_ex(ctx->contexts.fdir,\n                        &ctx->ns, opname, LOG_NOTHING, &oino.inode)) != 0)\n        {\n            return result;\n        }\n\n        oino.oper = opname->oper;\n        return fcfs_api_access_dentry_by_inode_ex(\n                ctx, &oino, mask, flags, dentry);\n    }\n\n    return fdir_client_access_dentry_by_pname(ctx->contexts.fdir,\n            &ctx->ns, opname, mask, flags, dentry);\n}\n\nstatic inline int fcfs_api_create_dentry_by_pname_ex(FCFSAPIContext *ctx,\n        const int64_t parent_inode, const string_t *name,\n        const FDIRDentryOperator *oper, const mode_t mode,\n        const dev_t rdev, FDIRDEntryInfo *dentry)\n{\n    FDIRClientOperPnamePair opname;\n    FCFSAPI_SET_PATH_OPER_PNAME(opname, *oper, parent_inode, name);\n    return fdir_client_create_dentry_by_pname_ex(ctx->contexts.fdir,\n            &ctx->ns, &opname, mode, rdev, dentry);\n}\n\nstatic inline int fcfs_api_symlink_dentry_by_pname_ex(FCFSAPIContext *ctx,\n        const string_t *link, const int64_t parent_inode,\n        const string_t *name, const FDIRDentryOperator *oper,\n        const mode_t mode, FDIRDEntryInfo *dentry)\n{\n    FDIRClientOperPnamePair opname;\n    FCFSAPI_SET_PATH_OPER_PNAME(opname, *oper, parent_inode, name);\n    return fdir_client_symlink_dentry_by_pname(ctx->contexts.fdir,\n            link, &ctx->ns, &opname, mode, dentry);\n}\n\nstatic inline int fcfs_api_readlink_by_pname_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperPnamePair *opname,\n        string_t *link, const int size)\n{\n    return fdir_client_readlink_by_pname(ctx->contexts.fdir,\n            &ctx->ns, opname, link, size);\n}\n\nstatic inline int fcfs_api_readlink_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, string_t *link, const int size)\n{\n    return fdir_client_readlink_by_inode(ctx->contexts.fdir,\n            &ctx->ns, oino, link, size);\n}\n\nint fcfs_api_remove_dentry_by_pname_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperPnamePair *opname,\n        const int flags, const int64_t tid);\n\nint fcfs_api_remove_dentry_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path,\n        const int flags, const int64_t tid);\n\nint fcfs_api_rename_dentry_by_pname_ex(FCFSAPIContext *ctx,\n        const int64_t src_parent_inode, const string_t *src_name,\n        const int64_t dest_parent_inode, const string_t *dest_name,\n        const FDIRDentryOperator *oper, const int flags,\n        const int64_t tid);\n\nint fcfs_api_rename_dentry_ex(FCFSAPIContext *ctx, const char *path1,\n        const char *path2, const FDIRDentryOperator *oper,\n        const int flags, const int64_t tid);\n\nstatic inline int fcfs_api_modify_stat_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, const struct stat *attr,\n        const int64_t mflags, const int flags, FDIRDEntryInfo *dentry)\n{\n    FDIRDEntryStat stat;\n\n    if (ctx->async_report.enabled) {\n        inode_htable_check_conflict_and_wait(oino->inode);\n    }\n    stat.mode = attr->st_mode;\n    stat.gid = attr->st_gid;\n    stat.uid = attr->st_uid;\n    stat.atime = attr->st_atime;\n    stat.ctime = attr->st_ctime;\n    stat.mtime = attr->st_mtime;\n    stat.size = attr->st_size;\n    return fdir_client_modify_stat_by_inode(ctx->contexts.fdir,\n            &ctx->ns, oino, mflags, &stat, flags, dentry);\n}\n\n#define FCFS_API_SET_UTIMES(stat, options, times) \\\n    do { \\\n        options.flags = 0; \\\n        options.atime = options.mtime = 1; \\\n        memset(&stat, 0, sizeof(stat)); \\\n        if (times == NULL) { \\\n            stat.atime = stat.mtime = get_current_time(); \\\n        } else { \\\n            stat.atime = times[0].tv_sec; \\\n            stat.mtime = times[1].tv_sec; \\\n        } \\\n    } while (0)\n\n\n#define SET_ONE_UTIMENS(var, option_flag, option_now, tp) \\\n    do { \\\n        if (tp.tv_nsec == UTIME_NOW) { \\\n            var = get_current_time();  \\\n            option_flag = 1; \\\n            option_now = 1;  \\\n        } else if (tp.tv_nsec == UTIME_OMIT) { \\\n            option_flag = 0; \\\n        } else { \\\n            var = tp.tv_sec; \\\n            option_flag = 1; \\\n        } \\\n    } while (0)\n\n#define FCFS_API_SET_UTIMENS(stat, options, times) \\\n    do { \\\n        options.flags = 0; \\\n        memset(&stat, 0, sizeof(stat)); \\\n        if (times == NULL) { \\\n            stat.atime = stat.mtime = get_current_time(); \\\n        } else { \\\n            SET_ONE_UTIMENS(stat.atime, options.atime, \\\n\t\toptions.atime_now, times[0]); \\\n            SET_ONE_UTIMENS(stat.mtime, options.mtime, \\\n\t\toptions.mtime_now, times[1]); \\\n        } \\\n    } while (0)\n\nstatic inline int fcfs_api_utimes_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, const struct timeval times[2],\n        const int flags)\n{\n    FDIRStatModifyFlags options;\n    FDIRDEntryStat stat;\n    FDIRDEntryInfo dentry;\n\n    if (ctx->async_report.enabled) {\n        inode_htable_check_conflict_and_wait(oino->inode);\n    }\n\n    FCFS_API_SET_UTIMES(stat, options, times);\n    return fdir_client_modify_stat_by_inode(ctx->contexts.fdir,\n            &ctx->ns, oino, options.flags, &stat, flags, &dentry);\n}\n\nstatic inline int fcfs_api_utimes_by_path_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path,\n        const struct timeval times[2], const int flags)\n{\n    FDIRStatModifyFlags options;\n    FDIRDEntryStat stat;\n    FDIRDEntryInfo dentry;\n\n    FCFS_API_SET_UTIMES(stat, options, times);\n    return fdir_client_modify_stat_by_path(ctx->contexts.fdir,\n            path, options.flags, &stat, flags, &dentry);\n}\n\nstatic inline int fcfs_api_utimens_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, const struct timespec times[2],\n        const int flags)\n{\n    FDIRStatModifyFlags options;\n    FDIRDEntryStat stat;\n    FDIRDEntryInfo dentry;\n\n    if (ctx->async_report.enabled) {\n        inode_htable_check_conflict_and_wait(oino->inode);\n    }\n\n    FCFS_API_SET_UTIMENS(stat, options, times);\n    if (options.flags == 0) {\n        return 0;\n    } else {\n        return fdir_client_modify_stat_by_inode(ctx->contexts.fdir,\n                &ctx->ns, oino, options.flags, &stat, flags, &dentry);\n    }\n}\n\nstatic inline int fcfs_api_utimens_by_path_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path,\n        const struct timespec times[2], const int flags)\n{\n    FDIRStatModifyFlags options;\n    FDIRDEntryStat stat;\n    FDIRDEntryInfo dentry;\n\n    FCFS_API_SET_UTIMENS(stat, options, times);\n    if (options.flags == 0) {\n        return 0;\n    } else {\n        return fdir_client_modify_stat_by_path(ctx->contexts.fdir,\n                path, options.flags, &stat, flags, &dentry);\n    }\n}\n\nstatic inline int fcfs_api_utime_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path,\n        const struct utimbuf *times)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    FDIRStatModifyFlags options;\n    FDIRDEntryStat stat;\n    FDIRDEntryInfo dentry;\n\n    options.flags = 0;\n    options.atime = options.mtime = 1;\n    memset(&stat, 0, sizeof(stat));\n    if (times == NULL) {\n        stat.atime = stat.mtime = get_current_time();\n    } else {\n        stat.atime = times->actime;\n        stat.mtime = times->modtime;\n    }\n\n    return fdir_client_modify_stat_by_path(ctx->contexts.fdir,\n            path, options.flags, &stat, flags, &dentry);\n}\n\nstatic inline int fcfs_api_chown_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, const uid_t uid,\n        const gid_t gid, const int flags)\n{\n    FDIRStatModifyFlags options;\n    FDIRDEntryStat stat;\n    FDIRDEntryInfo dentry;\n\n    options.flags = 0;\n    options.uid = options.gid = 1;\n    memset(&stat, 0, sizeof(stat));\n    stat.uid = uid;\n    stat.gid = gid;\n    return fdir_client_modify_stat_by_inode(ctx->contexts.fdir,\n            &ctx->ns, oino, options.flags, &stat, flags, &dentry);\n}\n\nstatic inline int fcfs_api_chown_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path, const uid_t uid,\n        const gid_t gid, const int flags)\n{\n    FDIRStatModifyFlags options;\n    FDIRDEntryStat stat;\n    FDIRDEntryInfo dentry;\n\n    options.flags = 0;\n    options.uid = options.gid = 1;\n    memset(&stat, 0, sizeof(stat));\n    stat.uid = uid;\n    stat.gid = gid;\n    return fdir_client_modify_stat_by_path(ctx->contexts.fdir,\n            path, options.flags, &stat, flags, &dentry);\n}\n\nstatic inline int fcfs_api_chmod_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, const mode_t mode,\n        const int flags)\n{\n    FDIRStatModifyFlags options;\n    FDIRDEntryStat stat;\n    FDIRDEntryInfo dentry;\n\n    options.flags = 0;\n    options.mode = 1;\n    memset(&stat, 0, sizeof(stat));\n    stat.mode = (mode & ALLPERMS);\n    return fdir_client_modify_stat_by_inode(ctx->contexts.fdir,\n            &ctx->ns, oino, options.flags, &stat, flags, &dentry);\n}\n\nstatic inline int fcfs_api_chmod_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path,\n        const mode_t mode, const int flags)\n{\n    FDIRStatModifyFlags options;\n    FDIRDEntryStat stat;\n    FDIRDEntryInfo dentry;\n\n    options.flags = 0;\n    options.mode = 1;\n    memset(&stat, 0, sizeof(stat));\n    stat.mode = (mode & ALLPERMS);\n    return fdir_client_modify_stat_by_path(ctx->contexts.fdir,\n            path, options.flags, &stat, flags, &dentry);\n}\n\nstatic inline int convert_xattr_flags(const int flags)\n{\n    int new_flags;\n\n    if (flags == 0) {\n        return 0;\n    }\n\n    new_flags = (flags & FDIR_FLAGS_FOLLOW_SYMLINK);\n    if ((flags & XATTR_CREATE) != 0) {\n        return (new_flags | FDIR_FLAGS_XATTR_CREATE);\n    } else if ((flags & XATTR_REPLACE) != 0) {\n        return (new_flags | FDIR_FLAGS_XATTR_REPLACE);\n    } else {\n        return new_flags;\n    }\n}\n\nstatic inline int fcfs_api_set_xattr_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, const key_value_pair_t *xattr,\n        const int flags)\n{\n    return fdir_client_set_xattr_by_inode(ctx->contexts.fdir,\n            &ctx->ns, oino, xattr, convert_xattr_flags(flags));\n}\n\nstatic inline int fcfs_api_set_xattr_by_path_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path,\n        const key_value_pair_t *xattr, const int flags)\n{\n    return fdir_client_set_xattr_by_path(ctx->contexts.fdir,\n            path, xattr, convert_xattr_flags(flags));\n}\n\nstatic inline int fcfs_api_remove_xattr_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino,\n        const string_t *name, const int flags)\n{\n    return fdir_client_remove_xattr_by_inode_ex(ctx->contexts.fdir,\n            &ctx->ns, oino, name, flags, LOG_DEBUG);\n}\n\nstatic inline int fcfs_api_remove_xattr_by_path_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path,\n        const string_t *name, const int flags)\n{\n    return fdir_client_remove_xattr_by_path_ex(ctx->contexts.fdir,\n            path, name, flags, LOG_DEBUG);\n}\n\nstatic inline int fcfs_api_get_xattr_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, const string_t *name,\n        const int enoattr_log_level, string_t *value, const int size,\n        const int flags)\n{\n    return fdir_client_get_xattr_by_inode_ex(ctx->contexts.fdir, &ctx->ns,\n            oino, name, enoattr_log_level, value, size, flags);\n}\n\nstatic inline int fcfs_api_get_xattr_by_path_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path, const string_t *name,\n        const int enoattr_log_level, string_t *value,\n        const int size, const int flags)\n{\n    return fdir_client_get_xattr_by_path_ex(ctx->contexts.fdir,\n            path, name, enoattr_log_level, value, size, flags);\n}\n\nstatic inline int fcfs_api_list_xattr_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, string_t *list,\n        const int size, const int flags)\n{\n    return fdir_client_list_xattr_by_inode(ctx->contexts.fdir,\n            &ctx->ns, oino, list, size, flags);\n}\n\nstatic inline int fcfs_api_list_xattr_by_path_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path, string_t *list,\n        const int size, const int flags)\n{\n    return fdir_client_list_xattr_by_path(ctx->contexts.fdir,\n            path, list, size, flags);\n}\n\nstatic inline int fcfs_api_list_dentry_by_inode_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperInodePair *oino, FDIRClientDentryArray *array)\n{\n    const int flags = FDIR_LIST_DENTRY_FLAGS_OUTPUT_SPECIAL;\n    if (ctx->async_report.enabled) {\n        async_reporter_wait_all(oino->inode);\n    }\n    return fdir_client_list_dentry_by_inode(ctx->contexts.fdir,\n            &ctx->ns, oino, array, flags);\n}\n\nstatic inline int fcfs_api_list_compact_dentry_by_path_ex(FCFSAPIContext *ctx,\n        const FDIRClientOperFnamePair *path,\n        FDIRClientCompactDentryArray *array)\n{\n    return fdir_client_list_compact_dentry_by_path(\n            ctx->contexts.fdir, path, array);\n}\n\nstatic inline int fcfs_api_list_compact_dentry_by_inode_ex(\n        FCFSAPIContext *ctx, const FDIRClientOperInodePair *oino,\n        FDIRClientCompactDentryArray *array)\n{\n    return fdir_client_list_compact_dentry_by_inode(\n            ctx->contexts.fdir, &ctx->ns, oino, array);\n}\n\nstatic inline FCFSAPIOpendirSession *fcfs_api_alloc_opendir_session_ex(\n        FCFSAPIContext *ctx)\n{\n    return (FCFSAPIOpendirSession *)fast_mblock_alloc_object(\n            &ctx->opendir_session_pool);\n}\n\nstatic inline void fcfs_api_free_opendir_session_ex(\n        FCFSAPIContext *ctx, FCFSAPIOpendirSession *session)\n{\n    fast_mblock_free_object(&ctx->opendir_session_pool, session);\n}\n\nstatic inline int fcfs_api_dentry_sys_lock_ex(FCFSAPIContext *ctx,\n        FDIRClientSession *session, const int64_t inode, const int flags,\n        int64_t *file_size, int64_t *space_end)\n{\n    int result;\n    if ((result=fdir_client_init_session(ctx->contexts.fdir, session)) != 0) {\n        return result;\n    }\n    return fdir_client_dentry_sys_lock(session, &ctx->ns,\n            inode, flags, file_size, space_end);\n}\n\nstatic inline int fcfs_api_dentry_sys_unlock(FDIRClientSession *session,\n        const string_t *ns, const int64_t old_size,\n        const FDIRSetDEntrySizeInfo *dsize)\n{\n    int result;\n    result = fdir_client_dentry_sys_unlock_ex(session, ns, old_size, dsize);\n    fdir_client_close_session(session, result != 0);\n    return result;\n}\n\nstatic inline int fcfs_api_link_dentry_by_pname_ex(FCFSAPIContext *ctx,\n        const int64_t src_inode, const int64_t dest_parent_inode,\n        const string_t *dest_name, const FDIRDentryOperator *oper,\n        const mode_t mode, const int flags, FDIRDEntryInfo *dentry)\n{\n    FDIRClientOperPnamePair dest_opname;\n    FCFSAPI_SET_PATH_OPER_PNAME(dest_opname, *oper,\n            dest_parent_inode, dest_name);\n    return fdir_client_link_dentry_by_pname(ctx->contexts.fdir,\n            src_inode, &ctx->ns, &dest_opname, mode, flags, dentry);\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/inode_htable.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdlib.h>\n#include \"fcfs_api_allocator.h\"\n#include \"async_reporter.h\"\n#include \"inode_htable.h\"\n\nstatic SFHtableShardingContext inode_sharding_ctx;\n\ntypedef struct fcfs_api_insert_event_context {\n    FCFSAPIAsyncReportEvent *event;\n} FCFSAPIInsertEventContext;\n\ntypedef struct fcfs_api_find_callback_arg {\n    FCFSAPIAllocatorContext *allocator_ctx;\n    FCFSAPIWaitingTask *waiting_task;\n} FCFSAPIFindCallbackArg;\n\nstatic int inode_htable_insert_callback(SFShardingHashEntry *he,\n        void *arg, const bool new_create)\n{\n    FCFSAPIInodeHEntry *inode;\n    FCFSAPIInsertEventContext *ictx;\n\n    inode = (FCFSAPIInodeHEntry *)he;\n    ictx = (FCFSAPIInsertEventContext *)arg;\n    if (new_create) {\n        FC_INIT_LIST_HEAD(&inode->head);\n    }\n\n    ictx->event->inode_hentry = inode;\n    fc_list_add(&ictx->event->dlink, &inode->head);\n    return 0;\n}\n\nstatic void *inode_htable_find_callback(SFShardingHashEntry *he, void *arg)\n{\n    FCFSAPIInodeHEntry *inode;\n    FCFSAPIFindCallbackArg *farg;\n    FCFSAPIAsyncReportEvent *event;\n\n    inode = (FCFSAPIInodeHEntry *)he;\n    event = fc_list_first_entry(&inode->head, FCFSAPIAsyncReportEvent, dlink);\n    if (event == NULL) {\n        return NULL;\n    }\n\n    farg = (FCFSAPIFindCallbackArg *)arg;\n    farg->waiting_task = (FCFSAPIWaitingTask *)fast_mblock_alloc_object(\n            &farg->allocator_ctx->waiting_task);\n    if (farg->waiting_task == NULL) {\n        return NULL;\n    }\n\n    //add to event waiting queue\n    farg->waiting_task->next = event->waitings.head;\n    event->waitings.head = farg->waiting_task;\n    return event;\n}\n\nstatic bool inode_htable_accept_reclaim_callback(SFShardingHashEntry *he)\n{\n    return fc_list_empty(&((FCFSAPIInodeHEntry *)he)->head);\n}\n\nint inode_htable_init(const int sharding_count,\n        const int64_t htable_capacity,\n        const int allocator_count, int64_t element_limit,\n        const int64_t min_ttl_sec, const int64_t max_ttl_sec)\n{\n    return sf_sharding_htable_init(&inode_sharding_ctx,\n            sf_sharding_htable_key_ids_one, inode_htable_insert_callback,\n            inode_htable_find_callback, NULL,\n            inode_htable_accept_reclaim_callback, sharding_count,\n            htable_capacity, allocator_count, sizeof(FCFSAPIInodeHEntry),\n            element_limit, min_ttl_sec, max_ttl_sec);\n}\n\nint inode_htable_insert(FCFSAPIAsyncReportEvent *event)\n{\n    SFTwoIdsHashKey key;\n    FCFSAPIInsertEventContext ictx;\n\n    key.oid = event->dsize.inode;\n    ictx.event = event;\n    return sf_sharding_htable_insert(&inode_sharding_ctx,\n            &key, &ictx);\n}\n\nint inode_htable_check_conflict_and_wait(const uint64_t inode)\n{\n    SFTwoIdsHashKey key;\n    FCFSAPIFindCallbackArg callback_arg;\n\n    key.oid = inode;\n    callback_arg.allocator_ctx = fcfs_api_allocator_get(inode);\n    callback_arg.waiting_task = NULL;\n    if (sf_sharding_htable_find(&inode_sharding_ctx,\n                &key, &callback_arg) == NULL)\n    {\n        return 0;\n    }\n\n    if (callback_arg.waiting_task != NULL) {\n        async_reporter_notify();\n        //logInfo(\"oid: %\"PRId64\", wait_report_done_and_release\", inode);\n        fcfs_api_wait_report_done_and_release(callback_arg.waiting_task);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "src/api/inode_htable.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _INODE_HTABLE_H\n#define _INODE_HTABLE_H\n\n#include \"fcfs_api_types.h\"\n#include \"sf/sf_sharding_htable.h\"\n\ntypedef struct fcfs_api_inode_hentry {\n    SFShardingHashEntry hentry; //must be the first\n    struct fc_list_head head;   //element: FCFSAPIAsyncReportEvent\n} FCFSAPIInodeHEntry;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    int inode_htable_init(const int sharding_count,\n            const int64_t htable_capacity,\n            const int allocator_count, int64_t element_limit,\n            const int64_t min_ttl_sec, const int64_t max_ttl_sec);\n\n    int inode_htable_insert(FCFSAPIAsyncReportEvent *event);\n\n    int inode_htable_check_conflict_and_wait(const uint64_t inode);\n\n    static inline void fcfs_api_notify_waiting_task(FCFSAPIWaitingTask *task)\n    {\n        PTHREAD_MUTEX_LOCK(&task->lcp.lock);\n        task->finished = true;\n        pthread_cond_signal(&task->lcp.cond);\n        PTHREAD_MUTEX_UNLOCK(&task->lcp.lock);\n    }\n\n    static inline void fcfs_api_wait_report_done_and_release(\n            FCFSAPIWaitingTask *waiting_task)\n    {\n        PTHREAD_MUTEX_LOCK(&waiting_task->lcp.lock);\n        while (!waiting_task->finished) {\n            pthread_cond_wait(&waiting_task->lcp.cond,\n                    &waiting_task->lcp.lock);\n        }\n        waiting_task->finished = false;  //reset for next use\n        PTHREAD_MUTEX_UNLOCK(&waiting_task->lcp.lock);\n        fast_mblock_free_object(waiting_task->allocator, waiting_task);\n    }\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/std/api_types.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_POSIX_API_TYPES_H\n#define _FCFS_POSIX_API_TYPES_H\n\n#include \"fastcommon/fc_list.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"../fcfs_api.h\"\n\n#ifndef OS_LINUX\ntypedef off_t off64_t;\n#endif\n\n#define FCFS_POSIX_API_FD_BASE  (2 << 28)\n\nstruct dirent;\ntypedef int (*fcfs_dir_filter_func)(const struct dirent *ent);\ntypedef int (*fcfs_dir_compare_func)(const struct dirent **ent1,\n        const struct dirent **ent2);\n\ntypedef struct fcfs_posix_api_context {\n    FCFSAPINSMountpointHolder nsmp;\n    string_t mountpoint;\n    FCFSAPIContext api_ctx;\n} FCFSPosixAPIContext;\n\ntypedef enum {\n    fcfs_papi_tpid_type_tid,\n    fcfs_papi_tpid_type_pid\n} FCFSPosixAPITPIDType;\n\ntypedef struct fcfs_posix_api_file_info {\n    string_t filename;\n    int fd;\n    FCFSPosixAPITPIDType tpid_type;  //use pid or tid\n    FCFSAPIFileInfo fi;\n} FCFSPosixAPIFileInfo;\n\ntypedef struct fcfs_posix_file_ptr_array {\n    volatile FCFSPosixAPIFileInfo **files;\n    volatile int count;\n} FCFSPosixFilePtrArray;\n\ntypedef struct fcfs_posix_api_dir {\n    FCFSPosixAPIFileInfo *file;\n    FDIRClientCompactDentryArray darray;\n    int magic;\n    int offset;\n} FCFSPosixAPIDIR;\n\ntypedef struct fcfs_posix_capi_file {\n    int magic;\n    int fd;\n    int error_no;\n    int eof;\n    pthread_mutex_t lock;\n    struct fc_list_head dlink;  //for opened files chain\n} FCFSPosixCAPIFILE;\n\ntypedef struct fcfs_posix_api_global_vars {\n    FCFSPosixAPIContext ctx;\n    string_t *cwd;\n} FCFSPosixAPIGlobalVars;\n\n#define FCFS_PAPI_IS_MY_FD(fd) \\\n    (fd > FCFS_POSIX_API_FD_BASE)\n\n#endif\n"
  },
  {
    "path": "src/api/std/capi.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdarg.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/locked_list.h\"\n#include \"papi.h\"\n#include \"capi.h\"\n\nstatic FCLockedList capi_opend_files;\n\n#define FCFS_CAPI_MAGIC_NUMBER 1645431088\n\n#ifdef OS_LINUX\n#define FCFS_POS_OFFSET(pos) (pos)->__pos\n#else\n#define FCFS_POS_OFFSET(pos) *(pos)\n#endif\n\n#define FCFS_CAPI_CONVERT_FP_EX(fp, ...) \\\n    FCFSPosixCAPIFILE *file; \\\n    file = (FCFSPosixCAPIFILE *)fp; \\\n    if (file->magic != FCFS_CAPI_MAGIC_NUMBER) { \\\n        errno = EBADF; \\\n        return __VA_ARGS__; \\\n    }\n\n#define FCFS_CAPI_CONVERT_FP(fp) \\\n    FCFS_CAPI_CONVERT_FP_EX(fp, -1)\n\n#define FCFS_CAPI_CONVERT_FP_VOID(fp) \\\n    FCFS_CAPI_CONVERT_FP_EX(fp)\n\nint fcfs_capi_init()\n{\n    return locked_list_init(&capi_opend_files);\n}\n\nvoid fcfs_capi_destroy()\n{\n    fcfs_fcloseall();\n    locked_list_destroy(&capi_opend_files);\n}\n\nstatic FILE *alloc_file_handle(int fd)\n{\n    FCFSPosixCAPIFILE *file;\n    int result;\n\n    if ((file=fc_malloc(sizeof(FCFSPosixCAPIFILE))) == NULL) {\n        errno = ENOMEM;\n        return NULL;\n    }\n\n    if ((result=init_pthread_lock(&file->lock)) != 0) {\n        free(file);\n        errno = result;\n        return NULL;\n    }\n\n    file->fd = fd;\n    file->magic = FCFS_CAPI_MAGIC_NUMBER;\n    file->error_no = 0;\n    file->eof = 0;\n    locked_list_add_tail(&file->dlink, &capi_opend_files);\n\n    return (FILE *)file;\n}\n\nstatic inline int free_file_handle(FCFSPosixCAPIFILE *file)\n{\n    int result;\n\n    locked_list_del(&file->dlink, &capi_opend_files);\n    if (fcfs_close(file->fd) == 0) {\n        result = 0;\n    } else {\n        result = EOF;\n    }\n    file->magic = 0;\n    free(file);\n\n    return result;\n}\n\nstatic int mode_to_flags(const char *mode, int *flags)\n{\n    const char *p;\n    const char *end;\n    int len;\n\n    len = strlen(mode);\n    if (len == 0) {\n        return EINVAL;\n    }\n\n    p = mode;\n    switch (*p++) {\n        case 'r':\n            if (*p == '+') {\n                *flags = O_RDWR;\n                p++;\n            } else {\n                *flags = O_RDONLY;\n            }\n            break;\n        case 'w':\n            if (*p == '+') {\n                *flags = O_RDWR | O_CREAT | O_TRUNC;\n                p++;\n            } else {\n                *flags = O_WRONLY | O_CREAT | O_TRUNC;\n            }\n            break;\n        case 'a':\n            if (*p == '+') {\n                *flags = O_RDWR | O_CREAT | O_APPEND;\n                p++;\n            } else {\n                *flags = O_WRONLY | O_CREAT | O_APPEND;\n            }\n            break;\n        default:\n            return EINVAL;\n    }\n\n    end = mode + len;\n    while (p < end) {\n        switch (*p) {\n            case 'e':\n                *flags |= O_CLOEXEC;\n                break;\n            case'x':\n                *flags |= O_EXCL;\n                break;\n            default:\n                break;\n        }\n        ++p;\n    }\n\n    return 0;\n}\n\nstatic int check_flags(const char *mode, const int flags, int *adding_flags)\n{\n    const char *p;\n    const char *end;\n    int rwflags;\n    int len;\n\n    len = strlen(mode);\n    if (len == 0) {\n        return EINVAL;\n    }\n\n    *adding_flags = 0;\n    p = mode;\n    switch (*p++) {\n        case 'r':\n            if (*p == '+') {\n                rwflags = O_RDWR;\n                p++;\n            } else {\n                rwflags = O_RDONLY;\n            }\n            break;\n        case 'a':\n            *adding_flags = O_APPEND;\n        case 'w':\n            if (*p == '+') {\n                rwflags = O_RDWR;\n                p++;\n            } else {\n                rwflags = O_WRONLY;\n            }\n\n            break;\n        default:\n            return EINVAL;\n    }\n\n    if ((flags & rwflags) != rwflags) {\n        return EINVAL;\n    }\n\n    end = mode + len;\n    while (p < end) {\n        switch (*p) {\n            case 'e':\n                *adding_flags |= O_CLOEXEC;\n                break;\n            case'x':\n                //just ignore\n                break;\n            default:\n                break;\n        }\n        ++p;\n    }\n\n    return 0;\n}\n\nstatic inline int do_fdopen(FCFSPosixAPIContext *ctx,\n        const char *path, const char *mode)\n{\n    int flags;\n    int result;\n\n    if ((result=mode_to_flags(mode, &flags)) != 0) {\n        errno = result;\n        return -1;\n    }\n\n    return fcfs_file_open(ctx, path, flags, 0666, fcfs_papi_tpid_type_pid);\n}\n\nvoid fcfs_flockfile(FILE *fp)\n{\n    FCFS_CAPI_CONVERT_FP_VOID(fp);\n    PTHREAD_MUTEX_LOCK(&file->lock);\n}\n\nint fcfs_ftrylockfile(FILE *fp)\n{\n    int result;\n    FCFS_CAPI_CONVERT_FP_EX(fp, -1);\n    if ((result=pthread_mutex_trylock(&file->lock)) != 0) {\n        errno = result;\n        return -1;\n    }\n    return 0;\n}\n\nvoid fcfs_funlockfile(FILE *fp)\n{\n    FCFS_CAPI_CONVERT_FP_VOID(fp);\n    PTHREAD_MUTEX_UNLOCK(&file->lock);\n}\n\nFILE *fcfs_fopen_ex(FCFSPosixAPIContext *ctx,\n        const char *path, const char *mode)\n{\n    int fd;\n\n    if ((fd=do_fdopen(ctx, path, mode)) < 0) {\n        return NULL;\n    }\n\n    return alloc_file_handle(fd);\n}\n\nFILE *fcfs_fdopen_ex(FCFSPosixAPIContext *ctx, int fd, const char *mode)\n{\n    int flags;\n    int adding_flags;\n    int result;\n\n    if ((flags=fcfs_fcntl(fd, F_GETFL)) < 0) {\n        return NULL;\n    }\n\n    if ((result=check_flags(mode, flags, &adding_flags)) != 0) {\n        errno = result;\n        return NULL;\n    }\n\n    if (adding_flags != 0 && (flags & adding_flags) != adding_flags) {\n        if (fcfs_fcntl(fd, F_SETFL, (flags | adding_flags)) != 0) {\n            return NULL;\n        }\n    }\n\n    return alloc_file_handle(fd);\n}\n\nFILE *fcfs_freopen_ex(FCFSPosixAPIContext *ctx,\n        const char *path, const char *mode, FILE *fp)\n{\n    int fd;\n    FCFSPosixAPIFileInfo *finfo;\n\n    FCFS_CAPI_CONVERT_FP_EX(fp, NULL);\n    if (path == NULL) {\n        if ((finfo=fcfs_get_file_handle(file->fd)) == NULL) {\n            errno = EBADF;\n            return NULL;\n        }\n\n        path = finfo->filename.str;\n    }\n\n    if ((fd=do_fdopen(ctx, path, mode)) < 0) {\n        return NULL;\n    }\n\n    fcfs_close(file->fd);\n    file->fd = fd;\n    file->eof = 0;\n    file->error_no = 0;\n    return (FILE *)file;\n}\n\nint fcfs_fclose(FILE *fp)\n{\n    FCFS_CAPI_CONVERT_FP_EX(fp, EOF);\n    return free_file_handle(file);\n}\n\nint fcfs_fcloseall()\n{\n    int result;\n    int r;\n    FCFSPosixCAPIFILE *file;\n\n    result = 0;\n    while (1) {\n        locked_list_first_entry(&capi_opend_files,\n                FCFSPosixCAPIFILE, dlink, file);\n        if (file == NULL) {\n            break;\n        }\n\n        if ((r=free_file_handle(file)) != 0) {\n            result = r;\n        }\n    }\n\n    return result;\n}\n\nint fcfs_setvbuf(FILE *fp, char *buf, int mode, size_t size)\n{\n    switch (mode) {\n        case _IOFBF:\n            break;\n        case _IOLBF:\n            break;\n        case _IONBF:\n            break;\n        default:\n            break;\n    }\n\n    //alloc_file_buffer(FCFSPosixCAPIFILE *file, const int size);\n    return 0;\n}\n\nint fcfs_fseek(FILE *fp, long offset, int whence)\n{\n    FCFS_CAPI_CONVERT_FP(fp);\n    return fcfs_lseek(file->fd, offset, whence) >= 0 ? 0 : -1;\n}\n\nint fcfs_fseeko(FILE *fp, off_t offset, int whence)\n{\n    FCFS_CAPI_CONVERT_FP(fp);\n    return fcfs_lseek(file->fd, offset, whence) >= 0 ? 0 : -1;\n}\n\nlong fcfs_ftell(FILE *fp)\n{\n    FCFS_CAPI_CONVERT_FP(fp);\n    return fcfs_ltell(file->fd);\n}\n\noff_t fcfs_ftello(FILE *fp)\n{\n    FCFS_CAPI_CONVERT_FP(fp);\n    return fcfs_ltell(file->fd);\n}\n\nvoid fcfs_rewind(FILE *fp)\n{\n    FCFS_CAPI_CONVERT_FP_VOID(fp);\n    fcfs_lseek(file->fd, 0L, SEEK_SET);\n}\n\nint fcfs_fgetpos(FILE *fp, fpos_t *pos)\n{\n    FCFS_CAPI_CONVERT_FP(fp);\n    FCFS_POS_OFFSET(pos) = fcfs_ltell(file->fd);\n    return FCFS_POS_OFFSET(pos) >= 0 ? 0 : -1;\n}\n\nint fcfs_fsetpos(FILE *fp, const fpos_t *pos)\n{\n    FCFS_CAPI_CONVERT_FP(fp);\n    return fcfs_lseek(file->fd, FCFS_POS_OFFSET(pos), SEEK_SET) >= 0 ? 0 : -1;\n}\n\n#define SET_FILE_ERROR(file, bytes) \\\n    do { \\\n        if (bytes < 0) { \\\n            file->error_no = (errno != 0 ? errno : EIO); \\\n        } else { \\\n            file->eof = 1; \\\n        } \\\n    } while (0)\n\nstatic inline int do_fgetc(FILE *fp, const bool need_lock)\n{\n    int bytes;\n    int c;\n    unsigned char buff[8];\n\n    FCFS_CAPI_CONVERT_FP_EX(fp, EOF);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n\n    bytes = fcfs_read(file->fd, buff, 1);\n    if (bytes > 0) {\n        c = *buff;\n    } else {\n        SET_FILE_ERROR(file, bytes);\n        c = EOF;\n    }\n\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n    return c;\n}\n\nstatic inline int do_fputc(int c, FILE *fp, const bool need_lock)\n{\n    int bytes;\n    char ch;\n\n    FCFS_CAPI_CONVERT_FP_EX(fp, EOF);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n\n    ch = c;\n    bytes = fcfs_write(file->fd, &ch, 1);\n    if (bytes <= 0) {\n        file->error_no = (errno != 0 ? errno : EIO);\n        ch = EOF;\n    }\n\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n    return ch;\n}\n\nstatic inline void do_clearerr(FILE *fp, const bool need_lock)\n{\n    FCFS_CAPI_CONVERT_FP_VOID(fp);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n\n    file->error_no = 0;\n    file->eof = 0;\n\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n}\n\nstatic inline int do_feof(FILE *fp, const bool need_lock)\n{\n    int eof;\n\n    FCFS_CAPI_CONVERT_FP(fp);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n    eof = file->eof;\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n\n    return eof;\n}\n\nstatic inline int do_ferror(FILE *fp, const bool need_lock)\n{\n    int error_no;\n\n    FCFS_CAPI_CONVERT_FP(fp);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n    error_no = file->error_no;\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n\n    return error_no;\n}\n\nstatic inline int do_fileno(FILE *fp, const bool need_lock)\n{\n    int fd;\n\n    FCFS_CAPI_CONVERT_FP(fp);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n    fd = file->fd;\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n\n    return fd;\n}\n\nstatic inline int do_fflush(FILE *fp, const bool need_lock)\n{\n    FCFS_CAPI_CONVERT_FP_EX(fp, EOF);\n    return 0;\n}\n\nstatic inline size_t do_fread(void *buff, size_t size,\n        size_t n, FILE *fp, const bool need_lock)\n{\n    size_t count;\n\n    FCFS_CAPI_CONVERT_FP_EX(fp, EOF);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n\n    count = fcfs_file_read(file->fd, buff, size, n);\n    if (count != n) {\n        if (count < 0) {\n            file->error_no = (errno != 0 ? errno : EIO);\n            count = 0;\n        } else if (count == 0) {\n            file->eof = 1;\n        }\n    }\n\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n    return count;\n}\n\nstatic inline size_t do_fwrite(const void *buff, size_t size,\n        size_t n, FILE *fp, const bool need_lock)\n{\n    size_t count;\n\n    FCFS_CAPI_CONVERT_FP_EX(fp, EOF);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n\n    count = fcfs_file_write(file->fd, buff, size, n);\n    if (count != n) {\n        if (count < 0) {\n            file->error_no = (errno != 0 ? errno : EIO);\n            count = 0;\n        } else if (count == 0) {\n            file->error_no = (errno != 0 ? errno : EIO);\n        }\n    }\n\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n    return count;\n}\n\nstatic inline int do_fputs(const char *s, FILE *fp, const bool need_lock)\n{\n    int len;\n    int bytes;\n\n    FCFS_CAPI_CONVERT_FP_EX(fp, EOF);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n\n    len = strlen(s);\n    if (len == 0) {\n        bytes = 0;\n    } else {\n        bytes = fcfs_write(file->fd, s, len);\n        if (bytes <= 0) {\n            file->error_no = (errno != 0 ? errno : EIO);\n            bytes = EOF;\n        }\n    }\n\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n    return bytes;\n}\n\nstatic inline char *do_fgets(char *s, int size,\n        FILE *fp, const bool need_lock)\n{\n    size_t bytes;\n\n    FCFS_CAPI_CONVERT_FP_EX(fp, NULL);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n    bytes = fcfs_file_gets(file->fd, s, size);\n    if (bytes < 0) {\n        file->error_no = (errno != 0 ? errno : EIO);\n    } else if (bytes == 0) {\n        file->eof = 1;\n    }\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n\n    return (bytes > 0 ? s : NULL);\n}\n\nint fcfs_fgetc_unlocked(FILE *fp)\n{\n    const bool need_lock = false;\n    return do_fgetc(fp, need_lock);\n}\n\nint fcfs_fputc_unlocked(int c, FILE *fp)\n{\n    const bool need_lock = false;\n    return do_fputc(c, fp, need_lock);\n}\n\nvoid fcfs_clearerr_unlocked(FILE *fp)\n{\n    const bool need_lock = false;\n    return do_clearerr(fp, need_lock);\n}\n\nint fcfs_feof_unlocked(FILE *fp)\n{\n    const bool need_lock = false;\n    return do_feof(fp, need_lock);\n}\n\nint fcfs_ferror_unlocked(FILE *fp)\n{\n    const bool need_lock = false;\n    return do_ferror(fp, need_lock);\n}\n\nint fcfs_fileno_unlocked(FILE *fp)\n{\n    const bool need_lock = false;\n    return do_fileno(fp, need_lock);\n}\n\nint fcfs_fflush_unlocked(FILE *fp)\n{\n    const bool need_lock = false;\n    return do_fflush(fp, need_lock);\n}\n\nsize_t fcfs_fread_unlocked(void *buff,\n        size_t size, size_t n, FILE *fp)\n{\n    const bool need_lock = false;\n    return do_fread(buff, size, n, fp, need_lock);\n}\n\nsize_t fcfs_fwrite_unlocked(const void *buff,\n        size_t size, size_t n, FILE *fp)\n{\n    const bool need_lock = false;\n    return do_fwrite(buff, size, n, fp, need_lock);\n}\n\nint fcfs_fputs_unlocked(const char *s, FILE *fp)\n{\n    const bool need_lock = false;\n    return do_fputs(s, fp, need_lock);\n}\n\nchar *fcfs_fgets_unlocked(char *s, int size, FILE *fp)\n{\n    const bool need_lock = false;\n    return do_fgets(s, size, fp, need_lock);\n}\n\nint fcfs_fgetc(FILE *fp)\n{\n    const bool need_lock = true;\n    return do_fgetc(fp, need_lock);\n}\n\nint fcfs_fputc(int c, FILE *fp)\n{\n    const bool need_lock = true;\n    return do_fputc(c, fp, need_lock);\n}\n\nvoid fcfs_clearerr(FILE *fp)\n{\n    const bool need_lock = true;\n    return do_clearerr(fp, need_lock);\n}\n\nint fcfs_feof(FILE *fp)\n{\n    const bool need_lock = true;\n    return do_feof(fp, need_lock);\n}\n\nint fcfs_ferror(FILE *fp)\n{\n    const bool need_lock = true;\n    return do_ferror(fp, need_lock);\n}\n\nint fcfs_fileno(FILE *fp)\n{\n    const bool need_lock = true;\n    return do_fileno(fp, need_lock);\n}\n\nint fcfs_fflush(FILE *fp)\n{\n    const bool need_lock = true;\n    return do_fflush(fp, need_lock);\n}\n\nsize_t fcfs_fread(void *buff,\n        size_t size, size_t n, FILE *fp)\n{\n    const bool need_lock = true;\n    return do_fread(buff, size, n, fp, need_lock);\n}\n\nsize_t fcfs_fwrite(const void *buff,\n        size_t size, size_t n, FILE *fp)\n{\n    const bool need_lock = true;\n    return do_fwrite(buff, size, n, fp, need_lock);\n}\n\nint fcfs_fputs(const char *s, FILE *fp)\n{\n    const bool need_lock = true;\n    return do_fputs(s, fp, need_lock);\n}\n\nchar *fcfs_fgets(char *s, int size, FILE *fp)\n{\n    const bool need_lock = true;\n    return do_fgets(s, size, fp, need_lock);\n}\n\nint fcfs_ungetc(int c, FILE *fp)\n{\n    FCFS_CAPI_CONVERT_FP_EX(fp, EOF);\n\n    PTHREAD_MUTEX_LOCK(&file->lock);\n    if (c == EOF) {\n        file->error_no = EINVAL;\n    } else {\n        if (fcfs_lseek(file->fd, -1, SEEK_CUR) < 0) {\n            file->error_no = (errno != 0 ? errno : EIO);\n            c = EOF;\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(&file->lock);\n\n    return c;\n}\n\nint fcfs_fprintf(FILE *fp, const char *format, ...)\n{\n    va_list ap;\n    int bytes;\n\n    FCFS_CAPI_CONVERT_FP(fp);\n\n    va_start(ap, format);\n    bytes = fcfs_vdprintf(file->fd, format, ap);\n    va_end(ap);\n    return bytes;\n}\n\nint fcfs_vfprintf(FILE *fp, const char *format, va_list ap)\n{\n    FCFS_CAPI_CONVERT_FP(fp);\n    return fcfs_vdprintf(file->fd, format, ap);\n}\n\nssize_t fcfs_getdelim(char **line, size_t *size, int delim, FILE *fp)\n{\n    FCFS_CAPI_CONVERT_FP(fp);\n    return fcfs_file_getdelim(file->fd, line, size, delim);\n}\n\nstatic inline ssize_t do_readline(FILE *fp, char *buff,\n        size_t size, const bool need_lock)\n{\n    size_t bytes;\n\n    FCFS_CAPI_CONVERT_FP(fp);\n    if (need_lock) {\n        PTHREAD_MUTEX_LOCK(&file->lock);\n    }\n    bytes = fcfs_file_readline(file->fd, buff, size);\n    if (bytes < 0) {\n        file->error_no = (errno != 0 ? errno : EIO);\n    } else if (bytes == 0) {\n        file->eof = 1;\n    }\n    if (need_lock) {\n        PTHREAD_MUTEX_UNLOCK(&file->lock);\n    }\n\n    return bytes;\n}\n\nssize_t fcfs_readline(FILE *fp, char *buff, size_t size)\n{\n    const bool need_lock = true;\n    return do_readline(fp, buff, size, need_lock);\n}\n\nssize_t fcfs_readline_unlocked(FILE *fp, char *buff, size_t size)\n{\n    const bool need_lock = false;\n    return do_readline(fp, buff, size, need_lock);\n}\n"
  },
  {
    "path": "src/api/std/capi.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_CAPI_H\n#define _FCFS_CAPI_H\n\n#include \"api_types.h\"\n\n#define fcfs_fopen(path, mode) \\\n    fcfs_fopen_ex(&G_FCFS_PAPI_CTX, path, mode)\n\n#define fcfs_fdopen(fd, mode) \\\n    fcfs_fdopen_ex(&G_FCFS_PAPI_CTX, fd, mode)\n\n#define fcfs_freopen(path, mode, fp) \\\n    fcfs_freopen_ex(&G_FCFS_PAPI_CTX, path, mode, fp)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    int fcfs_capi_init();\n\n    void fcfs_capi_destroy();\n\n    FILE *fcfs_fopen_ex(FCFSPosixAPIContext *ctx,\n            const char *path, const char *mode);\n\n    FILE *fcfs_fdopen_ex(FCFSPosixAPIContext *ctx, int fd, const char *mode);\n\n    FILE *fcfs_freopen_ex(FCFSPosixAPIContext *ctx,\n            const char *path, const char *mode, FILE *fp);\n\n    int fcfs_fclose(FILE *fp);\n\n    int fcfs_fcloseall();\n\n    void fcfs_flockfile(FILE *fp);\n\n    int fcfs_ftrylockfile(FILE *fp);\n\n    void fcfs_funlockfile(FILE *fp);\n\n    int fcfs_fseek(FILE *fp, long offset, int whence);\n\n    int fcfs_fseeko(FILE *fp, off_t offset, int whence);\n\n    long fcfs_ftell(FILE *fp);\n\n    off_t fcfs_ftello(FILE *fp);\n\n    void fcfs_rewind(FILE *fp);\n\n    int fcfs_fgetpos(FILE *fp, fpos_t *pos);\n\n    int fcfs_fsetpos(FILE *fp, const fpos_t *pos);\n\n    int fcfs_fgetc_unlocked(FILE *fp);\n\n    int fcfs_fputc_unlocked(int c, FILE *fp);\n\n    static inline int fcfs_getc_unlocked(FILE *fp)\n    {\n        return fcfs_fgetc_unlocked(fp);\n    }\n\n    static inline int fcfs_putc_unlocked(int c, FILE *fp)\n    {\n        return fcfs_fputc_unlocked(c, fp);\n    }\n\n    void fcfs_clearerr_unlocked(FILE *fp);\n\n    int fcfs_feof_unlocked(FILE *fp);\n\n    int fcfs_ferror_unlocked(FILE *fp);\n\n    int fcfs_fileno_unlocked(FILE *fp);\n\n    int fcfs_fflush_unlocked(FILE *fp);\n\n    size_t fcfs_fread_unlocked(void *buff,\n            size_t size, size_t n, FILE *fp);\n\n    size_t fcfs_fwrite_unlocked(const void *buff,\n            size_t size, size_t n, FILE *fp);\n\n    //with terminating null byte ('\\0')\n    char *fcfs_fgets_unlocked(char *s, int size, FILE *fp);\n\n    int fcfs_fputs_unlocked(const char *s, FILE *fp);\n\n    void fcfs_clearerr(FILE *fp);\n\n    int fcfs_feof(FILE *fp);\n\n    int fcfs_ferror(FILE *fp);\n\n    int fcfs_fileno(FILE *fp);\n\n    int fcfs_fgetc(FILE *fp);\n\n    //with terminating null byte ('\\0')\n    char *fcfs_fgets(char *s, int size, FILE *fp);\n\n    int fcfs_getc(FILE *fp);\n\n    int fcfs_ungetc(int c, FILE *fp);\n\n    int fcfs_fputc(int c, FILE *fp);\n\n    int fcfs_fputs(const char *s, FILE *fp);\n\n    int fcfs_putc(int c, FILE *fp);\n\n    size_t fcfs_fread(void *buff, size_t size, size_t nmemb, FILE *fp);\n\n    size_t fcfs_fwrite(const void *buff, size_t size, size_t nmemb, FILE *fp);\n\n    int fcfs_fprintf(FILE *fp, const char *format, ...)\n        __gcc_attribute__ ((format (printf, 2, 3)));\n\n    int fcfs_vfprintf(FILE *fp, const char *format, va_list ap);\n\n    ssize_t fcfs_getdelim(char **line, size_t *size, int delim, FILE *fp);\n\n    static inline ssize_t fcfs_getline(char **line, size_t *size, FILE *fp)\n    {\n        return fcfs_getdelim(line, size, '\\n', fp);\n    }\n\n    //without terminating null byte ('\\0')\n    ssize_t fcfs_readline(FILE *fp, char *buff, size_t size);\n\n    //without terminating null byte ('\\0')\n    ssize_t fcfs_readline_unlocked(FILE *fp, char *buff, size_t size);\n\n    //TODO\n    /*\n    int fcfs_fscanf(FILE *fp, const char *format, ...)\n        __gcc_attribute__ ((format (scanf, 2, 3)));\n\n    int fcfs_vfscanf(FILE *fp, const char *format, va_list ap);\n    */\n\n    int fcfs_setvbuf(FILE *fp, char *buf, int mode, size_t size);\n\n    static inline void fcfs_setbuf(FILE *fp, char *buf)\n    {\n        fcfs_setvbuf(fp, buf, (buf != NULL ? _IOFBF : _IONBF), BUFSIZ);\n    }\n\n    static inline void fcfs_setbuffer(FILE *fp, char *buf, size_t size)\n    {\n        fcfs_setvbuf(fp, buf, (buf != NULL ? _IOFBF : _IONBF), size);\n    }\n\n    static inline void fcfs_setlinebuf(FILE *fp)\n    {\n        fcfs_setvbuf(fp, NULL, _IOLBF, 0);\n    }\n\n    int fcfs_fflush(FILE *fp);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/std/fd_manager.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"fastcommon/fast_mblock.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fd_manager.h\"\n\n#define FILE_INFO_ALLOC_ONCE  1024\n\ntypedef struct fcfs_fd_manager_context {\n    volatile int generation;\n    volatile int in_realloc;\n    volatile int next_fd;\n    struct fast_mblock_man file_info_allocator;  //element: FCFSPosixAPIFileInfo\n    FCFSPosixFilePtrArray file_parray;\n} FCFSFDManagerContext;\n\nstatic FCFSFDManagerContext fd_manager_ctx;\n\n#define PARRAY_IN_REALLOC fd_manager_ctx.in_realloc\n#define PARRAY_GENERATION fd_manager_ctx.generation\n#define FINFO_ALLOCATOR   fd_manager_ctx.file_info_allocator\n#define PAPI_NEXT_FD      fd_manager_ctx.next_fd\n#define FILE_PARRAY       fd_manager_ctx.file_parray\n\nstatic int file_parray_realloc()\n{\n    FCFSPosixAPIFileInfo **new_files;\n    volatile FCFSPosixAPIFileInfo **old_files;\n    int new_count;\n    int old_count;\n    int bytes;\n\n    if (!__sync_bool_compare_and_swap(&PARRAY_IN_REALLOC, 0, 1)) {\n        return 0;\n    }\n\n    old_count = FC_ATOMIC_GET(FILE_PARRAY.count);\n    new_count = old_count + FILE_INFO_ALLOC_ONCE;\n    bytes = sizeof(FCFSPosixAPIFileInfo *) * new_count;\n    new_files = (FCFSPosixAPIFileInfo **)fc_malloc(bytes);\n    if (new_files == NULL) {\n        __sync_bool_compare_and_swap(&PARRAY_IN_REALLOC, 1, 0);\n        return ENOMEM;\n    }\n    memset(new_files, 0, bytes);\n\n    old_files = FC_ATOMIC_GET(FILE_PARRAY.files);\n    if (old_count > 0) {\n        memcpy(new_files, old_files, old_count *\n                sizeof(FCFSPosixAPIFileInfo *));\n    }\n\n    __sync_bool_compare_and_swap(&FILE_PARRAY.files, old_files,\n            (volatile FCFSPosixAPIFileInfo **)new_files);\n    __sync_bool_compare_and_swap(&FILE_PARRAY.count,\n            old_count, new_count);\n    FC_ATOMIC_INC(PARRAY_GENERATION);\n    __sync_bool_compare_and_swap(&PARRAY_IN_REALLOC, 1, 0);\n\n    if (old_files != NULL) {\n        usleep(100000);   //delay free\n        free(old_files);\n    }\n\n    return 0;\n}\n\nstatic int finfo_init_func(FCFSPosixAPIFileInfo *finfo, void *args)\n{\n    int elt_count;\n\n    finfo->fd = __sync_add_and_fetch(&PAPI_NEXT_FD, 1);\n    elt_count = finfo->fd - FCFS_POSIX_API_FD_BASE;\n    if (elt_count <= FILE_PARRAY.count) {\n        return 0;\n    } else {\n        return file_parray_realloc();\n    }\n}\n\nint fcfs_fd_manager_init()\n{\n    int result;\n\n    __sync_add_and_fetch(&PAPI_NEXT_FD, FCFS_POSIX_API_FD_BASE);\n    if ((result=fast_mblock_init_ex1(&FINFO_ALLOCATOR, \"papi_file_info\",\n                    sizeof(FCFSPosixAPIFileInfo), FILE_INFO_ALLOC_ONCE, 0,\n                    (fast_mblock_object_init_func)finfo_init_func,\n                    NULL, true)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nvoid fcfs_fd_manager_destroy()\n{\n    fast_mblock_destroy(&FINFO_ALLOCATOR);\n\n    free(FILE_PARRAY.files);\n    FILE_PARRAY.count = 0;\n}\n\n#define FCFS_FD_TO_INDEX(fd) ((fd - FCFS_POSIX_API_FD_BASE) - 1)\n\nstatic inline int set_file_info(const int index,\n        FCFSPosixAPIFileInfo *old_finfo,\n        FCFSPosixAPIFileInfo *new_finfo)\n{\n    int old_generation;\n\n    do {\n        old_generation = FC_ATOMIC_GET(PARRAY_GENERATION);\n        if (!__sync_bool_compare_and_swap(&FILE_PARRAY.\n                    files[index], old_finfo, new_finfo))\n        {\n            return EBUSY;\n        }\n    } while (FC_ATOMIC_GET(PARRAY_IN_REALLOC) || old_generation !=\n            FC_ATOMIC_GET(PARRAY_GENERATION));\n    return 0;\n}\n\nFCFSPosixAPIFileInfo *fcfs_fd_manager_alloc(const char *filename)\n{\n    FCFSPosixAPIFileInfo *finfo;\n\n    if ((finfo=fast_mblock_alloc_object(&FINFO_ALLOCATOR)) == NULL) {\n        return finfo;\n    }\n\n    finfo->filename.len = strlen(filename);\n    finfo->filename.str = (char *)fc_malloc(finfo->filename.len + 2);\n    if (finfo->filename.str == NULL) {\n        fast_mblock_free_object(&FINFO_ALLOCATOR, finfo);\n        return NULL;\n    }\n    memcpy(finfo->filename.str, filename, finfo->filename.len + 1);\n    if (set_file_info(FCFS_FD_TO_INDEX(finfo->fd), NULL, finfo) == 0) {\n        return finfo;\n    } else {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"set file info fail, filename: %s, fd: %d\",\n                __LINE__, filename, finfo->fd);\n        free(finfo->filename.str);\n        FC_SET_STRING_NULL(finfo->filename);\n        fast_mblock_free_object(&FINFO_ALLOCATOR, finfo);\n        return NULL;\n    }\n}\n\nFCFSPosixAPIFileInfo *fcfs_fd_manager_get(const int fd)\n{\n    int result;\n    int index;\n    FCFSPosixAPIFileInfo *finfo;\n\n    index = FCFS_FD_TO_INDEX(fd);\n    if (index >= 0 && index < FC_ATOMIC_GET(FILE_PARRAY.count)) {\n        finfo = (FCFSPosixAPIFileInfo *)FC_ATOMIC_GET(\n                FILE_PARRAY.files[index]);\n        result = finfo != NULL ? 0 : ENOENT;\n    } else {\n        finfo = NULL;\n        result = EOVERFLOW;\n    }\n\n    if (result != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"get file info fail, fd: %d, errno: %d, error info: %s\",\n                __LINE__, fd, result, STRERROR(result));\n    }\n    return finfo;\n}\n\nint fcfs_fd_manager_free(FCFSPosixAPIFileInfo *finfo)\n{\n    int result;\n    int index;\n\n    index = FCFS_FD_TO_INDEX(finfo->fd);\n    if (index >= 0 && index < FC_ATOMIC_GET(FILE_PARRAY.count)) {\n        if (finfo == FC_ATOMIC_GET(FILE_PARRAY.files[index])) {\n            result = set_file_info(index, finfo, NULL);\n        } else {\n            result = ENOENT;\n        }\n    } else {\n        result = EOVERFLOW;\n    }\n\n    if (result == 0) {\n        free(finfo->filename.str);\n        FC_SET_STRING_NULL(finfo->filename);\n        fast_mblock_free_object(&FINFO_ALLOCATOR, finfo);\n    } else {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"free file info fail, fd: %d, errno: %d, error info: %s\",\n                __LINE__, finfo->fd, result, STRERROR(result));\n    }\n    return result;\n}\n"
  },
  {
    "path": "src/api/std/fd_manager.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_FD_MANAGER_H\n#define _FCFS_FD_MANAGER_H\n\n#include \"api_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    int fcfs_fd_manager_init();\n\n    void fcfs_fd_manager_destroy();\n\n    FCFSPosixAPIFileInfo *fcfs_fd_manager_alloc(const char *filename);\n\n    FCFSPosixAPIFileInfo *fcfs_fd_manager_get(const int fd);\n\n    static inline void fcfs_fd_manager_normalize_path(\n            FCFSPosixAPIFileInfo *finfo)\n    {\n        if (!(finfo->filename.len > 0 &&  finfo->filename.str\n                    [finfo->filename.len - 1] == '/'))\n        {\n            *(finfo->filename.str + finfo->filename.len++) = '/';\n            *(finfo->filename.str + finfo->filename.len) = '\\0';\n        }\n    }\n\n    int fcfs_fd_manager_free(FCFSPosixAPIFileInfo *finfo);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/std/papi.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdarg.h>\n#include <sys/uio.h>\n#include <sys/stat.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"posix_api.h\"\n#include \"papi.h\"\n\n#define FCFS_PAPI_MAGIC_NUMBER    1644551636\n\nstatic FCFSPosixAPIFileInfo *do_open_ex(FCFSPosixAPIContext *ctx,\n        const char *path, const int flags, const int mode,\n        const FCFSPosixAPITPIDType tpid_type)\n{\n    FCFSPosixAPIFileInfo *file;\n    FCFSAPIFileContext fctx;\n    int result;\n\n    if ((file=fcfs_fd_manager_alloc(path)) == NULL) {\n        errno = ENOMEM;\n        return NULL;\n    }\n\n    fcfs_posix_api_set_fctx(&fctx, ctx, mode, tpid_type);\n    if ((result=fcfs_api_open_ex(&ctx->api_ctx, &file->fi,\n                    path, flags, &fctx)) != 0)\n    {\n        fcfs_fd_manager_free(file);\n        errno = result;\n        return NULL;\n    }\n\n    if (S_ISDIR(file->fi.dentry.stat.mode)) {\n        fcfs_fd_manager_normalize_path(file);\n    }\n\n    return file;\n}\n\nstatic inline int do_open(FCFSPosixAPIContext *ctx, const char *path,\n        const int flags, const int mode, const FCFSPosixAPITPIDType tpid_type)\n{\n    FCFSPosixAPIFileInfo *file;\n\n    file = do_open_ex(ctx, path, flags, mode, tpid_type);\n    if (file != NULL) {\n        file->tpid_type = tpid_type;\n        return file->fd;\n    } else {\n        return -1;\n    }\n}\n\nstatic inline char *do_getcwd(FCFSPosixAPIContext *ctx,\n        string_t *cwd, size_t size, const int overflow_errno)\n{\n    if (G_FCFS_PAPI_CWD == NULL) {\n        if (ctx->mountpoint.len >= size) {\n            errno = overflow_errno;\n            cwd->len = 0;\n            return NULL;\n        }\n\n        cwd->len = ctx->mountpoint.len;\n        memcpy(cwd->str, ctx->mountpoint.str, ctx->mountpoint.len);\n        *(cwd->str + cwd->len) = '\\0';\n        return cwd->str;\n    }\n\n    if (ctx->mountpoint.len + G_FCFS_PAPI_CWD->len >= size) {\n        errno = overflow_errno;\n        cwd->len = 0;\n        return NULL;\n    }\n\n    cwd->len = ctx->mountpoint.len + G_FCFS_PAPI_CWD->len;\n    memcpy(cwd->str, ctx->mountpoint.str, ctx->mountpoint.len);\n    memcpy(cwd->str + ctx->mountpoint.len, G_FCFS_PAPI_CWD->str,\n            G_FCFS_PAPI_CWD->len);\n    *(cwd->str + cwd->len) = '\\0';\n    return cwd->str;\n}\n\nstatic inline int do_getcwd1(FCFSPosixAPIContext *ctx,\n        string_t *cwd, size_t size)\n{\n    if (do_getcwd(ctx, cwd, size, EOVERFLOW) == NULL) {\n        return -1;\n    }\n\n    if (cwd->len == 0 || cwd->str[cwd->len - 1] != '/') {\n        cwd->str[cwd->len++] = '/';\n    }\n    return 0;\n}\n\nstatic inline int papi_resolve_path(FCFSPosixAPIContext *ctx,\n        const char *func, const char **path,\n        char *full_filename, const int size)\n{\n    char cwd[PATH_MAX];\n    string_t from_string;\n    string_t path_string;\n\n    if (**path != '/') {\n        from_string.str = cwd;\n        if (do_getcwd1(ctx, &from_string, sizeof(cwd)) != 0) {\n            return -1;\n        }\n        FC_SET_STRING(path_string, (char *)*path);\n        normalize_path(&from_string, &path_string, full_filename, size);\n        *path = full_filename;\n    }\n\n    FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, *path, func);\n    *path += ctx->mountpoint.len;\n    return 0;\n}\n\nstatic int papi_resolve_pathat(FCFSPosixAPIContext *ctx, const char *func,\n        int fd, const char **path, char *full_filename, const int size)\n{\n    FCFSPosixAPIFileInfo *file;\n    char cwd[PATH_MAX];\n    string_t from_string;\n    string_t path_string;\n\n    if (fd == AT_FDCWD) {\n        from_string.str = cwd;\n        if (do_getcwd1(ctx, &from_string, sizeof(cwd)) != 0) {\n            return -1;\n        }\n        FC_SET_STRING(path_string, (char *)*path);\n        normalize_path(&from_string, &path_string, full_filename, size);\n        FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, full_filename, func);\n        *path = full_filename + ctx->mountpoint.len;\n        return 0;\n    }\n\n    if (**path == '/') {\n        FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, *path, func);\n        *path += ctx->mountpoint.len;\n        return 0;\n    }\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    normalize_path1(file->filename.str, *path, full_filename, size);\n    *path = full_filename;\n    return 0;\n}\n\nint fcfs_open_ex(FCFSPosixAPIContext *ctx, const char *path, int flags, ...)\n{\n    char full_fname[PATH_MAX];\n    va_list ap;\n    int fd;\n    int mode;\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"open\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    va_start(ap, flags);\n    mode = va_arg(ap, int);\n    fd = do_open(ctx, path, flags, mode, fcfs_papi_tpid_type_tid);\n    va_end(ap);\n    return fd;\n}\n\nint fcfs_file_open(FCFSPosixAPIContext *ctx, const char *path,\n        const int flags, const int mode, const\n        FCFSPosixAPITPIDType tpid_type)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"open\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    return do_open(ctx, path, flags, mode, tpid_type);\n}\n\nint fcfs_openat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, int flags, ...)\n{\n    char full_fname[PATH_MAX];\n    va_list ap;\n    int mode;\n    int new_fd;\n    int result;\n\n    if ((result=papi_resolve_pathat(ctx, \"openat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    va_start(ap, flags);\n    mode = va_arg(ap, int);\n    new_fd = do_open(ctx, path, flags, mode, fcfs_papi_tpid_type_tid);\n    va_end(ap);\n    return new_fd;\n}\n\nint fcfs_creat_ex(FCFSPosixAPIContext *ctx, const char *path, mode_t mode)\n{\n    return fcfs_open_ex(ctx, path, O_CREAT | O_TRUNC | O_WRONLY, mode);\n}\n\nint fcfs_close(int fd)\n{\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    fcfs_api_close(&file->fi);\n    fcfs_fd_manager_free(file);\n    return 0;\n}\n\nint fcfs_fsync(int fd)\n{\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    return fcfs_api_fsync(&file->fi,\n            fcfs_posix_api_gettid(\n                file->tpid_type));\n}\n\nint fcfs_fdatasync(int fd)\n{\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    return fcfs_api_fdatasync(&file->fi,\n            fcfs_posix_api_gettid(\n                file->tpid_type));\n}\n\nstatic inline ssize_t do_write(FCFSPosixAPIFileInfo *file,\n        const void *buff, size_t count)\n{\n    int result;\n    int write_bytes;\n\n    if ((result=fcfs_api_write_ex(&file->fi, buff, count, &write_bytes,\n                    fcfs_posix_api_gettid(file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return write_bytes;\n    }\n}\n\nssize_t fcfs_write(int fd, const void *buff, size_t count)\n{\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    return do_write(file, buff, count);\n}\n\nssize_t fcfs_file_write(int fd, const void *buff, size_t size, size_t n)\n{\n    FCFSPosixAPIFileInfo *file;\n    size_t expect;\n    size_t bytes;\n    size_t count;\n    size_t remain;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    expect = size * n;\n    bytes = do_write(file, buff, expect);\n    if (bytes == expect) {\n        count = n;\n    } else if (bytes > 0) {\n        count = bytes / size;\n        remain = bytes - (size * count);\n        fcfs_api_lseek(&file->fi, -1 * remain, SEEK_CUR);\n    } else {\n        count = 0;\n    }\n\n    return count;\n}\n\nssize_t fcfs_pwrite(int fd, const void *buff, size_t count, off_t offset)\n{\n    int result;\n    int write_bytes;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_pwrite_ex(&file->fi, buff, count, offset,\n                    &write_bytes, fcfs_posix_api_gettid(\n                        file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return write_bytes;\n    }\n}\n\nssize_t fcfs_writev(int fd, const struct iovec *iov, int iovcnt)\n{\n    int result;\n    int write_bytes;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_writev_ex(&file->fi, iov, iovcnt, &write_bytes,\n                    fcfs_posix_api_gettid(file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return write_bytes;\n    }\n}\n\nssize_t fcfs_pwritev(int fd, const struct iovec *iov,\n        int iovcnt, off_t offset)\n{\n    int result;\n    int write_bytes;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_pwritev_ex(&file->fi, iov, iovcnt, offset,\n                    &write_bytes, fcfs_posix_api_gettid(\n                        file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return write_bytes;\n    }\n}\n\nstatic inline ssize_t do_read(FCFSPosixAPIFileInfo *file,\n        void *buff, size_t count)\n{\n    int result;\n    int read_bytes;\n\n    if ((result=fcfs_api_read_ex(&file->fi, buff, count, &read_bytes,\n                    fcfs_posix_api_gettid(file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return read_bytes;\n    }\n}\n\nssize_t fcfs_read(int fd, void *buff, size_t count)\n{\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    return do_read(file, buff, count);\n}\n\nssize_t fcfs_readahead(int fd, off64_t offset, size_t count)\n{\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    return 0;\n}\n\nssize_t fcfs_file_read(int fd, void *buff, size_t size, size_t n)\n{\n    FCFSPosixAPIFileInfo *file;\n    size_t expect;\n    size_t bytes;\n    size_t count;\n    size_t remain;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    expect = size * n;\n    bytes = do_read(file, buff, expect);\n    if (bytes == expect) {\n        count = n;\n    } else if (bytes > 0) {\n        count = bytes / size;\n        remain = bytes - (size * count);\n        fcfs_api_lseek(&file->fi, -1 * remain, SEEK_CUR);\n    } else {\n        count = 0;\n    }\n\n    return count;\n}\n\nssize_t fcfs_file_readline(int fd, char *s, size_t size)\n{\n    FCFSPosixAPIFileInfo *file;\n    char *p;\n    char *end;\n    char *ch;\n    int read_bytes_once;\n    int current;\n    int bytes;\n    int remain;\n\n    if (size <= 0) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    read_bytes_once = 64;\n    remain = size;\n    p = s;\n    while (1) {\n        current = FC_MIN(remain, read_bytes_once);\n        bytes = do_read(file, p, current);\n        if (bytes < 0) {\n            return -1;\n        } else if (bytes == 0) {\n            break;\n        }\n\n        end = p + bytes;\n        ch = p;\n        while (ch < end && *ch != '\\n') {\n            ++ch;\n        }\n\n        if (ch < end) {  //found\n            p = ++ch; //skip the new line\n            remain = end - p;\n            if (remain > 0) {\n                fcfs_api_lseek(&file->fi, -1 * remain, SEEK_CUR);\n            }\n            break;\n        }\n\n        p = end;\n        if (bytes < current) {  //end of file\n            break;\n        }\n\n        remain -= bytes;\n        if (remain == 0) {\n            break;\n        }\n\n        read_bytes_once *= 2;\n    }\n\n    return (p - s);\n}\n\nssize_t fcfs_file_gets(int fd, char *s, size_t size)\n{\n    ssize_t bytes;\n\n    if ((bytes=fcfs_file_readline(fd, s, size - 1)) >= 0) {\n        *(s + bytes) = '\\0';\n    }\n    return bytes;\n}\n\nssize_t fcfs_file_getdelim(int fd, char **line, size_t *size, int delim)\n{\n    FCFSPosixAPIFileInfo *file;\n    char *buff;\n    char *p;\n    char *end;\n    char *ch;\n    ssize_t alloc;\n    ssize_t bytes;\n    ssize_t len;\n    ssize_t remain;\n\n    if (*line != NULL && *size < 0) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if (*size < 128) {\n        *size = 128;\n        buff = fc_malloc(*size);\n        if (buff == NULL) {\n            errno = ENOMEM;\n            return -1;\n        }\n        if (*line != NULL) {\n            free(*line);\n        }\n        *line = buff;\n    } else {\n        buff = *line;\n    }\n\n    remain = *size - 1;\n    p = buff;\n    while (1) {\n        bytes = do_read(file, p, remain);\n        if (bytes < 0) {\n            return -1;\n        } else if (bytes == 0) {\n            break;\n        }\n\n        end = p + bytes;\n        ch = p;\n        while (ch < end && *ch != delim) {\n            ++ch;\n        }\n\n        if (ch < end) {  //found\n            p = ++ch; //skip the new line\n            remain = end - p;\n            if (remain > 0) {\n                fcfs_api_lseek(&file->fi, -1 * remain, SEEK_CUR);\n            }\n            break;\n        }\n\n        if (bytes < remain) {  //end of file\n            p = end;\n            break;\n        }\n\n        len = end - buff;\n        alloc = (*size) * 2;\n        buff = fc_malloc(alloc);\n        if (buff == NULL) {\n            errno = ENOMEM;\n            return -1;\n        }\n        memcpy(buff, *line, len);\n        free(*line);\n\n        *line = buff;\n        *size = alloc;\n        p = buff + len;\n        remain = (*size) - len;\n    }\n\n    *p = '\\0';\n    return (p - buff);\n}\n\nssize_t fcfs_pread(int fd, void *buff, size_t count, off_t offset)\n{\n    int result;\n    int read_bytes;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_pread_ex(&file->fi, buff, count, offset,\n                    &read_bytes, fcfs_posix_api_gettid(\n                        file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return read_bytes;\n    }\n}\n\nssize_t fcfs_readv(int fd, const struct iovec *iov, int iovcnt)\n{\n    int result;\n    int read_bytes;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_readv_ex(&file->fi, iov, iovcnt, &read_bytes,\n                    fcfs_posix_api_gettid(file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return read_bytes;\n    }\n}\n\nssize_t fcfs_preadv(int fd, const struct iovec *iov,\n        int iovcnt, off_t offset)\n{\n    int result;\n    int read_bytes;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_preadv_ex(&file->fi, iov, iovcnt, offset,\n                    &read_bytes, fcfs_posix_api_gettid(\n                        file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return read_bytes;\n    }\n}\n\noff_t fcfs_lseek(int fd, off_t offset, int whence)\n{\n    int result;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_lseek(&file->fi, offset, whence)) != 0) {\n        errno = result;\n        return -1;\n    } else {\n        return file->fi.offset;\n    }\n}\n\noff_t fcfs_ltell(int fd)\n{\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    return file->fi.offset;\n}\n\nint fcfs_fallocate(int fd, int mode, off_t offset, off_t length)\n{\n    int result;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_fallocate_ex(&file->fi, mode, offset, length,\n                    fcfs_posix_api_gettid(file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_truncate_ex(FCFSPosixAPIContext *ctx,\n        const char *path, off_t length)\n{\n    FCFSAPIFileContext fctx;\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"truncate\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    fcfs_posix_api_set_fctx(&fctx, ctx, ACCESSPERMS,\n            fcfs_papi_tpid_type_tid);\n    if ((result=fcfs_api_truncate_ex(&ctx->api_ctx,\n                    path, length, &fctx)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_ftruncate(int fd, off_t length)\n{\n    int result;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_ftruncate_ex(&file->fi, length,\n                    fcfs_posix_api_gettid(file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_lstat_ex(FCFSPosixAPIContext *ctx,\n        const char *path, struct stat *buf)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"lstat\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_lstat_ex(&ctx->api_ctx, path,\n                    &ctx->api_ctx.owner.oper, buf)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_stat_ex(FCFSPosixAPIContext *ctx,\n        const char *path, struct stat *buf)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"stat\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_stat_ex(&ctx->api_ctx, path, &ctx->\n                    api_ctx.owner.oper, buf, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_fstat(int fd, struct stat *buf)\n{\n    int result;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_fstat(&file->fi, buf)) != 0) {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_fstatat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, struct stat *buf, int flags)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_pathat(ctx, \"fstatat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_stat_ex(&ctx->api_ctx, path, &ctx->api_ctx.owner.\n                    oper, buf, ((flags & AT_SYMLINK_NOFOLLOW) != 0 ? 0 :\n                        FDIR_FLAGS_FOLLOW_SYMLINK))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_flock(int fd, int operation)\n{\n    FCFSPosixAPIFileInfo *file;\n    int result;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_flock(&file->fi, operation)) != 0) {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nstatic int do_fcntl(FCFSPosixAPIFileInfo *file, int cmd, void *arg)\n{\n    int64_t owner_id;\n    struct flock *lock;\n    int flags;\n    int result;\n\n\n    switch (cmd) {\n        /*\n        case F_DUPFD:\n            errno = EOPNOTSUPP;\n            return -1;\n        */\n        case F_GETFD:\n        case F_SETFD:\n            return 0;\n        case F_GETFL:\n            return file->fi.flags;\n        case F_SETFL:\n            flags = (long)arg;\n            result = fcfs_api_set_file_flags(&file->fi, flags);\n            break;\n        case F_GETLK:\n        case F_SETLK:\n        case F_SETLKW:\n            lock = (struct flock *)arg;\n            if (cmd == F_GETLK) {\n                result = fcfs_api_getlk_ex(&file->fi, lock, &owner_id);\n            } else {\n                owner_id = fc_gettid();\n                result = fcfs_api_setlk_ex(&file->fi, lock,\n                        owner_id, (cmd == F_SETLKW));\n            }\n            break;\n        default:\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"unsupport fcntl cmd: %d\", __LINE__, cmd);\n            errno = EOPNOTSUPP;\n            return -1;\n    }\n\n    if (result != 0) {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_fcntl(int fd, int cmd, ...)\n{\n    FCFSPosixAPIFileInfo *file;\n    va_list ap;\n    void *arg;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    va_start(ap, cmd);\n    arg = va_arg(ap, void *);\n    va_end(ap);\n    return do_fcntl(file, cmd, arg);\n}\n\nint fcfs_symlink_ex(FCFSPosixAPIContext *ctx,\n        const char *link, const char *path)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if (*link == '/') {\n        FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, link, \"symlink\");\n        link += ctx->mountpoint.len;\n    }\n\n    if ((result=papi_resolve_path(ctx, \"symlink\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_symlink_ex(&ctx->api_ctx, link, path,\n                    &ctx->api_ctx.owner.oper, ACCESSPERMS)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_symlinkat_ex(FCFSPosixAPIContext *ctx, const char *link,\n        int fd, const char *path)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if (*link == '/') {\n        FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, link, \"symlinkat\");\n        link += ctx->mountpoint.len;\n    }\n\n    if ((result=papi_resolve_pathat(ctx, \"symlinkat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_symlink_ex(&ctx->api_ctx, link, path,\n                    &ctx->api_ctx.owner.oper, ACCESSPERMS)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_link_ex(FCFSPosixAPIContext *ctx,\n        const char *path1, const char *path2)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    char full_fname1[PATH_MAX];\n    char full_fname2[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"linkat\", &path1,\n                    full_fname1, sizeof(full_fname1))) != 0)\n    {\n        return result;\n    }\n    if ((result=papi_resolve_path(ctx, \"linkat\", &path2,\n                    full_fname2, sizeof(full_fname2))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_link_ex(&ctx->api_ctx, path1, path2, &ctx->\n                    api_ctx.owner.oper, ACCESSPERMS, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_linkat_ex(FCFSPosixAPIContext *ctx, int fd1,\n        const char *path1, int fd2, const char *path2, int flags)\n{\n    char full_fname1[PATH_MAX];\n    char full_fname2[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_pathat(ctx, \"linkat\", fd1, &path1,\n                    full_fname1, sizeof(full_fname1))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=papi_resolve_pathat(ctx, \"linkat\", fd2, &path2,\n                    full_fname2, sizeof(full_fname2))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_link_ex(&ctx->api_ctx, path1, path2,\n                    &ctx->api_ctx.owner.oper, ACCESSPERMS,\n                    ((flags & AT_SYMLINK_FOLLOW) != 0) ?\n                    FDIR_FLAGS_FOLLOW_SYMLINK : 0)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nssize_t fcfs_readlink_ex(FCFSPosixAPIContext *ctx,\n        const char *path, char *buff, size_t size)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"readlink\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_readlink(&ctx->api_ctx, path, &ctx->\n                    api_ctx.owner.oper, buff, size)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nssize_t fcfs_readlinkat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, char *buff, size_t size)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_pathat(ctx, \"readlinkat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_readlink(&ctx->api_ctx, path, &ctx->\n                    api_ctx.owner.oper, buff, size)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_mknod_ex(FCFSPosixAPIContext *ctx,\n        const char *path, mode_t mode, dev_t dev)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"mknod\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_mknod_ex(&ctx->api_ctx, path, &ctx->\n                    api_ctx.owner.oper, mode, dev)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_mknodat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, mode_t mode, dev_t dev)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_pathat(ctx, \"mknodat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_mknod_ex(&ctx->api_ctx, path, &ctx->\n                    api_ctx.owner.oper, mode, dev)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_mkfifo_ex(FCFSPosixAPIContext *ctx,\n        const char *path, mode_t mode)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"mkfifo\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_mkfifo_ex(&ctx->api_ctx, path,\n                    &ctx->api_ctx.owner.oper, mode)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_mkfifoat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, mode_t mode)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_pathat(ctx, \"mkfifoat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_mkfifo_ex(&ctx->api_ctx, path,\n                    &ctx->api_ctx.owner.oper, mode)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_access_ex(FCFSPosixAPIContext *ctx,\n        const char *path, int mode)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"access\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_access_ex(&ctx->api_ctx, path, mode,\n                    &ctx->api_ctx.owner.oper, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_faccessat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, int mode, int flags)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_pathat(ctx, \"faccessat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_access_ex(&ctx->api_ctx, path,\n                    mode, &ctx->api_ctx.owner.oper,\n                    (flags & AT_SYMLINK_NOFOLLOW) ? 0 :\n                    FDIR_FLAGS_FOLLOW_SYMLINK)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_euidaccess_ex(FCFSPosixAPIContext *ctx,\n        const char *path, int mode)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"euidaccess\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_euidaccess_ex(&ctx->api_ctx, path, mode,\n                    &ctx->api_ctx.owner.oper, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_utime_ex(FCFSPosixAPIContext *ctx, const char *path,\n        const struct utimbuf *times)\n{\n    char full_fname[PATH_MAX];\n    int result;\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_path(ctx, \"utime\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_utime_ex(&ctx->api_ctx, &fname, times)) != 0) {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_utimes_ex(FCFSPosixAPIContext *ctx, const char *path,\n        const struct timeval times[2])\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    char full_fname[PATH_MAX];\n    int result;\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_path(ctx, \"utimes\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_utimes_by_path_ex(&ctx->api_ctx,\n                    &fname, times, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_futimes_ex(FCFSPosixAPIContext *ctx,\n        int fd, const struct timeval times[2])\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    FCFSPosixAPIFileInfo *file;\n    FDIRClientOperInodePair oino;\n    int result;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, ctx->api_ctx.\n            owner.oper, file->fi.dentry.inode);\n    if ((result=fcfs_api_utimes_by_inode_ex(&ctx->api_ctx,\n                    &oino, times, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_futimesat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, const struct timeval times[2])\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    char full_fname[PATH_MAX];\n    int result;\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_pathat(ctx, \"futimesat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_utimes_by_path_ex(&ctx->api_ctx,\n                    &fname, times, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_futimens_ex(FCFSPosixAPIContext *ctx, int fd,\n        const struct timespec times[2])\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    FCFSPosixAPIFileInfo *file;\n    int result;\n    FDIRClientOperInodePair oino;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, ctx->api_ctx.\n            owner.oper, file->fi.dentry.inode);\n    if ((result=fcfs_api_utimens_by_inode_ex(&ctx->api_ctx,\n                    &oino, times, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_utimensat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, const struct timespec times[2], int flags)\n{\n    char full_fname[PATH_MAX];\n    int result;\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_pathat(ctx, \"futimensat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_utimens_by_path_ex(&ctx->api_ctx, &fname,\n                    times, (flags & AT_SYMLINK_NOFOLLOW) ?\n                    0 : FDIR_FLAGS_FOLLOW_SYMLINK)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_unlink_ex(FCFSPosixAPIContext *ctx, const char *path)\n{\n    char full_fname[PATH_MAX];\n    int result;\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_path(ctx, \"unlink\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_unlink_ex(&ctx->api_ctx, &fname,\n                    fcfs_posix_api_gettid(fcfs_papi_tpid_type_tid))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_unlinkat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, int flags)\n{\n    char full_fname[PATH_MAX];\n    int result;\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_pathat(ctx, \"unlinkat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_remove_dentry_ex(&ctx->api_ctx, &fname,\n                    ((flags & AT_REMOVEDIR) ? FDIR_UNLINK_FLAGS_MATCH_DIR :\n                     FDIR_UNLINK_FLAGS_MATCH_FILE), fcfs_posix_api_gettid(\n                         fcfs_papi_tpid_type_tid))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_rename_ex(FCFSPosixAPIContext *ctx,\n        const char *path1, const char *path2)\n{\n    const int flags = 0;\n    int result;\n    char full_fname1[PATH_MAX];\n    char full_fname2[PATH_MAX];\n\n    if ((result=papi_resolve_path(ctx, \"rename\", &path1,\n                    full_fname1, sizeof(full_fname1))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=papi_resolve_path(ctx, \"rename\", &path2,\n                    full_fname2, sizeof(full_fname2))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_rename_dentry_ex(&ctx->api_ctx, path1,\n                    path2, &ctx->api_ctx.owner.oper, flags,\n                    fcfs_posix_api_gettid(fcfs_papi_tpid_type_tid))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nstatic inline int do_renameat(FCFSPosixAPIContext *ctx,\n        const char *func_name, int fd1, const char *path1,\n        int fd2, const char *path2, const int flags)\n{\n    int result;\n    char full_fname1[PATH_MAX];\n    char full_fname2[PATH_MAX];\n\n    if ((result=papi_resolve_pathat(ctx, func_name, fd1, &path1,\n                    full_fname1, sizeof(full_fname1))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=papi_resolve_pathat(ctx, func_name, fd2, &path2,\n                    full_fname2, sizeof(full_fname2))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_rename_dentry_ex(&ctx->api_ctx, path1,\n                    path2, &ctx->api_ctx.owner.oper, flags,\n                    fcfs_posix_api_gettid(fcfs_papi_tpid_type_tid))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_renameat_ex(FCFSPosixAPIContext *ctx, int fd1,\n        const char *path1, int fd2, const char *path2)\n{\n    const int flags = 0;\n    return do_renameat(ctx, \"renameat\", fd1, path1, fd2, path2, flags);\n}\n\nint fcfs_renameat2_ex(FCFSPosixAPIContext *ctx, int fd1,\n        const char *path1, int fd2, const char *path2,\n        unsigned int flags)\n{\n\n    /* flags convert from FreeBSD to Linux */\n#if defined(RENAME_SWAP) && defined(RENAME_EXCL)\n    if ((flags & RENAME_SWAP)) {\n        flags = ((flags & (~RENAME_SWAP)) | RENAME_EXCHANGE);\n    } else if ((flags & RENAME_EXCL)) {\n        flags = ((flags & (~RENAME_EXCL)) | RENAME_NOREPLACE);\n    }\n#endif\n\n    return do_renameat(ctx, \"renameat2\", fd1, path1, fd2, path2, flags);\n}\n\nint fcfs_mkdir_ex(FCFSPosixAPIContext *ctx,\n        const char *path, mode_t mode)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"mkdir\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_mkdir_ex(&ctx->api_ctx, path,\n                    &ctx->api_ctx.owner.oper, mode)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_mkdirat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, mode_t mode)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_pathat(ctx, \"mkdirat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_mkdir_ex(&ctx->api_ctx, path,\n                    &ctx->api_ctx.owner.oper, mode)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_rmdir_ex(FCFSPosixAPIContext *ctx, const char *path)\n{\n    const int flags = FDIR_UNLINK_FLAGS_MATCH_DIR;\n    int result;\n    char full_fname[PATH_MAX];\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_path(ctx, \"rmdir\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_remove_dentry_ex(&ctx->api_ctx, &fname, flags,\n                    fcfs_posix_api_gettid(fcfs_papi_tpid_type_tid))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_chown_ex(FCFSPosixAPIContext *ctx, const char *path,\n        uid_t owner, gid_t group)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    int result;\n    char full_fname[PATH_MAX];\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_path(ctx, \"chown\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_chown_ex(&ctx->api_ctx, &fname,\n                    owner, group, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_lchown_ex(FCFSPosixAPIContext *ctx, const char *path,\n        uid_t owner, gid_t group)\n{\n    const int flags = 0;\n    int result;\n    char full_fname[PATH_MAX];\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_path(ctx, \"lchown\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_chown_ex(&ctx->api_ctx, &fname,\n                    owner, group, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_fchown(int fd, uid_t owner, gid_t group)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    int result;\n    FCFSPosixAPIFileInfo *file;\n    FDIRClientOperInodePair oino;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx->\n            owner.oper, file->fi.dentry.inode);\n    if ((result=fcfs_api_chown_by_inode_ex(file->fi.ctx,\n                    &oino, owner, group, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_fchownat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, uid_t owner, gid_t group, int flags)\n{\n    int result;\n    char full_fname[PATH_MAX];\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_pathat(ctx, \"fchownat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_chown_ex(&ctx->api_ctx, &fname, owner,\n                    group, (flags & AT_SYMLINK_NOFOLLOW) ?\n                    0 : FDIR_FLAGS_FOLLOW_SYMLINK)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_chmod_ex(FCFSPosixAPIContext *ctx,\n        const char *path, mode_t mode)\n{\n    const int flags = 0;\n    int result;\n    char full_fname[PATH_MAX];\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_path(ctx, \"chmod\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_chmod_ex(&ctx->api_ctx,\n                    &fname, mode, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_fchmod(int fd, mode_t mode)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    int result;\n    FCFSPosixAPIFileInfo *file;\n    FDIRClientOperInodePair oino;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx->\n            owner.oper, file->fi.dentry.inode);\n    if ((result=fcfs_api_chmod_by_inode_ex(file->fi.ctx,\n                    &oino, mode, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_fchmodat_ex(FCFSPosixAPIContext *ctx, int fd,\n        const char *path, mode_t mode, int flags)\n{\n    int result;\n    char full_fname[PATH_MAX];\n    FDIRClientOperFnamePair fname;\n\n    if ((result=papi_resolve_pathat(ctx, \"fchmodat\", fd, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_chmod_ex(&ctx->api_ctx, &fname,\n                    mode, (flags & AT_SYMLINK_NOFOLLOW) ?\n                    0 : FDIR_FLAGS_FOLLOW_SYMLINK)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_statvfs_ex(FCFSPosixAPIContext *ctx,\n        const char *path, struct statvfs *buf)\n{\n    char full_fname[PATH_MAX];\n    int result;\n\n    if ((result=papi_resolve_path(ctx, \"chmod\", &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_statvfs_ex(&ctx->api_ctx, path, buf)) != 0) {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_fstatvfs(int fd, struct statvfs *buf)\n{\n    FCFSPosixAPIFileInfo *file;\n    int result;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_statvfs_ex(file->fi.ctx,\n                    file->filename.str, buf)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nstatic inline int do_setxattr(FCFSPosixAPIContext *ctx, const char *func,\n        const char *path, const char *name, const void *value,\n        size_t size, int flags)\n{\n    FDIRClientOperFnamePair fname;\n    char full_fname[PATH_MAX];\n    key_value_pair_t xattr;\n    int result;\n\n    if ((result=papi_resolve_path(ctx, func, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    FC_SET_STRING(xattr.key, (char *)name);\n    FC_SET_STRING_EX(xattr.value, (char *)value, size);\n    if ((result=fcfs_api_set_xattr_by_path_ex(&ctx->api_ctx,\n                    &fname, &xattr, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_setxattr_ex(FCFSPosixAPIContext *ctx, const char *path,\n        const char *name, const void *value, size_t size, int flags)\n{\n    return do_setxattr(ctx, \"setxattr\", path, name, value, size,\n            (flags | FDIR_FLAGS_FOLLOW_SYMLINK));\n}\n\nint fcfs_lsetxattr_ex(FCFSPosixAPIContext *ctx, const char *path,\n        const char *name, const void *value, size_t size, int flags)\n{\n    return do_setxattr(ctx, \"lsetxattr\", path, name, value, size,\n            (flags & (~FDIR_FLAGS_FOLLOW_SYMLINK)));\n}\n\nint fcfs_fsetxattr(int fd, const char *name, const void *value,\n        size_t size, int flags)\n{\n    FDIRClientOperInodePair oino;\n    key_value_pair_t xattr;\n    FCFSPosixAPIFileInfo *file;\n    int result;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx->\n            owner.oper, file->fi.dentry.inode);\n    FC_SET_STRING(xattr.key, (char *)name);\n    FC_SET_STRING_EX(xattr.value, (char *)value, size);\n    if ((result=fcfs_api_set_xattr_by_inode_ex(file->fi.ctx, &oino,\n                    &xattr, (flags | FDIR_FLAGS_FOLLOW_SYMLINK))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\n#define GET_XATTR_FLAGS_BY_VSIZE(size, flags) \\\n    ((size == 0) ? (flags | FDIR_FLAGS_XATTR_GET_SIZE) : flags)\n\nstatic inline ssize_t do_getxattr(FCFSPosixAPIContext *ctx,\n        const char *func, const char *path, const char *name,\n        void *value, size_t size, int flags)\n{\n    FDIRClientOperFnamePair fname;\n    char full_fname[PATH_MAX];\n    string_t nm;\n    string_t vl;\n    int result;\n\n    if ((result=papi_resolve_path(ctx, func, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    FC_SET_STRING(nm, (char *)name);\n    FC_SET_STRING_EX(vl, (char *)value, 0);\n    if ((result=fcfs_api_get_xattr_by_path_ex(&ctx->api_ctx,\n                    &fname, &nm, LOG_DEBUG, &vl, size,\n                    GET_XATTR_FLAGS_BY_VSIZE(size, flags))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return vl.len;\n    }\n}\n\nssize_t fcfs_getxattr_ex(FCFSPosixAPIContext *ctx, const char *path,\n        const char *name, void *value, size_t size)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    return do_getxattr(ctx, \"getxattr\", path, name, value, size, flags);\n}\n\nssize_t fcfs_lgetxattr_ex(FCFSPosixAPIContext *ctx, const char *path,\n        const char *name, void *value, size_t size)\n{\n    const int flags = 0;\n    return do_getxattr(ctx, \"lgetxattr\", path, name, value, size, flags);\n}\n\nssize_t fcfs_fgetxattr(int fd, const char *name,\n        void *value, size_t size)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    int result;\n    FCFSPosixAPIFileInfo *file;\n    string_t nm;\n    string_t vl;\n    FDIRClientOperInodePair oino;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx->\n            owner.oper, file->fi.dentry.inode);\n    FC_SET_STRING(nm, (char *)name);\n    FC_SET_STRING_EX(vl, (char *)value, 0);\n    if ((result=fcfs_api_get_xattr_by_inode_ex(file->fi.ctx,\n                    &oino, &nm, LOG_DEBUG, &vl, size,\n                    GET_XATTR_FLAGS_BY_VSIZE(size, flags))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return vl.len;\n    }\n}\n\nstatic inline ssize_t do_listxattr(FCFSPosixAPIContext *ctx,\n        const char *func, const char *path, char *list,\n        size_t size, int flags)\n{\n    FDIRClientOperFnamePair fname;\n    char full_fname[PATH_MAX];\n    string_t ls;\n    int result;\n\n    if ((result=papi_resolve_path(ctx, func, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    ls.str = list;\n    if ((result=fcfs_api_list_xattr_by_path_ex(&ctx->api_ctx, &fname, &ls,\n                    size, GET_XATTR_FLAGS_BY_VSIZE(size, flags))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return ls.len;\n    }\n}\n\nssize_t fcfs_listxattr_ex(FCFSPosixAPIContext *ctx,\n        const char *path, char *list, size_t size)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    return do_listxattr(ctx, \"listattr\", path, list, size, flags);\n}\n\nssize_t fcfs_llistxattr_ex(FCFSPosixAPIContext *ctx,\n        const char *path, char *list, size_t size)\n{\n    const int flags = 0;\n    return do_listxattr(ctx, \"llistattr\", path, list, size, flags);\n}\n\nssize_t fcfs_flistxattr(int fd, char *list, size_t size)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    int result;\n    FCFSPosixAPIFileInfo *file;\n    string_t ls;\n    FDIRClientOperInodePair oino;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx->\n            owner.oper, file->fi.dentry.inode);\n    ls.str = list;\n    if ((result=fcfs_api_list_xattr_by_inode_ex(file->fi.ctx, &oino, &ls,\n                    size, GET_XATTR_FLAGS_BY_VSIZE(size, flags))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return ls.len;\n    }\n}\n\nstatic inline int do_removexattr(FCFSPosixAPIContext *ctx, const char *func,\n        const char *path, const char *name, int flags)\n{\n    FDIRClientOperFnamePair fname;\n    char full_fname[PATH_MAX];\n    string_t nm;\n    int result;\n\n    if ((result=papi_resolve_path(ctx, func, &path,\n                    full_fname, sizeof(full_fname))) != 0)\n    {\n        return result;\n    }\n\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    FC_SET_STRING(nm, (char *)name);\n    if ((result=fcfs_api_remove_xattr_by_path_ex(&ctx->api_ctx,\n                    &fname, &nm, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_removexattr_ex(FCFSPosixAPIContext *ctx,\n        const char *path, const char *name)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    return do_removexattr(ctx, \"removexattr\", path, name, flags);\n}\n\nint fcfs_lremovexattr_ex(FCFSPosixAPIContext *ctx,\n        const char *path, const char *name)\n{\n    const int flags = 0;\n    return do_removexattr(ctx, \"lremovexattr\", path, name, flags);\n}\n\nint fcfs_fremovexattr(int fd, const char *name)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    int result;\n    FCFSPosixAPIFileInfo *file;\n    string_t nm;\n    FDIRClientOperInodePair oino;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx->\n            owner.oper, file->fi.dentry.inode);\n    FC_SET_STRING(nm, (char *)name);\n    if ((result=fcfs_api_remove_xattr_by_inode_ex(file->fi.ctx,\n                    &oino, &nm, flags)) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nstatic FCFSPosixAPIDIR *do_opendir(FCFSPosixAPIContext *ctx,\n        FCFSPosixAPIFileInfo *file)\n{\n    FDIRClientOperInodePair oino;\n    FCFSPosixAPIDIR *dir;\n    int result;\n\n    dir = (FCFSPosixAPIDIR *)fc_malloc(sizeof(FCFSPosixAPIDIR));\n    if (dir == NULL) {\n        errno = ENOMEM;\n        return NULL;\n    }\n\n    FCFSAPI_SET_OPER_INODE_PAIR(oino, file->fi.ctx->\n            owner.oper, file->fi.dentry.inode);\n    dir->file = file;\n    dir->magic = FCFS_PAPI_MAGIC_NUMBER;\n    dir->offset = 0;\n    fdir_client_compact_dentry_array_init(&dir->darray);\n    if ((result=fcfs_api_list_compact_dentry_by_inode_ex(\n                    &ctx->api_ctx, &oino, &dir->darray)) != 0)\n    {\n        free(dir);\n        errno = result;\n        return NULL;\n    }\n\n    return dir;\n}\n\nDIR *fcfs_opendir_ex(FCFSPosixAPIContext *ctx, const char *path)\n{\n    const int flags = O_RDONLY;\n    const mode_t mode = ACCESSPERMS | S_IFDIR;\n    FCFSPosixAPIFileInfo *file;\n    FCFSPosixAPIDIR *dir;\n    char full_fname[PATH_MAX];\n\n    if (papi_resolve_path(ctx, \"opendir\", &path,\n                full_fname, sizeof(full_fname)) != 0)\n    {\n        return NULL;\n    }\n\n    if ((file=do_open_ex(ctx, path, flags, mode,\n                    fcfs_papi_tpid_type_tid)) == NULL)\n    {\n        return NULL;\n    }\n\n    if ((dir=do_opendir(ctx, file)) == NULL) {\n        fcfs_api_close(&file->fi);\n        fcfs_fd_manager_free(file);\n    }\n    return (DIR *)dir;\n}\n\nDIR *fcfs_fdopendir_ex(FCFSPosixAPIContext *ctx, int fd)\n{\n    FCFSPosixAPIFileInfo *file;\n    FCFSPosixAPIDIR *dir;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return NULL;\n    }\n\n    if ((dir=do_opendir(ctx, file)) == NULL) {\n        fcfs_api_close(&file->fi);\n        fcfs_fd_manager_free(file);\n    }\n    return (DIR *)dir;\n}\n\n#define FCFS_CONVERT_DIRP_EX(dirp, ...) \\\n    FCFSPosixAPIDIR *dir; \\\n    dir = (FCFSPosixAPIDIR *)dirp; \\\n    if (dir->magic != FCFS_PAPI_MAGIC_NUMBER) { \\\n        errno = EBADF; \\\n        return __VA_ARGS__; \\\n    }\n\n#define FCFS_CONVERT_DIRP(dirp) \\\n    FCFS_CONVERT_DIRP_EX(dirp, -1)\n\n#define FCFS_CONVERT_DIRP_VOID(dirp) \\\n    FCFS_CONVERT_DIRP_EX(dirp)\n\nint fcfs_closedir_ex(FCFSPosixAPIContext *ctx, DIR *dirp)\n{\n    FCFS_CONVERT_DIRP(dirp);\n\n    fdir_client_compact_dentry_array_free(&dir->darray);\n    fcfs_api_close(&dir->file->fi);\n    fcfs_fd_manager_free(dir->file);\n    dir->magic = 0;\n    free(dir);\n    return 0;\n}\n\nstatic inline struct dirent *do_readdir(FCFSPosixAPIDIR *dir)\n{\n    if (dir->offset < 0 || dir->offset >= dir->darray.count) {\n        return NULL;\n    }\n\n    return dir->darray.entries + dir->offset++;\n}\n\nstruct dirent *fcfs_readdir_ex(FCFSPosixAPIContext *ctx, DIR *dirp)\n{\n    FCFS_CONVERT_DIRP_EX(dirp, NULL);\n    return do_readdir(dir);\n}\n\nint fcfs_readdir_r_ex(FCFSPosixAPIContext *ctx, DIR *dirp,\n        struct dirent *entry, struct dirent **result)\n{\n    struct dirent *current;\n    FCFS_CONVERT_DIRP(dirp);\n\n    if ((current=do_readdir(dir)) != NULL) {\n        memcpy(entry, current, sizeof(FDIRDirent));\n        *result = entry;\n    } else {\n        *result = NULL;\n    }\n\n    return 0;\n}\n\nvoid fcfs_seekdir_ex(FCFSPosixAPIContext *ctx, DIR *dirp, long loc)\n{\n    FCFS_CONVERT_DIRP_VOID(dirp);\n    dir->offset = loc;\n}\n\nlong fcfs_telldir_ex(FCFSPosixAPIContext *ctx, DIR *dirp)\n{\n    FCFS_CONVERT_DIRP(dirp);\n    return dir->offset;\n}\n\nvoid fcfs_rewinddir_ex(FCFSPosixAPIContext *ctx, DIR *dirp)\n{\n    FCFS_CONVERT_DIRP_VOID(dirp);\n    dir->offset = 0;\n}\n\nint fcfs_dirfd_ex(FCFSPosixAPIContext *ctx, DIR *dirp)\n{\n    FCFS_CONVERT_DIRP(dirp);\n\n    if (fcfs_fd_manager_get(dir->file->fd) != dir->file) {\n        errno = EBADF;\n        return -1;\n    }\n\n    return dir->file->fd;\n}\n\nstatic int do_scandir(FCFSPosixAPIContext *ctx, const char *path,\n        struct dirent ***namelist, int (*filter)(const struct dirent *),\n        int (*compar)(const struct dirent **, const struct dirent **))\n{\n    FDIRClientOperFnamePair fname;\n    FCFSPosixAPIDIR dir;\n    FDIRDirent *ent;\n    FDIRDirent *end;\n    FDIRDirent **cur;\n    int result;\n    int count;\n\n    fdir_client_compact_dentry_array_init(&dir.darray);\n    FCFSAPI_SET_PATH_OPER_FNAME(fname, &ctx->api_ctx,\n            ctx->api_ctx.owner.oper, path);\n    if ((result=fcfs_api_list_compact_dentry_by_path_ex(&ctx->\n                    api_ctx, &fname, &dir.darray)) != 0)\n    {\n        errno = result;\n        return -1;\n    }\n\n    do {\n        if ((*namelist=fc_malloc(sizeof(FDIRDirent *) *\n                        dir.darray.count)) == NULL)\n        {\n            errno = ENOMEM;\n            count = -1;\n            break;\n        }\n\n        count = 0;\n        if (dir.darray.count == 0) {\n            break;\n        }\n\n        cur = (FDIRDirent **)*namelist;\n        end = dir.darray.entries + dir.darray.count;\n        for (ent=dir.darray.entries; ent<end; ent++) {\n            if (filter != NULL && ((int (*)(const FDIRDirent *))\n                    filter)(ent) == 0)\n            {\n                continue;\n            }\n\n            if ((*cur=fc_malloc(sizeof(FDIRDirent))) == NULL) {\n                errno = ENOMEM;\n                count = -1;\n                break;\n            }\n            memcpy(*cur, ent, sizeof(FDIRDirent));\n            cur++;\n        }\n\n        if (count < 0) {  //error\n            FDIRDirent **endp;\n            endp = cur;\n            for (cur=(FDIRDirent **)*namelist; cur<endp; cur++) {\n                free(*cur);\n            }\n            free(*namelist);\n            *namelist = NULL;\n        } else {\n            count = cur - (FDIRDirent **)(*namelist);\n            if (compar != NULL && count > 1) {\n                qsort(*namelist, count, sizeof(struct dirent *),\n                        (int (*)(const void *, const void *))compar);\n            }\n        }\n    } while (0);\n\n    fdir_client_compact_dentry_array_free(&dir.darray);\n    return count;\n}\n\nint fcfs_scandir_ex(FCFSPosixAPIContext *ctx, const char *path,\n        struct dirent ***namelist, int (*filter)(const struct dirent *),\n        int (*compar)(const struct dirent **, const struct dirent **))\n{\n    char full_fname[PATH_MAX];\n\n    if (papi_resolve_path(ctx, \"scandir\", &path,\n                full_fname, sizeof(full_fname)) != 0)\n    {\n        return -1;\n    }\n\n    return do_scandir(ctx, path, namelist, filter, compar);\n}\n\nint fcfs_scandirat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path,\n        struct dirent ***namelist, int (*filter)(const struct dirent *),\n        int (*compar)(const struct dirent **, const struct dirent **))\n{\n    char full_fname[PATH_MAX];\n\n    if (papi_resolve_pathat(ctx, \"scandirat\", fd, &path,\n                full_fname, sizeof(full_fname)) != 0)\n    {\n        return -1;\n    }\n\n    return do_scandir(ctx, path, namelist, filter, compar);\n}\n\nstatic int do_chdir(const string_t *path)\n{\n    string_t *cwd;\n    char *old_cwd;\n\n    if ((cwd=fc_malloc(sizeof(string_t) + path->len + 1)) == NULL) {\n        errno = ENOMEM;\n        return -1;\n    }\n\n    cwd->str = (char *)(cwd + 1);\n    memcpy(cwd->str, path->str, path->len + 1);\n    cwd->len = path->len;\n\n    old_cwd = (G_FCFS_PAPI_CWD != NULL ? G_FCFS_PAPI_CWD->str : NULL);\n    G_FCFS_PAPI_CWD = cwd;\n    if (old_cwd != NULL) {\n        free(old_cwd);\n    }\n    return 0;\n}\n\nint fcfs_chdir_ex(FCFSPosixAPIContext *ctx, const char *path)\n{\n    char full_fname[PATH_MAX];\n    string_t cwd;\n\n    if (papi_resolve_path(ctx, \"chdir\", &path,\n                full_fname, sizeof(full_fname)) != 0)\n    {\n        return -1;\n    }\n\n    FC_SET_STRING(cwd, (char *)path);\n    return do_chdir(&cwd);\n}\n\nint fcfs_fchdir(int fd)\n{\n    FCFSPosixAPIFileInfo *file;\n    char full_path[PATH_MAX];\n    string_t cwd;\n    char *p;\n    int len;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    p = strrchr(file->filename.str, '/');\n    if (p == NULL) {\n        errno = EBUSY;\n        return -1;\n    }\n\n    len = (p - file->filename.str) + 1;\n    if (len >= sizeof(full_path)) {\n        snprintf(full_path, sizeof(full_path), \"%.*s\",\n                len, file->filename.str);\n    } else {\n        memcpy(full_path, file->filename.str, len);\n        *(full_path + len) = '\\0';\n    }\n    FC_SET_STRING_EX(cwd, full_path, len);\n    return do_chdir(&cwd);\n}\n\nchar *fcfs_getcwd_ex(FCFSPosixAPIContext *ctx, char *buf, size_t size)\n{\n    string_t cwd;\n\n    cwd.str = buf;\n    return do_getcwd(ctx, &cwd, size, ERANGE);\n}\n\nchar *fcfs_getwd_ex(FCFSPosixAPIContext *ctx, char *buf)\n{\n    string_t cwd;\n\n    cwd.str = buf;\n    return do_getcwd(ctx, &cwd, PATH_MAX, ENAMETOOLONG);\n}\n\n#define FCFS_API_NOT_IMPLEMENTED(api_name, retval)  \\\n    logError(\"file: \"__FILE__\", line: %d, \" \\\n            \"function \\\"%s\\\" not implemented!\", \\\n            __LINE__, api_name); \\\n    errno = EOPNOTSUPP;  \\\n    return retval\n\nint fcfs_chroot_ex(FCFSPosixAPIContext *ctx, const char *path)\n{\n    FCFS_API_NOT_IMPLEMENTED(\"chroot\", -1);\n}\n\nint fcfs_dup_ex(FCFSPosixAPIContext *ctx, int fd)\n{\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"fd: %d\", __LINE__, __FUNCTION__, fd);\n    FCFS_API_NOT_IMPLEMENTED(\"dup\", -1);\n}\n\nint fcfs_dup2_ex(FCFSPosixAPIContext *ctx, int fd1, int fd2)\n{\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"fd1: %d, fd2: %d\", __LINE__, __FUNCTION__, fd1, fd2);\n    FCFS_API_NOT_IMPLEMENTED(\"dup2\", -1);\n}\n\nvoid *fcfs_mmap_ex(FCFSPosixAPIContext *ctx, void *addr, size_t length,\n        int prot, int flags, int fd, off_t offset)\n{\n    FCFS_API_NOT_IMPLEMENTED(\"mmap\", NULL);\n}\n\nint fcfs_munmap_ex(FCFSPosixAPIContext *ctx, void *addr, size_t length)\n{\n    FCFS_API_NOT_IMPLEMENTED(\"munmap\", -1);\n}\n\nint fcfs_lockf_ex(FCFSPosixAPIContext *ctx, int fd, int cmd, off_t len)\n{\n    FCFSPosixAPIFileInfo *file;\n    struct flock lock;\n    int fcntl_cmd;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    switch (cmd) {\n        case F_LOCK:\n            fcntl_cmd = F_SETLKW;\n            lock.l_type = F_WRLCK;\n            break;\n        case F_TLOCK:\n            fcntl_cmd = F_SETLK;\n            lock.l_type = F_WRLCK;\n            break;\n        case F_ULOCK:\n            fcntl_cmd = F_SETLK;\n            lock.l_type = F_UNLCK;\n            break;\n        case F_TEST:\n            fcntl_cmd = F_GETLK;\n            lock.l_type = F_UNLCK;\n            break;\n        default:\n            errno = EOPNOTSUPP;\n            return -1;\n    }\n\n    lock.l_whence = SEEK_SET;\n    lock.l_start = file->fi.offset;\n    lock.l_len = len;\n    lock.l_pid = fcfs_posix_api_getpid();\n    return do_fcntl(file, fcntl_cmd, &lock);\n}\n\nint fcfs_posix_fallocate_ex(FCFSPosixAPIContext *ctx,\n        int fd, off_t offset, off_t len)\n{\n    const int mode = 0;\n    int result;\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    if ((result=fcfs_api_fallocate_ex(&file->fi, mode, offset, len,\n                    fcfs_posix_api_gettid(file->tpid_type))) != 0)\n    {\n        errno = result;\n        return -1;\n    } else {\n        return 0;\n    }\n}\n\nint fcfs_posix_fadvise_ex(FCFSPosixAPIContext *ctx, int fd,\n            off_t offset, off_t len, int advice)\n{\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    return 0;\n}\n\nstatic int do_vdprintf(FCFSPosixAPIFileInfo *file,\n        const char *format, va_list ap)\n{\n#define FIXED_BUFFUER_SIZE (4 * 1024)\n    char fixed[FIXED_BUFFUER_SIZE];\n    char *buff;\n    int length;\n    int result;\n    va_list new_ap;\n\n    va_copy(new_ap, ap);\n    buff = fixed;\n    length = vsnprintf(buff, FIXED_BUFFUER_SIZE, format, ap);\n    if (length > FIXED_BUFFUER_SIZE - 1) {  //overflow\n        buff = (char *)fc_malloc(length + 1);\n        if (buff == NULL) {\n            errno = ENOMEM;\n            return -1;\n        }\n        length = vsprintf(buff, format, new_ap);\n    }\n    va_end(new_ap);\n\n    result = do_write(file, buff, length);\n    if (buff != fixed) {\n        free(buff);\n    }\n    if (result != 0) {\n        errno = result;\n        return -1;\n    } else {\n        return length;\n    }\n}\n\nint fcfs_dprintf(int fd, const char *format, ...)\n{\n    FCFSPosixAPIFileInfo *file;\n    va_list ap;\n    int bytes;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    va_start(ap, format);\n    bytes = do_vdprintf(file, format, ap);\n    va_end(ap);\n    return bytes;\n}\n\nint fcfs_vdprintf(int fd, const char *format, va_list ap)\n{\n    FCFSPosixAPIFileInfo *file;\n\n    if ((file=fcfs_fd_manager_get(fd)) == NULL) {\n        errno = EBADF;\n        return -1;\n    }\n\n    return do_vdprintf(file, format, ap);\n}\n"
  },
  {
    "path": "src/api/std/papi.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_PAPI_H\n#define _FCFS_PAPI_H\n\n#include <utime.h>\n#include <dirent.h>\n#include \"api_types.h\"\n#include \"fd_manager.h\"\n\n#define G_FCFS_PAPI_CTX  g_fcfs_papi_global_vars.ctx\n#define G_FCFS_PAPI_CWD  g_fcfs_papi_global_vars.cwd\n\n#define fcfs_open(path, flags, ...) \\\n    fcfs_open_ex(&G_FCFS_PAPI_CTX, path, flags, ##__VA_ARGS__)\n\n#define fcfs_openat(fd, path, flags, ...) \\\n    fcfs_openat_ex(&G_FCFS_PAPI_CTX, fd, path, flags, ##__VA_ARGS__)\n\n#define fcfs_creat(path, mode) \\\n    fcfs_creat_ex(&G_FCFS_PAPI_CTX, path, mode)\n\n#define fcfs_truncate(path, length) \\\n    fcfs_truncate_ex(&G_FCFS_PAPI_CTX, path, length)\n\n#define fcfs_lstat(path, buf) \\\n    fcfs_lstat_ex(&G_FCFS_PAPI_CTX, path, buf)\n\n#define fcfs_stat(path, buf) \\\n    fcfs_stat_ex(&G_FCFS_PAPI_CTX, path, buf)\n\n#define fcfs_fstatat(fd, path, buf, flags) \\\n    fcfs_fstatat_ex(&G_FCFS_PAPI_CTX, fd, path, buf, flags)\n\n#define fcfs_symlink(link, path) \\\n    fcfs_symlink_ex(&G_FCFS_PAPI_CTX, link, path)\n\n#define fcfs_symlinkat(link, fd, path) \\\n    fcfs_symlinkat_ex(&G_FCFS_PAPI_CTX, link, fd, path)\n\n#define fcfs_link(path1, path2) \\\n    fcfs_link_ex(&G_FCFS_PAPI_CTX, path1, path2)\n\n#define fcfs_linkat(fd1, path1, fd2, path2, flags) \\\n    fcfs_linkat_ex(&G_FCFS_PAPI_CTX, fd1, path1, fd2, path2, flags)\n\n#define fcfs_readlink(path, buff, size) \\\n    fcfs_readlink_ex(&G_FCFS_PAPI_CTX, path, buff, size)\n\n#define fcfs_readlinkat(fd, path, buff, size) \\\n    fcfs_readlinkat_ex(&G_FCFS_PAPI_CTX, fd, path, buff, size)\n\n#define fcfs_mknod(path, mode, dev) \\\n    fcfs_mknod_ex(&G_FCFS_PAPI_CTX, path, mode, dev)\n\n#define fcfs_mknodat(fd, path, mode, dev) \\\n    fcfs_mknodat_ex(&G_FCFS_PAPI_CTX, fd, path, mode, dev)\n\n#define fcfs_mkfifo(path, mode) \\\n    fcfs_mkfifo_ex(&G_FCFS_PAPI_CTX, path, mode)\n\n#define fcfs_mkfifoat(fd, path, mode) \\\n    fcfs_mkfifoat_ex(&G_FCFS_PAPI_CTX, fd, path, mode)\n\n#define fcfs_access(path, mode) \\\n    fcfs_access_ex(&G_FCFS_PAPI_CTX, path, mode)\n\n#define fcfs_faccessat(fd, path, mode, flags) \\\n    fcfs_faccessat_ex(&G_FCFS_PAPI_CTX, fd, path, mode, flags)\n\n#define fcfs_euidaccess(path, mode) \\\n    fcfs_euidaccess_ex(&G_FCFS_PAPI_CTX, path, mode)\n\n#define fcfs_eaccess(path, mode) \\\n    fcfs_eaccess_ex(&G_FCFS_PAPI_CTX, path, mode)\n\n#define fcfs_utime(path, times) \\\n    fcfs_utime_ex(&G_FCFS_PAPI_CTX, path, times)\n\n#define fcfs_utimes(path, times) \\\n    fcfs_utimes_ex(&G_FCFS_PAPI_CTX, path, times)\n\n#define fcfs_futimes(fd, times) \\\n    fcfs_futimes_ex(&G_FCFS_PAPI_CTX, fd, times)\n\n#define fcfs_futimesat(fd, path, times) \\\n    fcfs_futimesat_ex(&G_FCFS_PAPI_CTX, fd, path, times)\n\n#define fcfs_futimens(fd, times) \\\n    fcfs_futimens_ex(&G_FCFS_PAPI_CTX, fd, times)\n\n#define fcfs_utimensat(fd, path, times, flags) \\\n    fcfs_utimensat_ex(&G_FCFS_PAPI_CTX, fd, path, times, flags)\n\n#define fcfs_unlink(path) \\\n    fcfs_unlink_ex(&G_FCFS_PAPI_CTX, path)\n\n#define fcfs_unlinkat(fd, path, flags) \\\n    fcfs_unlinkat_ex(&G_FCFS_PAPI_CTX, fd, path, flags)\n\n#define fcfs_rename(path1, path2) \\\n    fcfs_rename_ex(&G_FCFS_PAPI_CTX, path1, path2)\n\n#define fcfs_renameat(fd1, path1, fd2, path2) \\\n    fcfs_renameat_ex(&G_FCFS_PAPI_CTX, fd1, path1, fd2, path2)\n\n#define fcfs_renameat2(fd1, path1, fd2, path2, flags) \\\n    fcfs_renameat2_ex(&G_FCFS_PAPI_CTX, fd1, path1, fd2, path2, flags)\n\n#define fcfs_mkdir(path, mode) \\\n    fcfs_mkdir_ex(&G_FCFS_PAPI_CTX, path, mode)\n\n#define fcfs_mkdirat(fd, path, mode) \\\n    fcfs_mkdirat_ex(&G_FCFS_PAPI_CTX, fd, path, mode)\n\n#define fcfs_rmdir(path) \\\n    fcfs_rmdir_ex(&G_FCFS_PAPI_CTX, path)\n\n#define fcfs_chown(path, owner, group) \\\n    fcfs_chown_ex(&G_FCFS_PAPI_CTX, path, owner, group)\n\n#define fcfs_lchown(path, owner, group) \\\n    fcfs_lchown_ex(&G_FCFS_PAPI_CTX, path, owner, group)\n\n#define fcfs_fchownat(fd, path, owner, group, flags) \\\n    fcfs_fchownat_ex(&G_FCFS_PAPI_CTX, fd, path, owner, group, flags)\n\n#define fcfs_chmod(path, mode) \\\n    fcfs_chmod_ex(&G_FCFS_PAPI_CTX, path, mode)\n\n#define fcfs_fchmodat(fd, path, mode, flags) \\\n    fcfs_fchmodat_ex(&G_FCFS_PAPI_CTX, fd, path, mode, flags)\n\n#define fcfs_statvfs(path, buf) \\\n    fcfs_statvfs_ex(&G_FCFS_PAPI_CTX, path, buf)\n\n#define fcfs_setxattr(path, name, value, size, flags) \\\n    fcfs_setxattr_ex(&G_FCFS_PAPI_CTX, path, name, value, size, flags)\n\n#define fcfs_lsetxattr(path, name, value, size, flags) \\\n    fcfs_lsetxattr_ex(&G_FCFS_PAPI_CTX, path, name, value, size, flags)\n\n#define fcfs_getxattr(path, name, value, size) \\\n    fcfs_getxattr_ex(&G_FCFS_PAPI_CTX, path, name, value, size)\n\n#define fcfs_lgetxattr(path, name, value, size) \\\n    fcfs_lgetxattr_ex(&G_FCFS_PAPI_CTX, path, name, value, size)\n\n#define fcfs_listxattr(path, list, size) \\\n    fcfs_listxattr_ex(&G_FCFS_PAPI_CTX, path, list, size)\n\n#define fcfs_llistxattr(path, list, size) \\\n    fcfs_llistxattr_ex(&G_FCFS_PAPI_CTX, path, list, size)\n\n#define fcfs_removexattr(path, name) \\\n    fcfs_removexattr_ex(&G_FCFS_PAPI_CTX, path, name)\n\n#define fcfs_lremovexattr(path, name) \\\n    fcfs_lremovexattr_ex(&G_FCFS_PAPI_CTX, path, name)\n\n#define fcfs_opendir(path) \\\n    fcfs_opendir_ex(&G_FCFS_PAPI_CTX, path)\n\n#define fcfs_fdopendir(fd) \\\n    fcfs_fdopendir_ex(&G_FCFS_PAPI_CTX, fd)\n\n#define fcfs_closedir(dirp) \\\n    fcfs_closedir_ex(&G_FCFS_PAPI_CTX, dirp)\n\n#define fcfs_readdir(dirp) \\\n    fcfs_readdir_ex(&G_FCFS_PAPI_CTX, dirp)\n\n#define fcfs_readdir_r(dirp, entry, result) \\\n    fcfs_readdir_r_ex(&G_FCFS_PAPI_CTX, dirp, entry, result)\n\n#define fcfs_seekdir(dirp, loc) \\\n    fcfs_seekdir_ex(&G_FCFS_PAPI_CTX, dirp, loc)\n\n#define fcfs_telldir(dirp) \\\n    fcfs_telldir_ex(&G_FCFS_PAPI_CTX, dirp)\n\n#define fcfs_rewinddir(dirp) \\\n    fcfs_rewinddir_ex(&G_FCFS_PAPI_CTX, dirp)\n\n#define fcfs_dirfd(dirp) \\\n    fcfs_dirfd_ex(&G_FCFS_PAPI_CTX, dirp)\n\n#define fcfs_scandir(path, namelist, filter, compar) \\\n    fcfs_scandir_ex(&G_FCFS_PAPI_CTX, path, namelist, filter, compar)\n\n#define fcfs_scandirat(fd, path, namelist, filter, compar) \\\n    fcfs_scandirat_ex(&G_FCFS_PAPI_CTX, fd, path, namelist, filter, compar)\n\n#define fcfs_lockf(fd, cmd, len) \\\n    fcfs_lockf_ex(&G_FCFS_PAPI_CTX, fd, cmd, len)\n\n#define fcfs_posix_fallocate(fd, offset, len) \\\n    fcfs_posix_fallocate_ex(&G_FCFS_PAPI_CTX, fd, offset, len)\n\n#define fcfs_posix_fadvise(fd, offset, len, advice) \\\n    fcfs_posix_fadvise_ex(&G_FCFS_PAPI_CTX, fd, offset, len, advice)\n\n#define fcfs_chdir(path) \\\n    fcfs_chdir_ex(&G_FCFS_PAPI_CTX, path)\n\n#define fcfs_getcwd(buf, size) \\\n    fcfs_getcwd_ex(&G_FCFS_PAPI_CTX, buf, size)\n\n#define fcfs_getwd(buf) \\\n    fcfs_getwd_ex(&G_FCFS_PAPI_CTX, buf)\n\n#define fcfs_chroot(path) \\\n    fcfs_chroot_ex(&G_FCFS_PAPI_CTX, path)\n\n#define fcfs_dup(fd) \\\n    fcfs_dup_ex(&G_FCFS_PAPI_CTX, fd)\n\n#define fcfs_dup2(fd1, fd2) \\\n    fcfs_dup2_ex(&G_FCFS_PAPI_CTX, fd1, fd2)\n\n#define fcfs_mmap(addr, length, prot, flags, fd, offset) \\\n    fcfs_mmap_ex(&G_FCFS_PAPI_CTX, addr, length, prot, flags, fd, offset)\n\n#define fcfs_munmap(addr, length) \\\n    fcfs_munmap_ex(&G_FCFS_PAPI_CTX, addr, length)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    int fcfs_open_ex(FCFSPosixAPIContext *ctx,\n            const char *path, int flags, ...);\n\n    int fcfs_openat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, int flags, ...);\n\n    int fcfs_creat_ex(FCFSPosixAPIContext *ctx,\n            const char *path, mode_t mode);\n\n    int fcfs_close(int fd);\n\n    int fcfs_fsync(int fd);\n\n    int fcfs_fdatasync(int fd);\n\n    ssize_t fcfs_write(int fd, const void *buff, size_t count);\n\n    ssize_t fcfs_pwrite(int fd, const void *buff,\n            size_t count, off_t offset);\n\n    ssize_t fcfs_writev(int fd, const struct iovec *iov, int iovcnt);\n\n    ssize_t fcfs_pwritev(int fd, const struct iovec *iov,\n            int iovcnt, off_t offset);\n\n    ssize_t fcfs_read(int fd, void *buff, size_t count);\n\n    ssize_t fcfs_pread(int fd, void *buff, size_t count, off_t offset);\n\n    ssize_t fcfs_readv(int fd, const struct iovec *iov, int iovcnt);\n\n    ssize_t fcfs_preadv(int fd, const struct iovec *iov,\n            int iovcnt, off_t offset);\n\n    ssize_t fcfs_readahead(int fd, off64_t offset, size_t count);\n\n    off_t fcfs_lseek(int fd, off_t offset, int whence);\n\n    off_t fcfs_ltell(int fd);\n\n    int fcfs_fallocate(int fd, int mode, off_t offset, off_t length);\n\n    int fcfs_truncate_ex(FCFSPosixAPIContext *ctx,\n            const char *path, off_t length);\n\n    int fcfs_ftruncate(int fd, off_t length);\n\n    int fcfs_lstat_ex(FCFSPosixAPIContext *ctx,\n            const char *path, struct stat *buf);\n\n    int fcfs_stat_ex(FCFSPosixAPIContext *ctx,\n            const char *path, struct stat *buf);\n\n    int fcfs_fstat(int fd, struct stat *buf);\n\n    int fcfs_fstatat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, struct stat *buf, int flags);\n\n    int fcfs_flock(int fd, int operation);\n\n    int fcfs_fcntl(int fd, int cmd, ...);\n\n    int fcfs_symlink_ex(FCFSPosixAPIContext *ctx,\n            const char *link, const char *path);\n\n    int fcfs_symlinkat_ex(FCFSPosixAPIContext *ctx,\n            const char *link, int fd, const char *path);\n\n    int fcfs_link_ex(FCFSPosixAPIContext *ctx,\n            const char *path1, const char *path2);\n\n    int fcfs_linkat_ex(FCFSPosixAPIContext *ctx, int fd1,\n            const char *path1, int fd2, const char *path2,\n            int flags);\n\n    ssize_t fcfs_readlink_ex(FCFSPosixAPIContext *ctx,\n            const char *path, char *buff, size_t size);\n\n    ssize_t fcfs_readlinkat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, char *buff, size_t size);\n\n    int fcfs_mknod_ex(FCFSPosixAPIContext *ctx,\n            const char *path, mode_t mode, dev_t dev);\n\n    int fcfs_mknodat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, mode_t mode, dev_t dev);\n\n    int fcfs_mkfifo_ex(FCFSPosixAPIContext *ctx,\n            const char *path, mode_t mode);\n\n    int fcfs_mkfifoat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, mode_t mode);\n\n    int fcfs_access_ex(FCFSPosixAPIContext *ctx,\n            const char *path, int mode);\n\n    int fcfs_faccessat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, int mode, int flags);\n\n    int fcfs_euidaccess_ex(FCFSPosixAPIContext *ctx,\n            const char *path, int mode);\n\n    static inline int fcfs_eaccess_ex(FCFSPosixAPIContext *ctx,\n            const char *path, int mode)\n    {\n        return fcfs_euidaccess_ex(ctx, path, mode);\n    }\n\n    int fcfs_utime_ex(FCFSPosixAPIContext *ctx, const char *path,\n            const struct utimbuf *times);\n\n    int fcfs_utimes_ex(FCFSPosixAPIContext *ctx, const char *path,\n            const struct timeval times[2]);\n\n    int fcfs_futimes_ex(FCFSPosixAPIContext *ctx,\n            int fd, const struct timeval times[2]);\n\n    int fcfs_futimesat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, const struct timeval times[2]);\n\n    int fcfs_futimens_ex(FCFSPosixAPIContext *ctx, int fd,\n            const struct timespec times[2]);\n\n    int fcfs_utimensat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, const struct timespec times[2], int flags);\n\n    int fcfs_unlink_ex(FCFSPosixAPIContext *ctx, const char *path);\n\n    int fcfs_unlinkat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, int flags);\n\n    int fcfs_rename_ex(FCFSPosixAPIContext *ctx,\n            const char *path1, const char *path2);\n\n    int fcfs_renameat_ex(FCFSPosixAPIContext *ctx, int fd1,\n            const char *path1, int fd2, const char *path2);\n\n    //renameatx_np for FreeBSD\n    int fcfs_renameat2_ex(FCFSPosixAPIContext *ctx, int fd1,\n            const char *path1, int fd2, const char *path2,\n            unsigned int flags);\n\n    int fcfs_mkdir_ex(FCFSPosixAPIContext *ctx,\n            const char *path, mode_t mode);\n\n    int fcfs_mkdirat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, mode_t mode);\n\n    int fcfs_rmdir_ex(FCFSPosixAPIContext *ctx, const char *path);\n\n    int fcfs_chown_ex(FCFSPosixAPIContext *ctx, const char *path,\n            uid_t owner, gid_t group);\n\n    int fcfs_lchown_ex(FCFSPosixAPIContext *ctx, const char *path,\n            uid_t owner, gid_t group);\n\n    int fcfs_fchown(int fd, uid_t owner, gid_t group);\n\n    int fcfs_fchownat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, uid_t owner, gid_t group, int flags);\n\n    int fcfs_chmod_ex(FCFSPosixAPIContext *ctx,\n            const char *path, mode_t mode);\n\n    int fcfs_fchmod(int fd, mode_t mode);\n\n    int fcfs_fchmodat_ex(FCFSPosixAPIContext *ctx, int fd,\n            const char *path, mode_t mode, int flags);\n\n    int fcfs_statvfs_ex(FCFSPosixAPIContext *ctx,\n            const char *path, struct statvfs *buf);\n\n    int fcfs_fstatvfs(int fd, struct statvfs *buf);\n\n    int fcfs_setxattr_ex(FCFSPosixAPIContext *ctx, const char *path,\n            const char *name, const void *value, size_t size, int flags);\n\n    int fcfs_lsetxattr_ex(FCFSPosixAPIContext *ctx, const char *path,\n            const char *name, const void *value, size_t size, int flags);\n\n    int fcfs_fsetxattr(int fd, const char *name, const void *value,\n            size_t size, int flags);\n\n    ssize_t fcfs_getxattr_ex(FCFSPosixAPIContext *ctx, const char *path,\n            const char *name, void *value, size_t size);\n\n    ssize_t fcfs_lgetxattr_ex(FCFSPosixAPIContext *ctx, const char *path,\n            const char *name, void *value, size_t size);\n\n    ssize_t fcfs_fgetxattr(int fd, const char *name,\n            void *value, size_t size);\n\n    ssize_t fcfs_listxattr_ex(FCFSPosixAPIContext *ctx,\n            const char *path, char *list, size_t size);\n\n    ssize_t fcfs_llistxattr_ex(FCFSPosixAPIContext *ctx,\n            const char *path, char *list, size_t size);\n\n    ssize_t fcfs_flistxattr(int fd, char *list, size_t size);\n\n    int fcfs_removexattr_ex(FCFSPosixAPIContext *ctx,\n            const char *path, const char *name);\n\n    int fcfs_lremovexattr_ex(FCFSPosixAPIContext *ctx,\n            const char *path, const char *name);\n\n    int fcfs_fremovexattr(int fd, const char *name);\n\n    DIR *fcfs_opendir_ex(FCFSPosixAPIContext *ctx, const char *path);\n\n    DIR *fcfs_fdopendir_ex(FCFSPosixAPIContext *ctx, int fd);\n\n    int fcfs_closedir_ex(FCFSPosixAPIContext *ctx, DIR *dirp);\n\n    struct dirent *fcfs_readdir_ex(FCFSPosixAPIContext *ctx, DIR *dirp);\n\n    int fcfs_readdir_r_ex(FCFSPosixAPIContext *ctx, DIR *dirp,\n            struct dirent *entry, struct dirent **result);\n\n    void fcfs_seekdir_ex(FCFSPosixAPIContext *ctx, DIR *dirp, long loc);\n\n    long fcfs_telldir_ex(FCFSPosixAPIContext *ctx, DIR *dirp);\n\n    void fcfs_rewinddir_ex(FCFSPosixAPIContext *ctx, DIR *dirp);\n\n    int fcfs_dirfd_ex(FCFSPosixAPIContext *ctx, DIR *dirp);\n\n    int fcfs_scandir_ex(FCFSPosixAPIContext *ctx, const char *path,\n            struct dirent ***namelist, int (*filter)(const struct dirent *),\n            int (*compar)(const struct dirent **, const struct dirent **));\n\n    int fcfs_scandirat_ex(FCFSPosixAPIContext *ctx, int fd, const char *path,\n            struct dirent ***namelist, int (*filter)(const struct dirent *),\n            int (*compar)(const struct dirent **, const struct dirent **));\n\n    int fcfs_lockf_ex(FCFSPosixAPIContext *ctx, int fd, int cmd, off_t len);\n\n    int fcfs_posix_fallocate_ex(FCFSPosixAPIContext *ctx,\n            int fd, off_t offset, off_t len);\n\n    int fcfs_posix_fadvise_ex(FCFSPosixAPIContext *ctx, int fd,\n            off_t offset, off_t len, int advice);\n\n    int fcfs_dprintf(int fd, const char *format, ...)\n        __gcc_attribute__ ((format (printf, 2, 3)));\n\n    int fcfs_vdprintf(int fd, const char *format, va_list ap);\n\n    int fcfs_chdir_ex(FCFSPosixAPIContext *ctx, const char *path);\n\n    int fcfs_fchdir(int fd);\n\n    char *fcfs_getcwd_ex(FCFSPosixAPIContext *ctx, char *buf, size_t size);\n\n    char *fcfs_getwd_ex(FCFSPosixAPIContext *ctx, char *buf);\n\n    int fcfs_chroot_ex(FCFSPosixAPIContext *ctx, const char *path);\n\n    int fcfs_dup_ex(FCFSPosixAPIContext *ctx, int fd);\n\n    int fcfs_dup2_ex(FCFSPosixAPIContext *ctx, int fd1, int fd2);\n\n    void *fcfs_mmap_ex(FCFSPosixAPIContext *ctx, void *addr, size_t length,\n            int prot, int flags, int fd, off_t offset);\n\n    int fcfs_munmap_ex(FCFSPosixAPIContext *ctx, void *addr, size_t length);\n\n    /* following functions for internal use only */\n    static inline FCFSPosixAPIFileInfo *fcfs_get_file_handle(int fd)\n    {\n        return fcfs_fd_manager_get(fd);\n    }\n\n    int fcfs_file_open(FCFSPosixAPIContext *ctx, const char *path,\n            const int flags, const int mode, const\n            FCFSPosixAPITPIDType tpid_type);\n\n    //for fread\n    ssize_t fcfs_file_read(int fd, void *buff, size_t size, size_t n);\n\n    //for fwrite\n    ssize_t fcfs_file_write(int fd, const void *buff, size_t size, size_t n);\n\n    ssize_t fcfs_file_readline(int fd, char *s, size_t size);\n\n    //for fgets\n    ssize_t fcfs_file_gets(int fd, char *s, size_t size);\n\n    //for getdelim\n    ssize_t fcfs_file_getdelim(int fd, char **line, size_t *size, int delim);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/std/posix_api.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"fastcommon/logger.h\"\n#include \"sf/idempotency/client/client_channel.h\"\n#include \"posix_api.h\"\n\n#define DUMMY_MOUNTPOINT_STR  \"/fastcfs/dummy/\"\n#define DUMMY_MOUNTPOINT_LEN  (sizeof(DUMMY_MOUNTPOINT_STR) - 1)\n\nFCFSPosixAPIGlobalVars g_fcfs_papi_global_vars = {\n    {{NULL, NULL}, {DUMMY_MOUNTPOINT_STR, DUMMY_MOUNTPOINT_LEN}}\n};\n\nstatic int load_posix_api_config(FCFSPosixAPIContext *ctx,\n        const char *ns, IniFullContext *ini_ctx,\n        const char *fdir_section_name)\n{\n    int result;\n\n    ctx->nsmp.ns = (char *)ns;\n    FC_SET_STRING_NULL(ctx->mountpoint);\n    if ((result=fcfs_api_load_ns_mountpoint(ini_ctx,\n                    fdir_section_name, &ctx->nsmp,\n                    &ctx->mountpoint, false)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nint fcfs_posix_api_init_ex1(FCFSPosixAPIContext *ctx, const char\n        *log_prefix_name, const char *ns, const char *config_filename,\n        const char *fdir_section_name, const char *fs_section_name,\n        const bool publish)\n{\n    const bool need_lock = true;\n    const bool persist_additional_gids = false;\n    int result;\n    IniContext iniContext;\n    IniFullContext ini_ctx;\n\n    log_try_init();\n    if ((result=iniLoadFromFile(config_filename, &iniContext)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                __LINE__, config_filename, result);\n        return result;\n    }\n\n    FAST_INI_SET_FULL_CTX_EX(ini_ctx, config_filename,\n            NULL, &iniContext);\n    do {\n        if ((result=load_posix_api_config(ctx, ns, &ini_ctx,\n                        fdir_section_name)) != 0)\n        {\n            break;\n        }\n\n        if ((result=fcfs_api_pooled_init_ex1(&ctx->api_ctx,\n                        ns, &ini_ctx, fdir_section_name,\n                        fs_section_name, need_lock,\n                        persist_additional_gids)) != 0)\n        {\n            break;\n        }\n\n        if ((result=fcfs_api_check_mountpoint(config_filename,\n                        &ctx->mountpoint)) != 0)\n        {\n            break;\n        }\n\n        if ((result=fcfs_api_load_idempotency_config_ex(log_prefix_name,\n                        &ini_ctx, fdir_section_name, fs_section_name)) != 0)\n        {\n            break;\n        }\n    } while (0);\n\n    iniFreeContext(&iniContext);\n    if (result != 0) {\n        return result;\n    }\n\n    if ((result=fcfs_api_client_session_create(\n                    &ctx->api_ctx, publish)) != 0)\n    {\n        return result;\n    }\n    return fcfs_fd_manager_init();\n}\n\nvoid fcfs_posix_api_log_configs_ex(FCFSPosixAPIContext *ctx,\n        const char *fdir_section_name, const char *fs_section_name)\n{\n    BufferInfo sf_idempotency_config;\n    char buff[256];\n    char rdma_busy_polling[128];\n    char owner_config[2 * NAME_MAX + 64];\n\n    sf_idempotency_config.buff = buff;\n    sf_idempotency_config.alloc_size = sizeof(buff);\n    fcfs_api_log_client_common_configs(&ctx->api_ctx,\n            fdir_section_name, fs_section_name,\n            &sf_idempotency_config, owner_config);\n\n    if (ctx->api_ctx.rdma.enabled) {\n        sprintf(rdma_busy_polling, \"rdma busy polling: %s, \",\n                ctx->api_ctx.rdma.busy_polling ? \"true\" : \"false\");\n    } else {\n        *rdma_busy_polling = '\\0';\n    }\n\n    logInfo(\"%sFastDIR namespace: %s, %smountpoint: %s, %s\",\n            rdma_busy_polling, ctx->nsmp.ns,\n            sf_idempotency_config.buff,\n            ctx->nsmp.mountpoint, owner_config);\n}\n\nvoid fcfs_posix_api_destroy_ex(FCFSPosixAPIContext *ctx)\n{\n    if (ctx->mountpoint.str != NULL) {\n        fcfs_api_free_ns_mountpoint(&ctx->nsmp);\n        ctx->mountpoint.str = NULL;\n        fcfs_api_destroy_ex(&ctx->api_ctx);\n    }\n}\n"
  },
  {
    "path": "src/api/std/posix_api.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_POSIX_API_H\n#define _FCFS_POSIX_API_H\n\n#include <unistd.h>\n#include <sys/types.h>\n#include \"fastcommon/shared_func.h\"\n#include \"api_types.h\"\n#include \"fd_manager.h\"\n#include \"papi.h\"\n#include \"capi.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern FCFSPosixAPIGlobalVars g_fcfs_papi_global_vars;\n\n    /** FastCFS POSIX API init\n     * parameters:\n     *   ctx: the POSIX API context\n     *   log_prefix_name: the prefix name for log filename, NULL for stderr\n     *   ns: the namespace/poolname of FastDIR\n     *   config_filename: the config filename, eg. /etc/fastcfs/fcfs/fuse.conf\n     *   fdir_section_name: the section name of FastDIR\n     *   fs_section_name: the section name of FastStore\n     *   publish: if publish the session, this parameter is valid when auth enabled\n     * return: error no, 0 for success, != 0 fail\n    */\n    int fcfs_posix_api_init_ex1(FCFSPosixAPIContext *ctx,\n            const char *log_prefix_name, const char *ns,\n            const char *config_filename, const char *fdir_section_name,\n            const char *fs_section_name, const bool publish);\n\n    /** FastCFS POSIX API init with default section names\n     * parameters:\n     *   ctx: the POSIX API context\n     *   log_prefix_name: the prefix name for log filename, NULL for stderr\n     *   ns: the namespace/poolname of FastDIR\n     *   config_filename: the config filename, eg. /etc/fastcfs/fcfs/fuse.conf\n     * return: error no, 0 for success, != 0 fail\n    */\n    static inline int fcfs_posix_api_init_ex(FCFSPosixAPIContext *ctx,\n            const char *log_prefix_name, const char *ns,\n            const char *config_filename)\n    {\n        const bool publish = true;\n        return fcfs_posix_api_init_ex1(ctx, log_prefix_name, ns,\n                config_filename, FCFS_API_DEFAULT_FASTDIR_SECTION_NAME,\n                FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME, publish);\n    }\n\n    /** FastCFS POSIX API init with the global context\n     * parameters:\n     *   log_prefix_name: the prefix name for log filename, NULL for stderr\n     *   ns: the namespace/poolname of FastDIR\n     *   config_filename: the config filename, eg. /etc/fastcfs/fcfs/fuse.conf\n     * return: error no, 0 for success, != 0 fail\n    */\n    static inline int fcfs_posix_api_init(const char *log_prefix_name,\n            const char *ns, const char *config_filename)\n    {\n        return fcfs_posix_api_init_ex(&g_fcfs_papi_global_vars.ctx,\n                log_prefix_name, ns, config_filename);\n    }\n\n    /** log configs of FastCFS POSIX API\n     * parameters:\n     *   ctx: the POSIX API context\n     *   fdir_section_name: the section name of FastDIR\n     *   fs_section_name: the section name of FastStore\n     * return: none\n    */\n    void fcfs_posix_api_log_configs_ex(FCFSPosixAPIContext *ctx,\n            const char *fdir_section_name, const char *fs_section_name);\n\n    /** log configs of FastCFS POSIX API with the global context\n     *  and default section names\n     *\n     * return: none\n    */\n    static inline void fcfs_posix_api_log_configs()\n    {\n        fcfs_posix_api_log_configs_ex(&g_fcfs_papi_global_vars.ctx,\n                FCFS_API_DEFAULT_FASTDIR_SECTION_NAME,\n                FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME);\n    }\n\n    /** FastCFS POSIX API start (create the background threads)\n     * parameters:\n     *   ctx: the POSIX API context\n     * return: error no, 0 for success, != 0 fail\n    */\n    static inline int fcfs_posix_api_start_ex(FCFSPosixAPIContext *ctx)\n    {\n        return fcfs_api_start_ex(&ctx->api_ctx);\n    }\n\n    /** FastCFS POSIX API init and start\n     * parameters:\n     *   ctx: the POSIX API context\n     *   log_prefix_name: the prefix name for log filename, NULL for stderr\n     *   ns: the namespace/poolname of FastDIR\n     *   config_filename: the config filename, eg. /etc/fastcfs/fcfs/fuse.conf\n     * return: error no, 0 for success, != 0 fail\n    */\n    static inline int fcfs_posix_api_init_start_ex(\n            FCFSPosixAPIContext *ctx, const char *log_prefix_name,\n            const char *ns, const char *config_filename)\n    {\n        int result;\n\n        if ((result=fcfs_posix_api_init_ex(ctx, log_prefix_name,\n                        ns, config_filename)) != 0)\n        {\n            return result;\n        }\n        return fcfs_api_start_ex(&ctx->api_ctx);\n    }\n\n    /** FastCFS POSIX API stop the background threads\n     * parameters:\n     *   ctx: the POSIX API context\n     * return: none\n    */\n    static inline void fcfs_posix_api_stop_ex(FCFSPosixAPIContext *ctx)\n    {\n        fcfs_api_terminate_ex(&ctx->api_ctx);\n    }\n\n    /** FastCFS POSIX API destroy\n     * parameters:\n     *   ctx: the POSIX API context\n     * return: none\n    */\n    void fcfs_posix_api_destroy_ex(FCFSPosixAPIContext *ctx);\n\n\n    /** FastCFS POSIX API start (create the background threads)\n     *\n     * return: error no, 0 for success, != 0 fail\n    */\n    static inline int fcfs_posix_api_start()\n    {\n        return fcfs_posix_api_start_ex(&g_fcfs_papi_global_vars.ctx);\n    }\n\n    /** FastCFS POSIX API init and start with the global context\n     * parameters:\n     *   log_prefix_name: the prefix name for log filename, NULL for stderr\n     *   ns: the namespace/poolname of FastDIR\n     *   config_filename: the config filename, eg. /etc/fastcfs/fcfs/fuse.conf\n     * return: error no, 0 for success, != 0 fail\n    */\n    static inline int fcfs_posix_api_init_start(const char *log_prefix_name,\n            const char *ns, const char *config_filename)\n    {\n        return fcfs_posix_api_init_start_ex(&g_fcfs_papi_global_vars.ctx,\n                log_prefix_name, ns, config_filename);\n    }\n\n    /** FastCFS POSIX API stop the background threads with the global context\n     *\n     * return: none\n    */\n    static inline void fcfs_posix_api_stop()\n    {\n        fcfs_posix_api_stop_ex(&g_fcfs_papi_global_vars.ctx);\n    }\n\n    /** FastCFS POSIX API destroy with the global context\n     *\n     * return: none\n    */\n    static inline void fcfs_posix_api_destroy()\n    {\n        fcfs_posix_api_destroy_ex(&g_fcfs_papi_global_vars.ctx);\n    }\n\n\n    static inline pid_t fcfs_posix_api_getpid()\n    {\n        return getpid();\n    }\n\n    static inline pid_t fcfs_posix_api_gettid(\n            const FCFSPosixAPITPIDType tpid_type)\n    {\n        if (tpid_type == fcfs_papi_tpid_type_pid) {\n            return getpid();\n        } else {\n            return fc_gettid();\n        }\n    }\n\n    static inline int fcfs_posix_api_set_owner_ex(\n            FCFSPosixAPIContext *pctx)\n    {\n        return fcfs_api_set_owner(&pctx->api_ctx);\n    }\n\n    static inline int fcfs_posix_api_set_owner()\n    {\n        return fcfs_posix_api_set_owner_ex(&g_fcfs_papi_global_vars.ctx);\n    }\n\n    static inline void fcfs_posix_api_set_fctx(FCFSAPIFileContext *fctx,\n            const FCFSPosixAPIContext *pctx, const mode_t mode,\n            const FCFSPosixAPITPIDType tpid_type)\n    {\n        fctx->mode = (mode & (~fc_get_umask()));\n        fctx->oper = pctx->api_ctx.owner.oper;\n        fctx->tid = fcfs_posix_api_gettid(tpid_type);\n    }\n\n#define FCFS_API_IS_MY_MOUNTPOINT_EX(ctx, path) \\\n    (strlen(path) > (ctx)->mountpoint.len && \\\n     ((ctx)->mountpoint.len == 0 || \\\n      memcmp(path, (ctx)->mountpoint.str, \\\n          (ctx)->mountpoint.len) == 0))\n\n#define FCFS_API_IS_MY_MOUNTPOINT(path) \\\n    FCFS_API_IS_MY_MOUNTPOINT_EX(&g_fcfs_papi_global_vars.ctx, path)\n\n#define FCFS_API_CHECK_PATH_MOUNTPOINT_EX(file, line, ctx, path, func, retval) \\\n    do { \\\n        if (!FCFS_API_IS_MY_MOUNTPOINT_EX(ctx, path)) \\\n        { \\\n            logError(\"file: %s, line: %d, \"  \\\n                    \"%s path: %s is not the FastCFS mountpoint!\", \\\n                    file, line, func, path); \\\n            errno = EOPNOTSUPP; \\\n            return retval; \\\n        } \\\n    } while (0)\n\n#define FCFS_API_CHECK_PATH_MOUNTPOINT(ctx, path, func) \\\n    FCFS_API_CHECK_PATH_MOUNTPOINT_EX(__FILE__, __LINE__, ctx, path, func, -1)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/api/tests/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I../../include\nLIB_PATH = -L.. $(LIBS) -lfcfsapi -lfsapi -lfdirclient -lfsclient -lfastcommon -lserverframe\nTARGET_PATH = $(TARGET_PREFIX)/bin\n\nSTATIC_OBJS =\n\nALL_PRGS = fcfs_test_file_op fcfs_test_file_copy fcfs_test_papi_copy \\\n           fcfs_test_read_ahead fcfs_beachmark\n\nall: $(STATIC_OBJS) $(ALL_PRGS)\n\n.o:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n\ninstall:\n\tmkdir -p $(TARGET_PATH)\n\tcp -f $(ALL_PRGS) $(TARGET_PATH)\n\nclean:\n\trm -f $(STATIC_OBJS) $(ALL_PRGS)\n\n"
  },
  {
    "path": "src/api/tests/fcfs_beachmark.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <sys/uio.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/fc_atomic.h\"\n#include \"fastcfs/api/std/posix_api.h\"\n\ntypedef enum {\n    fcfs_beachmark_mode_read,\n    fcfs_beachmark_mode_write,\n    fcfs_beachmark_mode_randread,\n    fcfs_beachmark_mode_randwrite\n} BeachmarkMode;\n\ntypedef struct {\n    int index;\n    volatile int64_t success_count;\n} BeachmarkThreadInfo;\n\nstatic struct {\n    const char *config_filename;\n    char *ns;\n    int64_t file_size;\n    struct {\n        BeachmarkMode val;\n        const char *str;\n    } mode;\n    int buffer_size;\n    int thread_count;\n    int runtime;\n    string_t filename_prefix;\n    bool is_fcfs_input;\n} cfg = {FCFS_FUSE_DEFAULT_CONFIG_FILENAME,\n    \"fs\", 1 * 1024 * 1024 * 1024, {fcfs_beachmark_mode_read,\n        \"read\"}, 4 * 1024, 1, 60, {NULL, 0}, false};\n\nstatic struct {\n    volatile int ready_count;\n    volatile int running_count;\n    volatile char ready_flag;\n    volatile char continue_flag;\n    time_t start_time;\n    struct {\n        int cur;\n        int avg;\n        int max;\n    } iops;\n\n    struct {\n        char cur[32];\n        char avg[32];\n        char max[32];\n    } iops_buff;\n\n    BeachmarkThreadInfo *threads;\n    BeachmarkThreadInfo *tend;\n} st = {0, 0, 0, 1};\n\nstatic inline void usage(char *argv[])\n{\n    fprintf(stderr, \"Usage: %s [-c config_filename=%s] \"\n            \"[-n namespace=fs] [-b buffer_size=4KB] \"\n            \"[-s file_size=1G] [-m mode=read] \"\n            \"[-T threads=1] [-t runtime=60] <-f filename_prefix>\\n\"\n            \"\\t mode value list: read, write, randrand, randwrite\\n\\n\"\n            \"for example: \\n\"\n            \"\\tfcfs_beachmark -m randread -s 256M -T 4 -t 300 \"\n            \"-f /opt/fastcfs/fuse/test_file\\n\\n\",\n            argv[0], FCFS_FUSE_DEFAULT_CONFIG_FILENAME);\n}\n\nstatic inline int open_file(const char *filename, int flags)\n{\n    if (cfg.is_fcfs_input) {\n        return fcfs_open(filename, flags);\n    } else {\n        return open(filename, flags);\n    }\n}\n\nstatic inline void close_file(int fd)\n{\n    if (cfg.is_fcfs_input) {\n        fcfs_close(fd);\n    } else {\n        close(fd);\n    }\n}\n\nstatic int create_file(const char *filename, const int64_t start_offset)\n{\n#define BUFFER_SIZE  (4 * 1024 * 1024)\n    const int flags = O_WRONLY | O_CREAT;\n    int result;\n    int bytes;\n    int fd;\n    int len;\n    int64_t start_time_us;\n    char *buff;\n    char time_buff[32];\n    unsigned char *p;\n    unsigned char *end;\n    int64_t remain;\n\n    if (cfg.is_fcfs_input) {\n        fd = fcfs_open(filename, flags, 0755);\n    } else {\n        fd = open(filename, flags, 0755);\n    }\n    if (fd < 0) {\n        result = errno != 0 ? errno : ENOENT;\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"open file %s to write fail, errno: %d, error info: %s\",\n                __LINE__, filename, result, strerror(result));\n        return result;\n    }\n\n    if (start_offset > 0) {\n        if (cfg.is_fcfs_input) {\n            bytes = fcfs_lseek(fd, start_offset, SEEK_SET);\n        } else {\n            bytes = lseek(fd, start_offset, SEEK_SET);\n        }\n\n        if (bytes < 0) {\n            result = errno != 0 ? errno : ENOENT;\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"lseek file %s fail, errno: %d, error info: %s\",\n                    __LINE__, filename, result, strerror(result));\n            return result;\n        }\n    }\n\n    start_time_us = get_current_time_us();\n    printf(\"creating file: %s ...\\n\", filename);\n    if ((buff=fc_malloc(BUFFER_SIZE)) == NULL) {\n        close_file(fd);\n        return ENOMEM;\n    }\n\n    end = (unsigned char *)buff + BUFFER_SIZE;\n    for (p=(unsigned char *)buff; p<end; p++) {\n        *p = ((int64_t)rand() * 255) / (int64_t)RAND_MAX;\n    }\n\n    result = 0;\n    remain = cfg.file_size - start_offset;\n    while (remain > 0) {\n        len = remain > BUFFER_SIZE ? BUFFER_SIZE : remain;\n        if (cfg.is_fcfs_input) {\n            bytes = fcfs_write(fd, buff, len);\n        } else {\n            bytes = write(fd, buff, len);\n        }\n        if (bytes != len) {\n            result = errno != 0 ? errno : EIO;\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"write to file %s fail, errno: %d, error info: %s\",\n                    __LINE__, filename, result, strerror(result));\n            break;\n        }\n\n        remain -= len;\n    }\n\n    if (result == 0) {\n        long_to_comma_str((get_current_time_us() -\n                    start_time_us) / 1000, time_buff);\n        printf(\"create file: %s successfully, time used: %s ms\\n\",\n                filename, time_buff);\n    }\n\n    free(buff);\n    close_file(fd);\n    return result;\n}\n\nstatic int check_create_file(const char *filename)\n{\n    struct stat stbuf;\n    int ret;\n    int result;\n\n    if (cfg.is_fcfs_input) {\n        ret = fcfs_stat(filename, &stbuf);\n    } else {\n        ret = stat(filename, &stbuf);\n    }\n\n    if (ret != 0) {\n        result = errno != 0 ? errno : EIO;\n        if (result == ENOENT) {\n            stbuf.st_size = 0;\n        } else {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"stat file %s fail, errno: %d, error info: %s\",\n                    __LINE__, filename, result, strerror(result));\n            return result;\n        }\n    }\n\n    if (stbuf.st_size == cfg.file_size) {\n        return 0;\n    } else if (stbuf.st_size > cfg.file_size) {\n        if (cfg.is_fcfs_input) {\n            ret = fcfs_truncate(filename, cfg.file_size);\n        } else {\n            ret = truncate(filename, cfg.file_size);\n        }\n\n        if (ret == 0) {\n            return 0;\n        } else {\n            result = errno != 0 ? errno : EIO;\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"truncate file %s fail, errno: %d, error info: %s\",\n                    __LINE__, filename, result, strerror(result));\n            return result;\n        }\n    }\n\n    return create_file(filename, stbuf.st_size);\n}\n\nstatic int thread_run(BeachmarkThreadInfo *thread)\n{\n    int result;\n    int flags;\n    int fd;\n    bool is_read;\n    bool is_sequence;\n    char *buff;\n    int64_t blocks;\n    int64_t offset;\n    int size;\n    int bytes;\n    char *filename;\n    char *p;\n\n    buff = (char *)fc_malloc(cfg.buffer_size);\n    if (buff == NULL) {\n        return ENOMEM;\n    }\n\n    filename = fc_malloc(cfg.filename_prefix.len + 8);\n    if (filename == NULL) {\n        return ENOMEM;\n    }\n\n    p = filename;\n    memcpy(p, cfg.filename_prefix.str, cfg.filename_prefix.len);\n    p += cfg.filename_prefix.len;\n    *p++ = '.';\n    p += fc_itoa(thread->index, p);\n    *p = '\\0';\n    if ((result=check_create_file(filename)) != 0) {\n        return result;\n    }\n\n    switch (cfg.mode.val) {\n        case fcfs_beachmark_mode_read:\n        case fcfs_beachmark_mode_randread:\n            is_read = true;\n            flags = O_RDONLY;\n            break;\n        case fcfs_beachmark_mode_write:\n        case fcfs_beachmark_mode_randwrite:\n            is_read = false;\n            flags = O_WRONLY;\n            break;\n        default:\n            return EINVAL;\n    }\n\n    is_sequence = (cfg.mode.val == fcfs_beachmark_mode_read ||\n            cfg.mode.val == fcfs_beachmark_mode_write);\n    if ((fd=open_file(filename, flags)) < 0) {\n        result = errno != 0 ? errno : ENOENT;\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"open file %s to read fail, errno: %d, error info: %s\",\n                __LINE__, filename, result, strerror(result));\n        return result;\n    }\n\n    __sync_add_and_fetch(&st.ready_count, 1);\n    while (FC_ATOMIC_GET(st.continue_flag) &&\n            !FC_ATOMIC_GET(st.ready_flag))\n    {\n        fc_sleep_ms(10);\n    }\n\n    offset = 0;\n    size = cfg.buffer_size;\n    blocks = cfg.file_size / cfg.buffer_size;\n    while (FC_ATOMIC_GET(st.continue_flag)) {\n        switch (cfg.mode.val) {\n            case fcfs_beachmark_mode_read:\n            case fcfs_beachmark_mode_write:\n                size = cfg.file_size - offset;\n                if (size <= 0) {\n                    offset = 0;\n                    size = cfg.buffer_size;\n                } else if (size > cfg.buffer_size) {\n                    size = cfg.buffer_size;\n                }\n                break;\n            case fcfs_beachmark_mode_randread:\n            case fcfs_beachmark_mode_randwrite:\n                offset = (((int64_t)rand() * blocks) / (int64_t)RAND_MAX)\n                    * cfg.buffer_size;\n                break;\n            default:\n                break;\n        }\n\n        if (is_read) {\n            if (cfg.is_fcfs_input) {\n                bytes = fcfs_pread(fd, buff, size, offset);\n            } else {\n                bytes = pread(fd, buff, size, offset);\n            }\n        } else {\n            if (cfg.is_fcfs_input) {\n                bytes = fcfs_pwrite(fd, buff, size, offset);\n            } else {\n                bytes = pwrite(fd, buff, size, offset);\n            }\n        }\n        if (bytes != size) {\n            result = errno != 0 ? errno : EIO;\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"read from file %s fail, errno: %d, error info: %s\",\n                    __LINE__, filename, result, strerror(result));\n            return result;\n        }\n\n        FC_ATOMIC_INC(thread->success_count);\n        if (is_sequence) {\n            offset += bytes;\n        }\n    }\n\n    close_file(fd);\n    return 0;\n}\n\nstatic void *thread_entrance(void *arg)\n{\n    BeachmarkThreadInfo *thread;\n\n    thread = arg;\n    __sync_add_and_fetch(&st.running_count, 1);\n    thread_run(thread);\n    __sync_sub_and_fetch(&st.running_count, 1);\n    return NULL;\n}\n\nstatic void output(const time_t current_time)\n{\n    BeachmarkThreadInfo *thread;\n    static time_t last_time = 0;\n    static int64_t last_count = 0;\n    int64_t total_count;\n    int time_distance;\n\n    if (last_time == 0) {\n        last_time = st.start_time;\n        printf(\"\\n\");\n    }\n\n    total_count = 0;\n    for (thread=st.threads; thread<st.tend; thread++) {\n        total_count += FC_ATOMIC_GET(thread->success_count);\n    }\n\n    time_distance = current_time - last_time;\n    if (time_distance > 0) {\n        st.iops.cur = (total_count - last_count) / time_distance;\n        if (st.iops.cur > st.iops.max) {\n            st.iops.max = st.iops.cur;\n        }\n        st.iops.avg = total_count / (current_time - st.start_time);\n        long_to_comma_str(st.iops.cur, st.iops_buff.cur);\n        long_to_comma_str(st.iops.avg, st.iops_buff.avg);\n        printf(\"running time: %4d seconds, %s IOPS {current: %s, avg: %s}\\n\",\n                (int)(current_time - st.start_time), cfg.mode.str,\n                st.iops_buff.cur, st.iops_buff.avg);\n        last_time = current_time;\n        last_count = total_count;\n    }\n}\n\nstatic void sigQuitHandler(int sig)\n{\n    if (FC_ATOMIC_GET(st.continue_flag)) {\n        FC_ATOMIC_SET(st.continue_flag, 0);\n        printf(\"file: \"__FILE__\", line: %d, \"\n                \"catch signal %d, program exiting...\\n\",\n                __LINE__, sig);\n    }\n}\n\nstatic int setup_signal_handler()\n{\n    struct sigaction act;\n\n    memset(&act, 0, sizeof(act));\n    sigemptyset(&act.sa_mask);\n    act.sa_handler = sigQuitHandler;\n    if(sigaction(SIGINT, &act, NULL) < 0 ||\n            sigaction(SIGTERM, &act, NULL) < 0 ||\n            sigaction(SIGQUIT, &act, NULL) < 0)\n    {\n        fprintf(stderr, \"file: \"__FILE__\", line: %d, \"\n                \"call sigaction fail, errno: %d, error info: %s\\n\",\n                __LINE__, errno, strerror(errno));\n        return errno != 0 ? errno : EBUSY;\n    }\n\n    return 0;\n}\n\nstatic int beachmark()\n{\n    const char *log_prefix_name = NULL;\n\tint result;\n    int count;\n    int bytes;\n    long i;\n    time_t current_time;\n    time_t end_time;\n    pthread_t *tids;\n    void **args;\n    char buffer_size_prompt[32];\n    BeachmarkThreadInfo *thread;\n\n    if ((result=fcfs_posix_api_init(log_prefix_name,\n                    cfg.ns, cfg.config_filename)) != 0)\n    {\n        return result;\n    }\n    if ((result=fcfs_posix_api_start()) != 0) {\n        return result;\n    }\n\n    if ((result=setup_signal_handler()) != 0) {\n        return result;\n    }\n\n    fcfs_posix_api_log_configs();\n    cfg.is_fcfs_input = FCFS_API_IS_MY_MOUNTPOINT(cfg.filename_prefix.str);\n\n    bytes = sizeof(BeachmarkThreadInfo) * cfg.thread_count;\n    st.threads = fc_malloc(bytes);\n    if (st.threads == NULL) {\n        return ENOMEM;\n    }\n    memset(st.threads, 0, bytes);\n    st.tend = st.threads + cfg.thread_count;\n\n    tids = fc_malloc(sizeof(pthread_t) * cfg.thread_count);\n    if (tids == NULL) {\n        return ENOMEM;\n    }\n\n    args = fc_malloc(sizeof(void *) * cfg.thread_count);\n    if (args == NULL) {\n        return ENOMEM;\n    }\n\n    for (i=0, thread=st.threads; i<cfg.thread_count; i++, thread++) {\n        thread->index = i;\n        args[i] = thread;\n    }\n\n    count = cfg.thread_count;\n    if ((result=create_work_threads(&count, thread_entrance,\n                    args, tids, 256 * 1024)) != 0)\n    {\n        return result;\n    }\n\n    if (cfg.buffer_size % 1024 != 0) {\n        long_to_comma_str(cfg.buffer_size, buffer_size_prompt);\n    } else {\n        sprintf(buffer_size_prompt, \"%d KB\", cfg.buffer_size / 1024);\n    }\n    printf(\"\\nthreads: %d, mode: %s, file size: %\"PRId64\" MB, \"\n            \"buffer size: %s\\n\\n\", cfg.thread_count, cfg.mode.str,\n            cfg.file_size / (1024 * 1024), buffer_size_prompt);\n\n    fc_sleep_ms(100);\n    while (FC_ATOMIC_GET(st.continue_flag) &&\n            FC_ATOMIC_GET(st.ready_count) <\n            FC_ATOMIC_GET(st.running_count))\n    {\n        fc_sleep_ms(10);\n    }\n    FC_ATOMIC_SET(st.ready_flag, 1);\n\n    st.start_time = time(NULL);\n    end_time = st.start_time + cfg.runtime;\n    do {\n        sleep(1);\n        current_time = time(NULL);\n        output(current_time);\n    } while (FC_ATOMIC_GET(st.continue_flag) &&\n            FC_ATOMIC_GET(st.running_count) > 0 &&\n            current_time < end_time);\n\n    long_to_comma_str(st.iops.avg, st.iops_buff.avg);\n    long_to_comma_str(st.iops.max, st.iops_buff.max);\n    printf(\"\\nrunning time: %4d seconds, %s IOPS {avg: %s, max: %s}\\n\",\n            (int)(current_time - st.start_time), cfg.mode.str,\n            st.iops_buff.avg, st.iops_buff.max);\n\n    FC_ATOMIC_SET(st.continue_flag, 0);\n    while (FC_ATOMIC_GET(st.running_count) > 0) {\n        sleep(1);\n    }\n\n    fcfs_posix_api_stop();\n    return 0;\n}\n\nint main(int argc, char *argv[])\n{\n    int result;\n\tint ch;\n    int64_t bytes;\n\n    if (argc < 3) {\n        usage(argv);\n        return 1;\n    }\n\n    log_try_init();\n    while ((ch=getopt(argc, argv, \"hc:m:n:b:T:t:f:s:\")) != -1) {\n        switch (ch) {\n            case 'h':\n                usage(argv);\n                return 0;\n            case 'c':\n                cfg.config_filename = optarg;\n                break;\n            case 'm':\n                if (strcasecmp(optarg, \"read\") == 0) {\n                    cfg.mode.val = fcfs_beachmark_mode_read;\n                    cfg.mode.str = \"read\";\n                } else if (strcasecmp(optarg, \"write\") == 0) {\n                    cfg.mode.val = fcfs_beachmark_mode_write;\n                    cfg.mode.str = \"write\";\n                } else if (strcasecmp(optarg, \"randread\") == 0) {\n                    cfg.mode.val = fcfs_beachmark_mode_randread;\n                    cfg.mode.str = \"randread\";\n                } else if (strcasecmp(optarg, \"randwrite\") == 0) {\n                    cfg.mode.val = fcfs_beachmark_mode_randwrite;\n                    cfg.mode.str = \"randwrite\";\n                } else {\n                    logError(\"file: \"__FILE__\", line: %d, \"\n                            \"invalid mode: %s!\", __LINE__, optarg);\n                    usage(argv);\n                    return EINVAL;\n                }\n                break;\n            case 'n':\n                cfg.ns = optarg;\n                break;\n            case 's':\n                if ((result=parse_bytes(optarg, 1, &cfg.file_size)) != 0) {\n                    usage(argv);\n                    return result;\n                }\n                break;\n            case 'T':\n                cfg.thread_count = strtol(optarg, NULL, 10);\n                if (cfg.thread_count <= 0) {\n                    logError(\"file: \"__FILE__\", line: %d, \"\n                            \"invalid thread count: %d which <= 0\",\n                            __LINE__, cfg.thread_count);\n                    return EINVAL;\n                }\n                break;\n            case 't':\n                cfg.runtime = strtol(optarg, NULL, 10);\n                if (cfg.runtime <= 0) {\n                    logError(\"file: \"__FILE__\", line: %d, \"\n                            \"invalid runtime: %d which <= 0\",\n                            __LINE__, cfg.runtime);\n                    return EINVAL;\n                }\n                break;\n            case 'b':\n                if ((result=parse_bytes(optarg, 1, &bytes)) != 0) {\n                    usage(argv);\n                    return result;\n                }\n                if (bytes <= 0) {\n                    logError(\"file: \"__FILE__\", line: %d, \"\n                            \"invalid buffer size: %\"PRId64\" which <= 0\",\n                            __LINE__, bytes);\n                    return EINVAL;\n                }\n                cfg.buffer_size = bytes;\n                break;\n            case 'f':\n                FC_SET_STRING(cfg.filename_prefix, optarg);\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    if (cfg.filename_prefix.str == NULL) {\n        fprintf(stderr, \"expect parameter -f filename_prefix\\n\\n\");\n        usage(argv);\n        return EINVAL;\n    }\n\n    if (cfg.file_size < cfg.buffer_size) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"invalid file size: %\"PRId64\" which < buffer size: %d\",\n                __LINE__, cfg.file_size, cfg.buffer_size);\n        return EINVAL;\n    }\n\n    srand(time(NULL));\n    return beachmark();\n}\n"
  },
  {
    "path": "src/api/tests/fcfs_test_file_copy.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcfs/api/fcfs_api.h\"\n\nconst char *config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME;\nstatic char *ns = \"fs\";\nstatic int buffer_size = 128 * 1024;\nstatic char *input_filename;\nstatic char *fs_filename;\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"Usage: %s [-c config_filename=%s] \"\n            \"[-n namespace=fs] [-b buffer_size=128KB] \"\n            \"<input_local_filename> <fs_filename>\\n\\n\",\n            argv[0], FCFS_FUSE_DEFAULT_CONFIG_FILENAME);\n}\n\nstatic int copy_file()\n{\n    const bool publish = false;\n#define FIXED_BUFFEER_SIZE  (128 * 1024)\n    FCFSAPIFileContext fctx;\n\tint result;\n    int fd;\n    FCFSAPIFileInfo fi;\n    char fixed_buff[FIXED_BUFFEER_SIZE];\n    char async_report_config[256];\n    char write_combine_config[512];\n    char *buff;\n    char new_fs_filename[PATH_MAX];\n    int read_bytes;\n    int write_bytes;\n    int current_write;\n\n    if ((fd=open(input_filename, O_RDONLY)) < 0) {\n        result = errno != 0 ? errno : ENOENT;\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"open file %s to read fail, \"\n                \"errno: %d, error info: %s\",\n                __LINE__, input_filename,\n                result, strerror(result));\n        return result;\n    }\n\n    if ((result=fcfs_api_pooled_init_with_auth(ns,\n                    config_filename, publish)) != 0)\n    {\n        return result;\n    }\n    if ((result=fcfs_api_start()) != 0) {\n        return result;\n    }\n\n\n    fcfs_api_async_report_config_to_string_ex(&g_fcfs_api_ctx,\n            async_report_config, sizeof(async_report_config));\n    fdir_client_log_config_ex(g_fcfs_api_ctx.contexts.fdir,\n            async_report_config, true);\n\n    fs_api_config_to_string(write_combine_config,\n            sizeof(write_combine_config));\n    fs_client_log_config_ex(g_fcfs_api_ctx.contexts.fsapi->fs,\n            write_combine_config, true);\n\n    if (fs_filename[strlen(fs_filename) - 1] == '/') {\n        const char *filename;\n        filename = strrchr(input_filename, '/');\n        if (filename != NULL) {\n            filename++;  //skip \"/\"\n        } else {\n            filename = input_filename;\n        }\n        snprintf(new_fs_filename, sizeof(new_fs_filename),\n                \"%s%s\", fs_filename, filename);\n    } else {\n        snprintf(new_fs_filename, sizeof(new_fs_filename),\n                \"%s\", fs_filename);\n    }\n\n    fctx.tid = getpid();\n    fctx.mode = 0755;\n    FDIR_SET_OPERATOR(fctx.oper, geteuid(), getegid(), 0, NULL);\n    if ((result=fcfs_api_open(&fi, new_fs_filename,\n                    O_CREAT | O_WRONLY, &fctx)) != 0)\n    {\n        return result;\n    }\n\n    if (buffer_size <= FIXED_BUFFEER_SIZE) {\n        buff = fixed_buff;\n    } else {\n        buff = (char *)fc_malloc(buffer_size);\n        if (buff == NULL) {\n            return ENOMEM;\n        }\n    }\n\n    write_bytes = 0;\n    while (1) {\n        read_bytes = read(fd, buff, buffer_size);\n        if (read_bytes == 0) {\n            break;\n        } else if (read_bytes < 0) {\n            result = errno != 0 ? errno : ENOENT;\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"read from file %s fail, \"\n                    \"errno: %d, error info: %s\",\n                    __LINE__, input_filename,\n                    result, strerror(result));\n            return result;\n        }\n\n        result = fcfs_api_write(&fi, buff, read_bytes, &current_write);\n        if (result != 0) {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"write to file %s fail, \"\n                    \"errno: %d, error info: %s\",\n                    __LINE__, fs_filename,\n                    result, STRERROR(result));\n            return result;\n        }\n\n        write_bytes += current_write;\n    }\n    fcfs_api_close(&fi);\n\n    fcfs_api_terminate();\n    return 0;\n}\n\nint main(int argc, char *argv[])\n{\n    int result;\n\tint ch;\n    int64_t bytes;\n\n    if (argc < 3) {\n        usage(argv);\n        return 1;\n    }\n\n    while ((ch=getopt(argc, argv, \"hc:n:b:\")) != -1) {\n        switch (ch) {\n            case 'h':\n                usage(argv);\n                break;\n            case 'c':\n                config_filename = optarg;\n                break;\n            case 'n':\n                ns = optarg;\n                break;\n            case 'b':\n                if ((result=parse_bytes(optarg, 1, &bytes)) != 0) {\n                    usage(argv);\n                    return result;\n                }\n                buffer_size = bytes;\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    if (optind + 1 >= argc) {\n        usage(argv);\n        return 1;\n    }\n\n    log_init();\n    //g_log_context.log_level = LOG_DEBUG;\n\n    input_filename = argv[optind];\n    fs_filename = argv[optind + 1];\n    if (strlen(fs_filename) == 0) {\n        usage(argv);\n        return EINVAL;\n    }\n\n    return copy_file();\n}\n"
  },
  {
    "path": "src/api/tests/fcfs_test_file_op.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcfs/api/fcfs_api.h\"\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"Usage: %s [-c config_filename=%s] \"\n            \"-n [namespace=fs] \\n\\t[-o offset=0] [-l length=0 for auto] \"\n            \"[-A append mode] \\n\\t[-T truncate mode] [-S set file size = -1] \"\n            \"-i <input_filename> <filename>\\n\\n\", argv[0],\n            FCFS_FUSE_DEFAULT_CONFIG_FILENAME);\n}\n\nint main(int argc, char *argv[])\n{\n    const bool publish = false;\n    const char *config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME;\n    FCFSAPIFileContext fctx;\n\tint ch;\n\tint result;\n    int open_flags;\n    int64_t offset = 0;\n    int64_t file_size;\n    int64_t file_size_to_set;\n    int length = 0;\n    FCFSAPIFileInfo fi;\n    char *ns = \"fs\";\n    char *input_filename = NULL;\n    char *filename;\n    char *endptr;\n    char *out_buff;\n    char *in_buff;\n    int write_bytes;\n    int read_bytes;\n\n    if (argc < 2) {\n        usage(argv);\n        return 1;\n    }\n\n    fctx.tid = getpid();\n    fctx.mode = 0755;\n    FDIR_SET_OPERATOR(fctx.oper, geteuid(), getegid(), 0, NULL);\n    open_flags = 0;\n    file_size_to_set = -1;\n    while ((ch=getopt(argc, argv, \"hc:o:n:i:l:S:AT\")) != -1) {\n        switch (ch) {\n            case 'h':\n                usage(argv);\n                break;\n            case 'c':\n                config_filename = optarg;\n                break;\n            case 'i':\n                input_filename = optarg;\n                break;\n            case 'n':\n                ns = optarg;\n                break;\n            case 'o':\n                offset = strtol(optarg, &endptr, 10);\n                break;\n            case 'l':\n                length = strtol(optarg, &endptr, 10);\n                break;\n            case 'S':\n                file_size_to_set = strtol(optarg, &endptr, 10);\n                break;\n            case 'A':\n                open_flags |= O_APPEND;\n                break;\n            case 'T':\n                open_flags |= O_TRUNC;\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    if (input_filename == NULL) {\n        fprintf(stderr, \"expect input filename\\n\");\n        usage(argv);\n        return 1;\n    }\n\n    if (optind >= argc) {\n        fprintf(stderr, \"expect filename\\n\");\n        usage(argv);\n        return 1;\n    }\n\n    log_init();\n    //g_log_context.log_level = LOG_DEBUG;\n\n    filename = argv[optind];\n    if ((result=getFileContent(input_filename, &out_buff, &file_size)) != 0) {\n        return result;\n    }\n    if (file_size == 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"empty file: %s\", __LINE__, input_filename);\n        return ENOENT;\n    }\n    if (length == 0 || length > file_size) {\n        length = file_size;\n    }\n\n    if ((result=fcfs_api_pooled_init_with_auth(ns,\n                    config_filename, publish)) != 0)\n    {\n        return result;\n    }\n\n    /*\n    {\n        struct statvfs stbuf;\n        if (fcfs_api_statvfs(filename, &stbuf) == 0) {\n            printf(\"%s fs id: %ld, total: %\"PRId64\" MB, free: %\"PRId64\" MB, \"\n                    \"avail: %\"PRId64\" MB, f_namemax: %ld, f_flag: %ld\\n\", filename,\n                    stbuf.f_fsid, (int64_t)(stbuf.f_blocks * stbuf.f_frsize) / (1024 * 1024),\n                    (int64_t)(stbuf.f_bfree * stbuf.f_frsize) / (1024 * 1024),\n                    (int64_t)(stbuf.f_bavail * stbuf.f_frsize) / (1024 * 1024),\n                    stbuf.f_namemax, stbuf.f_flag);\n        }\n    }\n    */\n\n    if ((result=fcfs_api_start()) != 0) {\n        return result;\n    }\n\n    if ((result=fcfs_api_open(&fi, filename, O_CREAT |\n                    O_WRONLY | open_flags, &fctx)) != 0)\n    {\n        return result;\n    }\n\n    if (file_size_to_set >= 0) {\n        if ((result=fcfs_api_ftruncate_ex(&fi, file_size_to_set,\n                        fctx.tid)) != 0)\n        {\n            return result;\n        }\n    }\n\n    if (offset == 0) {\n        result = fcfs_api_write(&fi, out_buff, length, &write_bytes);\n    } else {\n        result = fcfs_api_pwrite(&fi, out_buff, length, offset, &write_bytes);\n    }\n    if (result != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"write to file %s fail, offset: %\"PRId64\", length: %d, \"\n                \"write_bytes: %d, errno: %d, error info: %s\",\n                __LINE__, filename, offset, length, write_bytes,\n                result, STRERROR(result));\n        return result;\n    }\n    fcfs_api_close(&fi);\n\n    if ((result=fcfs_api_open(&fi, filename, O_RDONLY, &fctx)) != 0) {\n        return result;\n    }\n    in_buff = (char *)malloc(length);\n    if (in_buff == NULL) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"malloc %d bytes fail\", __LINE__, length);\n        return ENOMEM;\n    }\n\n    if ((result=fcfs_api_lseek(&fi, offset, SEEK_SET)) != 0) {\n        return result;\n    }\n\n    if (offset == 0) {\n        result = fcfs_api_read(&fi, in_buff, length, &read_bytes);\n    } else {\n        //result = fcfs_api_pread(&fi, in_buff, length, offset, &read_bytes);\n        result = fcfs_api_read(&fi, in_buff, length, &read_bytes);\n    }\n\n    if (result != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"read from file %s fail, offset: %\"PRId64\", length: %d, \"\n                \"read_bytes: %d, errno: %d, error info: %s\",\n                __LINE__, filename, offset, length, read_bytes,\n                result, STRERROR(result));\n        return result;\n    }\n\n    if (read_bytes != length) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"read bytes: %d != slice length: %d\",\n                __LINE__, read_bytes, length);\n        return EINVAL;\n    }\n\n    result = memcmp(in_buff, out_buff, length);\n    if (result != 0) {\n        printf(\"read and write buffer compare result: %d != 0\\n\", result);\n        return EINVAL;\n    }\n\n    fcfs_api_close(&fi);\n    fcfs_api_terminate();\n    return 0;\n}\n"
  },
  {
    "path": "src/api/tests/fcfs_test_papi_copy.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <sys/uio.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcfs/api/std/posix_api.h\"\n\nconst char *config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME;\nstatic char *ns = \"fs\";\nstatic int buffer_size = 128 * 1024;\nstatic int slice_size = 4 * 1024;\nstatic char *input_filename;\nstatic char *fs_filename;\nstatic bool use_iov = false;  //use readv & writev\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"Usage: %s [-c config_filename=%s] \"\n            \"[-n namespace=fs] [-b buffer_size=128KB] \"\n            \"[-V use readv & writev] [-s slice_size=4K] \"\n            \"<input_local_filename> <fs_filename>\\n\\n\",\n            argv[0], FCFS_FUSE_DEFAULT_CONFIG_FILENAME);\n}\n\nstatic int alloc_iovec_array(iovec_array_t *array, char *buff)\n{\n    struct iovec *iob;\n    struct iovec *last;\n    char *p;\n\n    array->count = array->alloc = (buffer_size +\n            (slice_size - 1)) / slice_size;\n    array->iovs = fc_malloc(sizeof(struct iovec) * array->alloc);\n    if (array->iovs == NULL) {\n        return ENOMEM;\n    }\n\n    p = buff;\n    last = array->iovs + (array->alloc - 1);\n    for (iob=array->iovs; iob<last; iob++) {\n        iob->iov_base = p;\n        iob->iov_len = slice_size;\n        p += iob->iov_len;\n    }\n\n    last->iov_base = p;\n    last->iov_len = (buff + buffer_size) - p;\n\n    return 0;\n}\n\nstatic int copy_file()\n{\n#define FIXED_BUFFEER_SIZE  (128 * 1024)\n    const char *log_prefix_name = NULL;\n\tint result;\n    int src_fd;\n    int dst_fd;\n    bool is_fcfs_input;\n    char fixed_buff[FIXED_BUFFEER_SIZE];\n    iovec_array_t src_buffers;\n    iovec_array_t dst_buffers;\n    char *buff;\n    char new_filename[PATH_MAX];\n    char new_fs_filename[PATH_MAX];\n    int filename_len;\n    int read_bytes;\n    int write_bytes;\n    int current_write;\n    int count;\n\n    if ((result=fcfs_posix_api_init(log_prefix_name,\n                    ns, config_filename)) != 0)\n    {\n        return result;\n    }\n    if ((result=fcfs_posix_api_start()) != 0) {\n        return result;\n    }\n\n    fcfs_posix_api_log_configs();\n\n    is_fcfs_input = FCFS_API_IS_MY_MOUNTPOINT(input_filename);\n    if (is_fcfs_input) {\n        src_fd = fcfs_open(input_filename, O_RDONLY);\n    } else {\n        src_fd = open(input_filename, O_RDONLY);\n    }\n    if (src_fd < 0) {\n        result = errno != 0 ? errno : ENOENT;\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"open file %s to read fail, \"\n                \"errno: %d, error info: %s\",\n                __LINE__, input_filename,\n                result, strerror(result));\n        return result;\n    }\n\n    if (fs_filename[strlen(fs_filename) - 1] == '/') {\n        const char *filename;\n        filename = strrchr(input_filename, '/');\n        if (filename != NULL) {\n            filename++;  //skip \"/\"\n        } else {\n            filename = input_filename;\n        }\n        filename_len = snprintf(new_filename, sizeof(new_filename),\n                \"%s%s\", fs_filename, filename);\n    } else {\n        filename_len = snprintf(new_filename, sizeof(new_filename),\n                \"%s%s\", (*fs_filename != '/' ? \"/\" : \"\"), fs_filename);\n    }\n\n    if (!(filename_len > g_fcfs_papi_global_vars.ctx.mountpoint.len &&\n                memcmp(new_filename, g_fcfs_papi_global_vars.ctx.mountpoint.str,\n                    g_fcfs_papi_global_vars.ctx.mountpoint.len) == 0))\n    {\n        snprintf(new_fs_filename, sizeof(new_fs_filename), \"%s%s\",\n                g_fcfs_papi_global_vars.ctx.mountpoint.str, new_filename);\n    } else {\n        snprintf(new_fs_filename, sizeof(new_fs_filename), \"%s\", new_filename);\n    }\n\n    if ((dst_fd=fcfs_open(new_fs_filename, O_CREAT | O_WRONLY, 0755)) < 0) {\n        result = errno != 0 ? errno : EIO;\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"open file %s fail, errno: %d, error info: %s\",\n                __LINE__, fs_filename, result, STRERROR(result));\n        return result;\n    }\n\n    logInfo(\"is_fcfs_input: %d, input fd: %d, output fd: %d\",\n            is_fcfs_input, src_fd, dst_fd);\n\n    if (buffer_size <= FIXED_BUFFEER_SIZE) {\n        buff = fixed_buff;\n    } else {\n        buff = (char *)fc_malloc(buffer_size);\n        if (buff == NULL) {\n            return ENOMEM;\n        }\n    }\n\n    if (use_iov) {\n        if ((result=alloc_iovec_array(&src_buffers, buff)) != 0) {\n            return result;\n        }\n\n        if ((result=alloc_iovec_array(&dst_buffers, buff)) != 0) {\n            return result;\n        }\n    }\n\n    count = 0;\n    write_bytes = 0;\n    while (1) {\n        if (is_fcfs_input) {\n            if (use_iov) {\n                read_bytes = fcfs_readv(src_fd, src_buffers.iovs,\n                        src_buffers.count);\n            } else {\n                read_bytes = fcfs_read(src_fd, buff, buffer_size);\n            }\n        } else {\n            if (use_iov) {\n                read_bytes = readv(src_fd, src_buffers.iovs,\n                        src_buffers.count);\n            } else {\n                read_bytes = read(src_fd, buff, buffer_size);\n            }\n        }\n        if (read_bytes == 0) {\n            break;\n        } else if (read_bytes < 0) {\n            result = errno != 0 ? errno : ENOENT;\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"read from file %s fail, \"\n                    \"errno: %d, error info: %s\",\n                    __LINE__, input_filename,\n                    result, strerror(result));\n            return result;\n        }\n\n        if (use_iov) {\n            int bytes;\n            int remain;\n            struct iovec *iob;\n            struct iovec *last;\n\n            if (read_bytes == buffer_size) {\n                dst_buffers.count = src_buffers.count;\n            } else {\n                iob = src_buffers.iovs;\n                bytes = iob->iov_len;\n                while (bytes < read_bytes) {\n                    ++iob;\n                    bytes += iob->iov_len;\n                }\n\n                dst_buffers.count = (iob - src_buffers.iovs + 1);\n                last = dst_buffers.iovs + (dst_buffers.count - 1);\n                remain = bytes - read_bytes;\n                last->iov_len -= remain;\n\n                logInfo(\"read_bytes: %d, bytes: %d, count: %d, last length: %d\",\n                        read_bytes, bytes, dst_buffers.count, (int)last->iov_len);\n            }\n\n            current_write = fcfs_writev(dst_fd, dst_buffers.iovs,\n                    dst_buffers.count);\n            if (read_bytes < buffer_size) {\n                /* restore the last iov */\n                (dst_buffers.iovs + (dst_buffers.count - 1))->iov_len =\n                    (src_buffers.iovs + (dst_buffers.count - 1))->iov_len;\n            }\n        } else {\n            current_write = fcfs_write(dst_fd, buff, read_bytes);\n        }\n        if (current_write != read_bytes) {\n            result = errno != 0 ? errno : EIO;\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"write to file %s fail, \"\n                    \"errno: %d, error info: %s\",\n                    __LINE__, fs_filename,\n                    result, STRERROR(result));\n            return result;\n        }\n\n        if (++count % 10 == 0) {\n            fcfs_fsync(dst_fd);\n        }\n\n        write_bytes += current_write;\n    }\n\n    if (is_fcfs_input) {\n        fcfs_close(src_fd);\n    } else {\n        close(src_fd);\n    }\n\n    fcfs_fsync(dst_fd);\n    fcfs_close(dst_fd);\n\n    fcfs_posix_api_stop();\n    return 0;\n}\n\nint main(int argc, char *argv[])\n{\n    int result;\n\tint ch;\n    int64_t bytes;\n\n    if (argc < 3) {\n        usage(argv);\n        return 1;\n    }\n\n    while ((ch=getopt(argc, argv, \"hc:n:b:s:V\")) != -1) {\n        switch (ch) {\n            case 'h':\n                usage(argv);\n                return 0;\n            case 'c':\n                config_filename = optarg;\n                break;\n            case 'n':\n                ns = optarg;\n                break;\n            case 'b':\n                if ((result=parse_bytes(optarg, 1, &bytes)) != 0) {\n                    usage(argv);\n                    return result;\n                }\n                if (bytes < 4 * 1024) {\n                    buffer_size = 4 * 1024;\n                } else {\n                    buffer_size = bytes;\n                }\n                break;\n            case 's':\n                if ((result=parse_bytes(optarg, 1, &bytes)) != 0) {\n                    usage(argv);\n                    return result;\n                }\n                if (bytes < 1 * 1024) {\n                    slice_size = 1 * 1024;\n                } else {\n                    slice_size = bytes;\n                }\n                break;\n            case 'V':\n                use_iov = true;\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    if (optind + 1 >= argc) {\n        usage(argv);\n        return 1;\n    }\n\n    log_try_init();\n    //g_log_context.log_level = LOG_DEBUG;\n\n    if (use_iov) {\n        logInfo(\"use writev & readv, slice size: %d\", slice_size);\n    }\n\n    input_filename = argv[optind];\n    fs_filename = argv[optind + 1];\n    if (strlen(fs_filename) == 0) {\n        usage(argv);\n        return EINVAL;\n    }\n\n    return copy_file();\n}\n"
  },
  {
    "path": "src/api/tests/fcfs_test_read_ahead.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"sf/sf_global.h\"\n#include \"fastcfs/api/fcfs_api.h\"\n\n#define FCFS_BLOCK_SIZE  512\n\ntypedef struct {\n    int count;\n    char **blocks;\n} BlockArray;\n\nstatic char *ns = \"test\";\nstatic int64_t file_size;\nstatic int64_t write_buffer_size;\nstatic int64_t read_buffer_size;\nstatic int read_thread_count;\nstatic int read_loop_count;\nstatic bool is_random_read;\nstatic bool is_random_write;\nstatic volatile int running_read_threads;\nstatic volatile int can_read = 0;\n\nstatic BlockArray barray;\nconst char *filename = \"/test.dat\";\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"Usage: %s [-c config_filename=%s] \"\n            \"-n [namespace=test]\\n\\t[-s file_size=64MB] \"\n            \"[-r read_buffer_size=4KB] [-w write_buffer_size=4KB]\\n\"\n            \"\\t[-t read_thread_count=1]\\n\\t[-R random read] \"\n            \"[-W random write] [-l read_loop_count=1]\\n\\n\", argv[0],\n            FCFS_FUSE_DEFAULT_CONFIG_FILENAME);\n}\n\nstatic int check_create_root_path()\n{\n    FDIRClientOperFnamePair path;\n    FDIRDEntryInfo dentry;\n    int64_t inode;\n    int result;\n\n    FC_SET_STRING(path.fullname.ns, ns);\n    FC_SET_STRING(path.fullname.path, \"/\");\n    FDIR_SET_OPERATOR(path.oper, geteuid(), getegid(), 0, NULL);\n    if ((result=fdir_client_lookup_inode_by_path_ex(g_fcfs_api_ctx.\n                    contexts.fdir, &path, LOG_DEBUG, &inode)) != 0)\n    {\n        if (result == ENOENT) {\n            result = fdir_client_create_dentry(g_fcfs_api_ctx.\n                    contexts.fdir, &path, 0777 | S_IFDIR, &dentry);\n        }\n    }\n\n    return result;\n}\n\nstatic int init()\n{\n    char *buff;\n    char *p;\n    char **pp;\n    char **end;\n    int count;\n    int bytes;\n\n    srand(time(NULL));\n    count = (int64_t)rand() * 256LL / RAND_MAX;\n    barray.count = 2;\n    while (barray.count < count) {\n        barray.count *= 2;\n    }\n\n    bytes = FCFS_BLOCK_SIZE * barray.count;\n    buff = (char *)fc_malloc(bytes);\n    if (buff == NULL) {\n        return ENOMEM;\n    }\n\n    barray.blocks = (char **)fc_malloc(sizeof(char *) * barray.count);\n    if (barray.blocks == NULL) {\n        return ENOMEM;\n    }\n\n    p = buff;\n    end = barray.blocks + barray.count;\n    for (pp=barray.blocks; pp<end; pp++) {\n        memset(p, pp - barray.blocks, FCFS_BLOCK_SIZE);\n        *pp = p;\n        p += FCFS_BLOCK_SIZE;\n    }\n    \n    printf(\"pid: %d, block count: %d\\n\", (int)getpid(), barray.count);\n    return 0;\n}\n\nstatic void fill_buffer(char *buff, const int length,\n        const int64_t offset)\n{\n    int64_t block_index;\n    int count;\n    int i;\n    char *block;\n    char *p;\n\n    block_index = offset / FCFS_BLOCK_SIZE;\n    count = length / FCFS_BLOCK_SIZE;\n    p = buff;\n    for (i=0; i<count; i++) {\n        block = barray.blocks[(block_index + i) % barray.count];\n        memcpy(p, block, FCFS_BLOCK_SIZE);\n        p += FCFS_BLOCK_SIZE;\n    }\n}\n\nstatic int sequential_write(FCFSAPIFileInfo *fi, char *out_buff)\n{\n    int length;\n    int write_bytes;\n    int result;\n    int64_t remain;\n\n    if ((result=fcfs_api_lseek(fi, 0, SEEK_SET)) != 0) {\n        return result;\n    }\n\n    remain = file_size;\n    while (remain > 0 && SF_G_CONTINUE_FLAG) {\n        fill_buffer(out_buff, write_buffer_size, file_size - remain);\n        length = FC_MIN(remain, write_buffer_size);\n        result = fcfs_api_write(fi, out_buff, length, &write_bytes);\n        if (result != 0) {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"write to file %s fail, offset: %\"PRId64\", length: %d, \"\n                    \"write_bytes: %d, errno: %d, error info: %s\",\n                    __LINE__, filename, file_size - remain, length,\n                    write_bytes, result, STRERROR(result));\n            return result;\n        }\n\n        remain -= write_bytes;\n    }\n\n    return 0;\n}\n\nstatic int random_write(FCFSAPIFileInfo *fi, char *out_buff)\n{\n    int result;\n    int write_bytes;\n    int i;\n    int64_t loop_count;\n    int64_t offset;\n\n    i = 0;\n    loop_count = (file_size / write_buffer_size) - 1;\n    while (i <= loop_count && SF_G_CONTINUE_FLAG) {\n        offset = ((int64_t)rand() * loop_count / RAND_MAX) *\n            write_buffer_size;\n        fill_buffer(out_buff, write_buffer_size, offset);\n        result = fcfs_api_pwrite(fi, out_buff, write_buffer_size,\n                offset, &write_bytes);\n        if (result != 0) {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"write to file %s fail, offset: %\"PRId64\", \"\n                    \"length: %\"PRId64\", write_bytes: %d, \"\n                    \"errno: %d, error info: %s\", __LINE__,\n                    filename, offset, write_buffer_size,\n                    write_bytes, result, STRERROR(result));\n            return result;\n        }\n        ++i;\n    }\n\n    return 0;\n}\n\nstatic inline int do_write(FCFSAPIFileInfo *fi, char *out_buff)\n{\n    if (is_random_write) {\n        return random_write(fi, out_buff);\n    } else {\n        return sequential_write(fi, out_buff);\n    }\n}\n\nstatic void *write_thread(void *arg)\n{\n    FCFSAPIFileContext fctx;\n    FCFSAPIFileInfo fi;\n    char *out_buff;\n    long thread_index;\n    int result;\n\n    thread_index = (long)arg;\n    fctx.tid = getpid() + thread_index;\n    fctx.mode = 0755;\n    FDIR_SET_OPERATOR(fctx.oper, geteuid(), getegid(), 0, NULL);\n    if ((result=fcfs_api_open(&fi, filename, O_CREAT |\n                    O_WRONLY | O_TRUNC, &fctx)) != 0)\n    {\n        __sync_bool_compare_and_swap(&can_read, 0, -1);\n        return NULL;\n    }\n\n    out_buff = (char *)fc_malloc(write_buffer_size);\n    if (out_buff == NULL) {\n        __sync_bool_compare_and_swap(&can_read, 0, -1);\n        fcfs_api_close(&fi);\n        return NULL;\n    }\n\n    do {\n        if (is_random_read || is_random_write) {\n            logInfo(\"file: \"__FILE__\", line: %d, \"\n                    \"prepare file for read ...\", __LINE__);\n            if (sequential_write(&fi, out_buff) != 0) {\n                __sync_bool_compare_and_swap(&can_read, 0, -1);\n                break;\n            }\n            logInfo(\"file: \"__FILE__\", line: %d, \"\n                    \"file ready for read.\", __LINE__);\n        }\n        __sync_bool_compare_and_swap(&can_read, 0, 1);\n\n        while (SF_G_CONTINUE_FLAG) {\n            if (do_write(&fi, out_buff) != 0) {\n                break;\n            }\n        }\n    } while (0);\n\n    fcfs_api_close(&fi);\n    free(out_buff);\n    return NULL;\n}\n\nstatic int do_read(const int thread_index, FCFSAPIFileInfo *fi,\n        char *in_buff, char *expect_buff, int *wait_count, int *retry_count)\n{\n    int length;\n    int read_bytes;\n    int result;\n    int fail_count;\n    int64_t loop_count;\n    int64_t i;\n    int64_t offset;\n    int64_t remain;\n\n    length = read_buffer_size;\n    loop_count = (file_size / read_buffer_size) - 1;\n    i = 0;\n    fail_count = 0;\n    remain = file_size;\n    while (i <= loop_count && SF_G_CONTINUE_FLAG) {\n        if (is_random_read) {\n            offset = ((int64_t)rand() * loop_count / RAND_MAX) *\n                read_buffer_size;\n        } else {\n            offset = file_size - remain;\n            length = FC_MIN(remain, read_buffer_size);\n        }\n\n        result = fcfs_api_pread(fi, in_buff, length,\n                offset, &read_bytes);\n        if (result != 0) {\n            logError(\"file: \"__FILE__\", line: %d, thread index: %d, \"\n                    \"read from file %s fail, offset: %\"PRId64\", length: %d, \"\n                    \"read_bytes: %d, errno: %d, error info: %s\", __LINE__,\n                    thread_index, filename, offset, length,\n                    read_bytes, result, STRERROR(result));\n            break;\n        }\n\n        if (read_bytes != length) {\n            ++(*wait_count);\n            fc_sleep_ms(1);\n            continue;\n        }\n\n        fill_buffer(expect_buff, read_buffer_size, offset);\n        result = memcmp(in_buff, expect_buff, length);\n        if (result != 0) {\n            if (fail_count++ < 10) {\n                fc_sleep_ms(1);\n                ++(*retry_count);\n                continue;\n            }\n\n            logError(\"file: \"__FILE__\", line: %d, thread index: %d, \"\n                    \"file offset: %\"PRId64\", read and expect buffer \"\n                    \"compare result: %d != 0\", __LINE__, thread_index,\n                    offset, result);\n            return result;\n        }\n\n        if (fail_count > 0) {\n            fail_count = 0;\n        }\n        remain -= read_bytes;\n        ++i;\n    }\n\n    return 0;\n}\n\nstatic int read_test(const int thread_index)\n{\n    FCFSAPIFileContext fctx;\n    FCFSAPIFileInfo fi;\n    char *expect_buff;\n    char *in_buff;\n    int wait_count;\n    int retry_count;\n    int i;\n    int result;\n\n    fctx.tid = getpid() + thread_index;\n    fctx.mode = 0755;\n    FDIR_SET_OPERATOR(fctx.oper, geteuid(), getegid(), 0, NULL);\n    if ((result=fcfs_api_open(&fi, filename, O_RDONLY, &fctx)) != 0) {\n        return result;\n    }\n\n    in_buff = (char *)fc_malloc(read_buffer_size * 2);\n    if (in_buff == NULL) {\n        fcfs_api_close(&fi);\n        return ENOMEM;\n    }\n    expect_buff = in_buff + read_buffer_size;\n\n    wait_count = retry_count = 0;\n    for (i=0; i<read_loop_count && SF_G_CONTINUE_FLAG; i++) {\n        if ((result=do_read(thread_index, &fi, in_buff, expect_buff,\n                        &wait_count, &retry_count)) != 0)\n        {\n            break;\n        }\n    }\n\n    fcfs_api_close(&fi);\n    free(in_buff);\n\n    if (wait_count > 0 || retry_count > 0) {\n        logInfo(\"file: \"__FILE__\", line: %d, \"\n                \"thread index: %d, wait file content ready count: %d, \"\n                \"retry count: %d\", __LINE__, thread_index, wait_count,\n                retry_count);\n    }\n\n    return result;\n}\n\nstatic void *read_thread(void *arg)\n{\n    int thread_index;\n\n    thread_index = (long)arg;\n    if (read_test(thread_index) == 0) {\n        logInfo(\"file: \"__FILE__\", line: %d,  thread index: %d, \"\n                \"read test successfully!\", __LINE__, thread_index);\n    }\n\n    FC_ATOMIC_DEC(read_thread_count);\n    return NULL;\n}\n\nstatic void sigQuitHandler(int sig)\n{\n    if (SF_G_CONTINUE_FLAG) {\n        SF_G_CONTINUE_FLAG = false;\n        logInfo(\"file: \"__FILE__\", line: %d, \"\n                \"catch signal %d, program exiting...\",\n                __LINE__, sig);\n    }\n}\n\nint main(int argc, char *argv[])\n{\n    const bool publish = false;\n    const char *config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME;\n\tint ch;\n    int i;\n\tint result;\n    int status;\n    struct sigaction act;\n    long thread_index;\n    pthread_t wtid;\n    pthread_t rtid;\n\n    is_random_read = is_random_write = false;\n    read_thread_count = 1;\n    read_loop_count = 1;\n    file_size = 64 * 1024 * 1024;\n    write_buffer_size = read_buffer_size = 4 * 1024;\n    while ((ch=getopt(argc, argv, \"hc:n:s:w:r:t:l:RW\")) != -1) {\n        switch (ch) {\n            case 'h':\n                usage(argv);\n                return 0;\n            case 'c':\n                config_filename = optarg;\n                break;\n            case 'n':\n                ns = optarg;\n                break;\n            case 's':\n                if ((result=parse_bytes(optarg, 1, &file_size)) != 0) {\n                    return EINVAL;\n                }\n                if (file_size < 1024 * 1024) {\n                    fprintf(stderr, \"file size: %\"PRId64\" \"\n                            \"is too small < 1MB\", file_size);\n                    return EINVAL;\n                }\n                file_size = MEM_ALIGN_CEIL(file_size, 1024 * 1024);\n                break;\n            case 'w':\n                if ((result=parse_bytes(optarg, 1, &write_buffer_size)) != 0) {\n                    return EINVAL;\n                }\n                write_buffer_size = MEM_ALIGN_CEIL(\n                        write_buffer_size, 1024);\n                break;\n            case 'r':\n                if ((result=parse_bytes(optarg, 1, &read_buffer_size)) != 0) {\n                    return EINVAL;\n                }\n                read_buffer_size = MEM_ALIGN_CEIL(\n                        read_buffer_size, 1024);\n                break;\n            case 't':\n                read_thread_count = strtol(optarg, NULL, 10);\n                break;\n            case 'l':\n                read_loop_count = strtol(optarg, NULL, 10);\n                break;\n            case 'R':\n                is_random_read = true;\n                break;\n            case 'W':\n                is_random_write = true;\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    log_init();\n    //g_log_context.log_level = LOG_DEBUG;\n   \n    if ((result=init()) != 0) {\n        return result;\n    }\n\n    if ((result=fcfs_api_pooled_init_with_auth(ns,\n                    config_filename, publish)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=check_create_root_path()) != 0) {\n        return result;\n    }\n\n    if ((result=fcfs_api_start()) != 0) {\n        return result;\n    }\n\n    memset(&act, 0, sizeof(act));\n    sigemptyset(&act.sa_mask);\n    act.sa_handler = sigQuitHandler;\n    if(sigaction(SIGINT, &act, NULL) < 0 ||\n        sigaction(SIGTERM, &act, NULL) < 0 ||\n        sigaction(SIGQUIT, &act, NULL) < 0)\n    {\n        logCrit(\"file: \"__FILE__\", line: %d, \"\n                \"call sigaction fail, errno: %d, error info: %s\",\n                __LINE__, errno, strerror(errno));\n        logCrit(\"exit abnormally!\\n\");\n        return errno;\n    }\n\n    thread_index = 1;\n    if ((result=pthread_create(&wtid, NULL, write_thread,\n                    (void *)(thread_index++))) != 0)\n    {\n        return result;\n    }\n\n    while ((status=FC_ATOMIC_GET(can_read)) == 0) {\n        fc_sleep_ms(10);\n    }\n\n    if (status < 0) {\n        return EIO;\n    }\n\n    running_read_threads = read_thread_count;\n    for (i=0; i<read_thread_count; i++) {\n        if ((result=pthread_create(&rtid, NULL, read_thread,\n                        (void *)(thread_index++))) != 0)\n        {\n            return result;\n        }\n    }\n\n    while (FC_ATOMIC_GET(read_thread_count) != 0) {\n        fc_sleep_ms(10);\n    }\n    SF_G_CONTINUE_FLAG = false;\n    fc_sleep_ms(100);\n\n    fcfs_api_terminate();\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/client/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I../common\nLIB_PATH = $(LIBS) -lfastcommon -lserverframe\nTARGET_LIB = $(TARGET_PREFIX)/$(LIB_VERSION)\n\nFAST_SHARED_OBJS = ../common/auth_global.lo ../common/auth_proto.lo \\\n                   ../common/auth_func.lo  ../common/server_session.lo \\\n                   client_func.lo client_global.lo client_proto.lo \\\n                   fcfs_auth_client.lo simple_connection_manager.lo \\\n                   session_sync.lo session_regenerate.lo \\\n                   fcfs_auth_for_server.lo\n\nFAST_STATIC_OBJS = ../common/auth_global.o ../common/auth_proto.o \\\n                   ../common/auth_func.o ../common/server_session.o \\\n                   client_func.o client_global.o client_proto.o \\\n                   fcfs_auth_client.o  simple_connection_manager.o \\\n                   session_sync.o session_regenerate.o \\\n                   fcfs_auth_for_server.o\n\nHEADER_FILES = ../common/auth_global.h ../common/auth_types.h \\\n               ../common/auth_proto.h ../common/auth_func.h \\\n               ../common/server_session.h \\\n               fcfs_auth_client.h client_types.h client_func.h \\\n               client_global.h client_proto.h simple_connection_manager.h \\\n               session_sync.h session_regenerate.h fcfs_auth_for_server.h\n\nALL_OBJS = $(FAST_STATIC_OBJS) $(FAST_SHARED_OBJS)\n\nALL_PRGS = \nSHARED_LIBS = libfcfsauthclient.so\nSTATIC_LIBS = libfcfsauthclient.a\nALL_LIBS = $(SHARED_LIBS) $(STATIC_LIBS)\n\nall: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)\nlibfcfsauthclient.so: $(FAST_SHARED_OBJS)\n\t$(COMPILE) -o $@ -shared $(FAST_SHARED_OBJS) $(LIB_PATH)\nlibfcfsauthclient.a: $(FAST_STATIC_OBJS)\n\tar rcs $@ $(FAST_STATIC_OBJS)\n.o:\n\t$(COMPILE) -o $@ $<  $(FAST_STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c:\n\t$(COMPILE) -o $@ $<  $(FAST_STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n.c.lo:\n\t$(COMPILE) -c -fPIC -o $@ $<  $(INC_PATH)\ninstall:\n\tmkdir -p $(TARGET_LIB)\n\tmkdir -p $(TARGET_PREFIX)/lib\n\tmkdir -p $(TARGET_PREFIX)/include/fastcfs/auth\n\n\tinstall -m 755 $(SHARED_LIBS) $(TARGET_LIB)\n\tinstall -m 644 $(HEADER_FILES) $(TARGET_PREFIX)/include/fastcfs/auth\n\t@BUILDROOT=$$(echo \"$(TARGET_PREFIX)\" | grep BUILDROOT); \\\n\tif [ -z \"$$BUILDROOT\" ] && [ \"$(TARGET_LIB)\" != \"$(TARGET_PREFIX)/lib\" ]; then ln -sf $(TARGET_LIB)/libfcfsauthclient.so $(TARGET_PREFIX)/lib/libfcfsauthclient.so; fi\nclean:\n\trm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)\n\n"
  },
  {
    "path": "src/auth/client/client_func.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/ini_file_reader.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"client_global.h\"\n#include \"simple_connection_manager.h\"\n#include \"client_func.h\"\n\nint fcfs_auth_alloc_group_servers(FCFSAuthServerGroup *server_group,\n        const int alloc_size)\n{\n    int bytes;\n\n    bytes = sizeof(ConnectionInfo) * alloc_size;\n    server_group->servers = (ConnectionInfo *)fc_malloc(bytes);\n    if (server_group->servers == NULL) {\n        return errno != 0 ? errno : ENOMEM;\n    }\n    memset(server_group->servers, 0, bytes);\n\n    server_group->alloc_size = alloc_size;\n    server_group->count = 0;\n    return 0;\n}\n\nstatic int fcfs_auth_load_server_config(FCFSAuthClientContext *client_ctx,\n        IniFullContext *ini_ctx)\n{\n    int result;\n\n    if ((result=sf_load_cluster_config(&client_ctx->cluster, ini_ctx,\n                    FCFS_AUTH_DEFAULT_CLUSTER_PORT)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nstatic int fcfs_auth_client_do_init_ex(FCFSAuthClientContext *client_ctx,\n        IniFullContext *ini_ctx)\n{\n    int result;\n\n    client_ctx->common_cfg.connect_timeout = iniGetIntValueEx(\n            ini_ctx->section_name, \"connect_timeout\",\n            ini_ctx->context, SF_DEFAULT_CONNECT_TIMEOUT, true);\n    if (client_ctx->common_cfg.connect_timeout <= 0) {\n        client_ctx->common_cfg.connect_timeout = SF_DEFAULT_CONNECT_TIMEOUT;\n    }\n\n    client_ctx->common_cfg.network_timeout = iniGetIntValueEx(\n            ini_ctx->section_name, \"network_timeout\",\n            ini_ctx->context, SF_DEFAULT_NETWORK_TIMEOUT, true);\n    if (client_ctx->common_cfg.network_timeout <= 0) {\n        client_ctx->common_cfg.network_timeout = SF_DEFAULT_NETWORK_TIMEOUT;\n    }\n    if ((result=fcfs_auth_load_server_config(client_ctx, ini_ctx)) != 0) {\n        return result;\n    }\n\n    if ((result=sf_load_net_retry_config(&client_ctx->\n                    common_cfg.net_retry_cfg, ini_ctx)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nvoid fcfs_auth_client_log_config_ex(FCFSAuthClientContext *client_ctx,\n        const char *extra_config)\n{\n    char net_retry_output[256];\n\n    sf_net_retry_config_to_string(&client_ctx->common_cfg.net_retry_cfg,\n            net_retry_output, sizeof(net_retry_output));\n    logInfo(\"fauth v%d.%d.%d, \"\n            \"connect_timeout=%d, \"\n            \"network_timeout=%d, \"\n            \"%s, auth_server_count=%d%s%s\",\n            g_fcfs_auth_global_vars.version.major,\n            g_fcfs_auth_global_vars.version.minor,\n            g_fcfs_auth_global_vars.version.patch,\n            client_ctx->common_cfg.connect_timeout,\n            client_ctx->common_cfg.network_timeout,\n            net_retry_output, FC_SID_SERVER_COUNT(client_ctx->cluster.\n                server_cfg), (extra_config != NULL ? \", \" : \"\"),\n            (extra_config != NULL ? extra_config : \"\"));\n}\n\nint fcfs_auth_client_load_from_file_ex1(FCFSAuthClientContext *client_ctx,\n        IniFullContext *ini_ctx)\n{\n    IniContext iniContext;\n    int result;\n\n    if (ini_ctx->context == NULL) {\n        if ((result=iniLoadFromFile(ini_ctx->filename, &iniContext)) != 0) {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                    __LINE__, ini_ctx->filename, result);\n            return result;\n        }\n        ini_ctx->context = &iniContext;\n    }\n\n    if ((result=fcfs_auth_client_do_init_ex(client_ctx, ini_ctx)) == 0) {\n        client_ctx->inited = true;\n    }\n\n    if (ini_ctx->context == &iniContext) {\n        iniFreeContext(&iniContext);\n        ini_ctx->context = NULL;\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_init_ex2(FCFSAuthClientContext *client_ctx,\n        IniFullContext *ini_ctx, const SFServerGroupIndexType index_type)\n{\n    int result;\n    int server_group_index;\n\n    if ((result=fcfs_auth_client_load_from_file_ex1(\n                    client_ctx, ini_ctx)) != 0)\n    {\n        return result;\n    }\n\n    server_group_index = (index_type == sf_server_group_index_type_cluster ?\n            client_ctx->cluster.cluster_group_index :\n            client_ctx->cluster.service_group_index);\n    if ((result=fcfs_auth_simple_connection_manager_init(client_ctx,\n                    &client_ctx->cm, server_group_index)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nvoid fcfs_auth_client_destroy_ex(FCFSAuthClientContext *client_ctx)\n{\n    fc_server_destroy(&client_ctx->cluster.server_cfg);\n    fcfs_auth_simple_connection_manager_destroy(&client_ctx->cm);\n    memset(client_ctx, 0, sizeof(FCFSAuthClientContext));\n}\n"
  },
  {
    "path": "src/auth/client/client_func.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_CLIENT_FUNC_H\n#define _FCFS_AUTH_CLIENT_FUNC_H\n\n#include \"auth_global.h\"\n#include \"client_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define fcfs_auth_client_load_from_file(filename) \\\n    fcfs_auth_client_load_from_file_ex((&g_fcfs_auth_client_vars.client_ctx), \\\n            filename, NULL)\n\n#define fcfs_auth_client_init_ex(filename, index_type)  \\\n    fcfs_auth_client_init_ex1((&g_fcfs_auth_client_vars.client_ctx),  \\\n            filename, NULL, index_type)\n\n#define fcfs_auth_client_init(filename)  \\\n    fcfs_auth_client_init_ex(filename,   \\\n            sf_server_group_index_type_service)\n\n#define fcfs_auth_client_destroy() \\\n    fcfs_auth_client_destroy_ex((&g_fcfs_auth_client_vars.client_ctx))\n\n#define fcfs_auth_client_log_config(client_ctx) \\\n    fcfs_auth_client_log_config_ex(client_ctx, NULL)\n\nint fcfs_auth_client_load_from_file_ex1(FCFSAuthClientContext *client_ctx,\n        IniFullContext *ini_ctx);\n\n/**\n* client initial from config file\n* params:\n*       client_ctx: the client context\n*       config_filename: the client config filename\n*       section_name: the section name, NULL or empty for global section\n* return: 0 success, != 0 fail, return the error code\n**/\nstatic inline int fcfs_auth_client_load_from_file_ex(FCFSAuthClientContext\n        *client_ctx, const char *config_filename, const char *section_name)\n{\n    IniFullContext ini_ctx;\n\n    FAST_INI_SET_FULL_CTX(ini_ctx, config_filename, section_name);\n    return fcfs_auth_client_load_from_file_ex1(client_ctx, &ini_ctx);\n}\n\nint fcfs_auth_client_init_ex2(FCFSAuthClientContext *client_ctx,\n        IniFullContext *ini_ctx, const SFServerGroupIndexType index_type);\n\nstatic inline int fcfs_auth_client_init_ex1(FCFSAuthClientContext *client_ctx,\n        const char *config_filename, const char *section_name,\n        const SFServerGroupIndexType index_type)\n{\n    IniFullContext ini_ctx;\n\n    FAST_INI_SET_FULL_CTX(ini_ctx, config_filename, section_name);\n    return fcfs_auth_client_init_ex2(client_ctx, &ini_ctx, index_type);\n}\n\n/**\n* client destroy function\n* params:\n*       client_ctx: tracker group\n* return: none\n**/\nvoid fcfs_auth_client_destroy_ex(FCFSAuthClientContext *client_ctx);\n\nint fcfs_auth_alloc_group_servers(FCFSAuthServerGroup *server_group,\n        const int alloc_size);\n\nvoid fcfs_auth_client_log_config_ex(FCFSAuthClientContext *client_ctx,\n        const char *extra_config);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/client/client_global.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"client_global.h\"\n\nFCFSAuthClientGlobalVars g_fcfs_auth_client_vars = {\n    true, false, {false}\n};\n"
  },
  {
    "path": "src/auth/client/client_global.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_CLIENT_GLOBAL_H\n#define _FCFS_AUTH_CLIENT_GLOBAL_H\n\n#include \"auth_global.h\"\n#include \"client_types.h\"\n\ntypedef struct fcfs_auth_client_global_vars {\n    bool need_load_passwd;   //false for auth server\n    bool ignore_when_passwd_not_exist; //true for fdir and fstore servers\n    FCFSAuthClientContext client_ctx;\n} FCFSAuthClientGlobalVars;\n\n#define fcfs_auth_client_init_full_ctx_ex(auth, client_ctx) \\\n    (auth)->ctx = client_ctx\n\n#define fcfs_auth_client_init_full_ctx(auth) \\\n    fcfs_auth_client_init_full_ctx_ex(auth, &g_fcfs_auth_client_vars.client_ctx)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern FCFSAuthClientGlobalVars g_fcfs_auth_client_vars;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/client/client_proto.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/ini_file_reader.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/connection_pool.h\"\n#include \"auth_proto.h\"\n#include \"auth_func.h\"\n#include \"client_global.h\"\n#include \"client_proto.h\"\n\n#define check_name(name, caption) check_name_ex(name, caption, true)\n\n#define check_username_ex(username, required) \\\n    check_name_ex(username, \"username\", required)\n#define check_username(username) check_username_ex(username, true)\n\n#define pack_username_ex(username, proto_uname, required) \\\n    pack_name_ex(username, \"username\", proto_uname, 0, required)\n#define pack_username(username, proto_uname) \\\n     pack_username_ex(username, proto_uname, true)\n\n#define check_poolname_ex(poolname, required) \\\n    check_name_ex(poolname, \"poolname\", required)\n#define check_poolname(poolname) check_poolname_ex(poolname, true)\n\n#define pack_poolname_ex(poolname, proto_pname, str_offset, required) \\\n    pack_name_ex(poolname, \"poolname\", proto_pname, str_offset, required)\n#define pack_poolname(poolname, proto_pname) \\\n    pack_poolname_ex(poolname, proto_pname, 0, true)\n\n#define pack_user_pool_pair(username, poolname, up_pair) \\\n    pack_user_pool_pair_ex(username, poolname, up_pair, true)\n\nstatic inline int check_name_ex(const string_t *name,\n        const char *caption, const bool required)\n{\n    int min_length;\n\n    min_length = required ? 1 : 0;\n    if (name->len < min_length || name->len > NAME_MAX) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"invalid %s length: %d, which <= 0 or > %d\",\n                __LINE__, caption, name->len, NAME_MAX);\n        return EINVAL;\n    }\n\n    return 0;\n}\n\nstatic inline int pack_name_ex(const string_t *name, const char *caption,\n        FCFSAuthProtoNameInfo *proto_name, const int str_offset,\n        const bool required)\n{\n    int result;\n\n    if ((result=check_name_ex(name, caption, required)) != 0) {\n        return result;\n    }\n    if (name->len > 0) {\n        memcpy(proto_name->str + str_offset, name->str, name->len);\n    }\n    proto_name->len = name->len;\n    return 0;\n}\n\nstatic inline int pack_user_pool_pair_ex(const string_t *username,\n        const string_t *poolname, FCFSAuthProtoUserPoolPair *up_pair,\n        const bool required)\n{\n    int result;\n\n    if ((result=check_username_ex(username, required)) != 0) {\n        return result;\n    }\n\n    if ((result=check_poolname_ex(poolname, required)) != 0) {\n        return result;\n    }\n\n    fcfs_auth_pack_user_pool_pair(username, poolname, up_pair);\n    return 0;\n}\n\nstatic inline int pack_user_passwd_pair(const string_t *username,\n        const string_t *passwd, FCFSAuthProtoUserPasswdPair *up_pair)\n{\n    if (passwd->len != FCFS_AUTH_PASSWD_LEN) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"invalid password length: %d != %d\",\n                __LINE__, passwd->len, FCFS_AUTH_PASSWD_LEN);\n        return EINVAL;\n    }\n    memcpy(up_pair->passwd, passwd->str, passwd->len);\n    return pack_username(username, &up_pair->username);\n}\n\nint fcfs_auth_client_proto_user_login(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const string_t *passwd, const string_t *poolname,\n        const int flags)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoUserLoginReq *req;\n    FCFSAuthProtoNameInfo *proto_pname;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoUserLoginReq) +\n        2 * NAME_MAX + FCFS_AUTH_PASSWD_LEN];\n    SFResponseInfo response;\n    FCFSAuthProtoUserLoginResp login_resp;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoUserLoginReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) +\n        username->len + poolname->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    req->flags = flags;\n    if ((result=pack_user_passwd_pair(username,\n                    passwd, &req->up_pair)) != 0)\n    {\n        return result;\n    }\n\n    proto_pname = (FCFSAuthProtoNameInfo *)(req->\n            up_pair.username.str + username->len);\n    if ((result=pack_poolname_ex(poolname, proto_pname, 0, false)) != 0) {\n        return result;\n    }\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_RESP,\n                    (char *)&login_resp, sizeof(login_resp))) == 0)\n    {\n        memcpy(client_ctx->session.id, login_resp.session_id,\n                FCFS_AUTH_SESSION_ID_LEN);\n    } else {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_proto_session_subscribe(\n        FCFSAuthClientContext *client_ctx, ConnectionInfo *conn)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoSessionSubscribeReq *req;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSessionSubscribeReq) +\n        NAME_MAX + FCFS_AUTH_PASSWD_LEN];\n    SFResponseInfo response;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoSessionSubscribeReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) +\n        client_ctx->auth_cfg.username.len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    if ((result=pack_user_passwd_pair(&client_ctx->auth_cfg.username,\n                    &client_ctx->auth_cfg.passwd, &req->up_pair)) != 0)\n    {\n        return result;\n    }\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_proto_session_validate(\n        FCFSAuthClientContext *client_ctx, ConnectionInfo *conn,\n        const string_t *session_id, const string_t *validate_key,\n        const FCFSAuthValidatePriviledgeType priv_type,\n        const int64_t pool_id, const int64_t priv_required)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoSessionValidateReq *req;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSessionValidateReq) +\n        NAME_MAX + FCFS_AUTH_PASSWD_LEN];\n    FCFSAuthProtoSessionValidateResp validate_resp;\n    SFResponseInfo response;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoSessionValidateReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req);\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    if (validate_key->len != FCFS_AUTH_PASSWD_LEN) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"invalid session validate key length: %d != %d\",\n                __LINE__, validate_key->len, FCFS_AUTH_PASSWD_LEN);\n        return EINVAL;\n    }\n    memcpy(req->validate_key, validate_key->str, validate_key->len);\n\n    if (session_id->len != FCFS_AUTH_SESSION_ID_LEN) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"invalid session id length: %d != %d\", __LINE__,\n                session_id->len, FCFS_AUTH_SESSION_ID_LEN);\n        return EINVAL;\n    }\n    memcpy(req->session_id, session_id->str, session_id->len);\n    req->priv_type = priv_type;\n    long2buff(pool_id, req->pool_id);\n    long2buff(priv_required, req->priv_required);\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_RESP,\n                    (char *)&validate_resp, sizeof(validate_resp))) == 0)\n    {\n        result = buff2int(validate_resp.result);\n    } else {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_proto_user_create(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const FCFSAuthUserInfo *user)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoUserCreateReq *req;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoUserCreateReq) +\n        NAME_MAX + FCFS_AUTH_PASSWD_LEN];\n    SFResponseInfo response;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoUserCreateReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + user->name.len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    if ((result=pack_user_passwd_pair(&user->name,\n                    &user->passwd, &req->up_pair)) != 0)\n    {\n        return result;\n    }\n    long2buff(user->priv, req->priv);\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_USER_CREATE_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_proto_user_passwd(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const string_t *passwd)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoUserPasswdReq *req;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoUserPasswdReq) +\n        NAME_MAX + FCFS_AUTH_PASSWD_LEN];\n    SFResponseInfo response;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoUserPasswdReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + username->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    if ((result=pack_user_passwd_pair(username, passwd,\n                    &req->up_pair)) != 0)\n    {\n        return result;\n    }\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\ntypedef int (*client_proto_list_func)(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const string_t *poolname, const SFListLimitInfo *limit,\n        SFProtoRecvBuffer *buffer, struct fast_mpool_man *mpool,\n        void *array, bool *is_last);\n\nstatic int client_proto_user_list_do(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const string_t *poolname, const SFListLimitInfo *limit,\n        SFProtoRecvBuffer *buffer, struct fast_mpool_man *mpool,\n        FCFSAuthUserArray *array, bool *is_last)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoUserListReq *req;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoUserListReq) + NAME_MAX];\n    SFResponseInfo response;\n    FCFSAuthProtoListRespHeader *resp_header;\n    FCFSAuthProtoUserListRespBodyPart *body_part;\n    FCFSAuthUserInfo *user;\n    FCFSAuthUserInfo *end;\n    char *p;\n    int out_bytes;\n    int count;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoUserListReq *)(header + 1);\n    if ((result=pack_username_ex(username, &req->username, false)) != 0) {\n        return result;\n    }\n\n    out_bytes = sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoUserListReq) + username->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_LIST_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    sf_proto_pack_limit(limit, &req->limit);\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_vary_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_USER_LIST_RESP, buffer,\n                    sizeof(FCFSAuthProtoListRespHeader))) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n        return result;\n    }\n\n    resp_header = (FCFSAuthProtoListRespHeader *)buffer->buff;\n    count = buff2int(resp_header->count);\n    *is_last = resp_header->is_last;\n    if ((result=fcfs_auth_user_check_realloc_array(array,\n                    array->count + count)) != 0)\n    {\n        return result;\n    }\n\n    p = (char *)(resp_header + 1);\n    end = array->users + array->count + count;\n    for (user=array->users+array->count; user<end; user++) {\n        body_part = (FCFSAuthProtoUserListRespBodyPart *)p;\n        user->priv = buff2long(body_part->priv);\n        if ((result=fast_mpool_alloc_string_ex(mpool,\n                        &user->name, body_part->username.str,\n                        body_part->username.len)) != 0)\n        {\n            return result;\n        }\n        p += sizeof(FCFSAuthProtoUserListRespBodyPart) + user->name.len;\n    }\n\n    if (response.header.body_len != p - buffer->buff) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"response body length: %d != expect: %d\",\n                __LINE__, response.header.body_len,\n                (int)(p - buffer->buff));\n        return EINVAL;\n    }\n\n    array->count += count;\n    return result;\n}\n\nstatic int client_proto_list_wrapper(client_proto_list_func list_func,\n        FCFSAuthClientContext *client_ctx, ConnectionInfo *conn,\n        const string_t *username, const string_t *poolname,\n        const SFListLimitInfo *limit, struct fast_mpool_man *mpool,\n        void *array, int *count)\n{\n    int result;\n    bool is_last;\n    SFProtoRBufferFixedWrapper buffer_wrapper;\n    SFListLimitInfo new_limit;\n\n    sf_init_recv_buffer_by_wrapper(&buffer_wrapper);\n    if (limit->count > 0 && limit->count <= 64) {\n        result = list_func(client_ctx, conn,\n                username, poolname, limit, &buffer_wrapper.buffer,\n                mpool, array, &is_last);\n        sf_free_recv_buffer(&buffer_wrapper.buffer);\n        return result;\n    }\n\n    result = 0;\n    is_last = false;\n    while (!is_last) {\n        new_limit.offset = limit->offset + *count;\n        if (limit->count <= 0) {\n            new_limit.count = 0;\n        } else {\n            new_limit.count = limit->count - *count;\n            if (new_limit.count == 0) {\n                break;\n            }\n        }\n\n        if ((result=list_func(client_ctx, conn, username,\n                        poolname, &new_limit, &buffer_wrapper.buffer,\n                        mpool, array, &is_last)) != 0)\n        {\n            break;\n        }\n    }\n\n    sf_free_recv_buffer(&buffer_wrapper.buffer);\n    return result;\n}\n\nint fcfs_auth_client_proto_user_list(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const SFListLimitInfo *limit, struct fast_mpool_man *mpool,\n        FCFSAuthUserArray *array)\n{\n    const string_t *poolname = NULL;\n    return client_proto_list_wrapper((client_proto_list_func)\n            client_proto_user_list_do, client_ctx, conn, username,\n            poolname, limit, mpool, array, &array->count);\n}\n\nint fcfs_auth_client_proto_user_grant(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username, const int64_t priv)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoUserGrantReq *req;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoUserGrantReq) + NAME_MAX];\n    SFResponseInfo response;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoUserGrantReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + username->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_GRANT_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    if ((result=pack_username(username, &req->username)) != 0) {\n        return result;\n    }\n    long2buff(priv, req->priv);\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_USER_GRANT_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_proto_user_remove(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoUserRemoveReq *req;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoUserRemoveReq) + NAME_MAX];\n    SFResponseInfo response;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoUserRemoveReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + username->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    if ((result=pack_username(username, &req->username)) != 0) {\n        return result;\n    }\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_proto_spool_create(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, FCFSAuthStoragePoolInfo *spool,\n        const int name_size, const bool dryrun)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoSPoolCreateReq *req;\n    FCFSAuthProtoSPoolCreateResp *resp;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSPoolCreateReq) + NAME_MAX];\n    char in_buff[sizeof(FCFSAuthProtoSPoolCreateResp) + NAME_MAX];\n    SFResponseInfo response;\n    int out_bytes;\n    int body_len;\n    int buff_size;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoSPoolCreateReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + spool->name.len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    if ((result=pack_poolname_ex(&spool->name,\n                    &req->poolname, 0, false)) != 0)\n    {\n        return result;\n    }\n    long2buff(spool->quota, req->quota);\n    req->dryrun = (dryrun ? 1 : 0);\n\n    if (name_size < sizeof(in_buff) - sizeof(*resp)) {\n        buff_size = name_size;\n    } else {\n        buff_size = sizeof(in_buff);\n    }\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_response_ex1(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_RESP, in_buff,\n                    buff_size, &body_len)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n        return result;\n    }\n\n    resp = (FCFSAuthProtoSPoolCreateResp *)in_buff;\n    if (body_len != sizeof(*resp) + resp->poolname.len) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"response body length: %d != expect: %d\", __LINE__,\n                body_len, (int)sizeof(*resp) + resp->poolname.len);\n        return EINVAL;\n    }\n\n    spool->name.len = resp->poolname.len;\n    memcpy(spool->name.str, resp->poolname.str, spool->name.len);\n    return 0;\n}\n\nint client_proto_spool_list_do(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const string_t *poolname, const SFListLimitInfo *limit,\n        SFProtoRecvBuffer *buffer, struct fast_mpool_man *mpool,\n        FCFSAuthStoragePoolArray *array, bool *is_last)\n{\n    FCFSAuthProtoHeader *header;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSPoolListReq) + 2 * NAME_MAX];\n    SFResponseInfo response;\n    FCFSAuthProtoSPoolListReq *req;\n    FCFSAuthProtoListRespHeader *resp_header;\n    FCFSAuthProtoSPoolListRespBodyPart *body_part;\n    FCFSAuthStoragePoolInfo *spool;\n    FCFSAuthStoragePoolInfo *end;\n    char *p;\n    int out_bytes;\n    int count;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    out_bytes = sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSPoolListReq) +\n        username->len + poolname->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n\n    req = (FCFSAuthProtoSPoolListReq *)(header + 1);\n    if ((result=pack_user_pool_pair_ex(username, poolname,\n                    &req->up_pair, false)) != 0)\n    {\n        return result;\n    }\n    sf_proto_pack_limit(limit, &req->limit);\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_vary_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_RESP, buffer,\n                    sizeof(FCFSAuthProtoListRespHeader))) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n        return result;\n    }\n\n    resp_header = (FCFSAuthProtoListRespHeader *)buffer->buff;\n    count = buff2int(resp_header->count);\n    *is_last = resp_header->is_last;\n    if ((result=fcfs_auth_spool_check_realloc_array(array,\n                    array->count + count)) != 0)\n    {\n        return result;\n    }\n\n    p = (char *)(resp_header + 1);\n    end = array->spools + array->count + count;\n    for (spool=array->spools+array->count; spool<end; spool++) {\n        body_part = (FCFSAuthProtoSPoolListRespBodyPart *)p;\n        spool->quota = buff2long(body_part->quota);\n        if ((result=fast_mpool_alloc_string_ex(mpool,\n                        &spool->name, body_part->poolname.str,\n                        body_part->poolname.len)) != 0)\n        {\n            return result;\n        }\n        p += sizeof(FCFSAuthProtoSPoolListRespBodyPart) + spool->name.len;\n    }\n\n    if (response.header.body_len != p - buffer->buff) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"response body length: %d != expect: %d\",\n                __LINE__, response.header.body_len,\n                (int)(p - buffer->buff));\n        return EINVAL;\n    }\n\n    array->count += count;\n    return result;\n}\n\nint fcfs_auth_client_proto_spool_list(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const string_t *poolname, const SFListLimitInfo *limit,\n        struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *array)\n{\n    return client_proto_list_wrapper((client_proto_list_func)\n            client_proto_spool_list_do, client_ctx, conn, username,\n            poolname, limit, mpool, array, &array->count);\n}\n\nint fcfs_auth_client_proto_spool_remove(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *poolname)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoSPoolRemoveReq *req;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSPoolRemoveReq) + NAME_MAX];\n    SFResponseInfo response;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoSPoolRemoveReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + poolname->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    if ((result=pack_poolname(poolname, &req->poolname)) != 0) {\n        return result;\n    }\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_proto_spool_set_quota(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *poolname, const int64_t quota)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoSPoolSetQuotaReq *req;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSPoolSetQuotaReq) + NAME_MAX];\n    SFResponseInfo response;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoSPoolSetQuotaReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + poolname->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    if ((result=pack_poolname(poolname, &req->poolname)) != 0) {\n        return result;\n    }\n    long2buff(quota, req->quota);\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_proto_spool_get_quota(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *poolname, int64_t *quota)\n{\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoSPoolGetQuotaReq *req;\n    FCFSAuthProtoSPoolGetQuotaResp resp;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSPoolGetQuotaReq) + NAME_MAX];\n    SFResponseInfo response;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    req = (FCFSAuthProtoSPoolGetQuotaReq *)(header + 1);\n    out_bytes = sizeof(FCFSAuthProtoHeader) + sizeof(*req) + poolname->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n    if ((result=pack_poolname(poolname, &req->poolname)) != 0) {\n        return result;\n    }\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_RESP,\n                    (char *)&resp, sizeof(resp))) == 0)\n    {\n        *quota = buff2long(resp.quota);\n    } else {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_proto_gpool_grant(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username, const string_t\n        *poolname, const FCFSAuthSPoolPriviledges *privs)\n{\n    FCFSAuthProtoHeader *header;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSPoolGrantReq) + 2 * NAME_MAX];\n    SFResponseInfo response;\n    FCFSAuthProtoSPoolGrantReq *req;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    out_bytes = sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSPoolGrantReq) +\n        username->len + poolname->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n\n    req = (FCFSAuthProtoSPoolGrantReq *)(header + 1);\n    if ((result=pack_user_pool_pair(username, poolname,\n                    &req->up_pair)) != 0)\n    {\n        return result;\n    }\n    int2buff(privs->fdir, req->privs.fdir);\n    int2buff(privs->fstore, req->privs.fstore);\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nint fcfs_auth_client_proto_gpool_withdraw(FCFSAuthClientContext\n        *client_ctx, ConnectionInfo *conn, const string_t *username,\n        const string_t *poolname)\n{\n    FCFSAuthProtoHeader *header;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSPoolWithdrawReq) + 2 * NAME_MAX];\n    SFResponseInfo response;\n    FCFSAuthProtoSPoolWithdrawReq *req;\n    int out_bytes;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    out_bytes = sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoSPoolWithdrawReq) +\n        username->len + poolname->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n\n    req = (FCFSAuthProtoSPoolWithdrawReq *)(header + 1);\n    if ((result=pack_user_pool_pair(username, poolname,\n                    &req->up_pair)) != 0)\n    {\n        return result;\n    }\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nstatic int client_proto_gpool_list_do(FCFSAuthClientContext\n        *client_ctx, ConnectionInfo *conn, const string_t *username,\n        const string_t *poolname, const SFListLimitInfo *limit,\n        SFProtoRecvBuffer *buffer, struct fast_mpool_man *mpool,\n        FCFSAuthGrantedPoolArray *array, bool *is_last)\n{\n    FCFSAuthProtoHeader *header;\n    char out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoGPoolListReq) + 2 * NAME_MAX];\n    SFResponseInfo response;\n    FCFSAuthProtoGPoolListReq *req;\n    FCFSAuthProtoListRespHeader *resp_header;\n    FCFSAuthProtoGPoolListRespBodyPart *body_part;\n    FCFSAuthProtoNameInfo *proto_pname;\n    FCFSAuthGrantedPoolFullInfo *gpool;\n    FCFSAuthGrantedPoolFullInfo *end;\n    char *p;\n    int out_bytes;\n    int count;\n    int result;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    out_bytes = sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoGPoolListReq) +\n        username->len + poolname->len;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ,\n            out_bytes - sizeof(FCFSAuthProtoHeader));\n\n    req = (FCFSAuthProtoGPoolListReq *)(header + 1);\n    if ((result=pack_user_pool_pair_ex(username, poolname,\n                    &req->up_pair, false)) != 0)\n    {\n        return result;\n    }\n    sf_proto_pack_limit(limit, &req->limit);\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_vary_response(conn, out_buff, out_bytes,\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_RESP, buffer,\n                    sizeof(FCFSAuthProtoListRespHeader))) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n        return result;\n    }\n\n    resp_header = (FCFSAuthProtoListRespHeader *)buffer->buff;\n    count = buff2int(resp_header->count);\n    *is_last = resp_header->is_last;\n    if ((result=fcfs_auth_gpool_check_realloc_array(array,\n                    array->count + count)) != 0)\n    {\n        return result;\n    }\n\n    p = (char *)(resp_header + 1);\n    end = array->gpools + array->count + count;\n    for (gpool=array->gpools+array->count; gpool<end; gpool++) {\n        body_part = (FCFSAuthProtoGPoolListRespBodyPart *)p;\n        gpool->granted.privs.fdir = buff2int(body_part->privs.fdir);\n        gpool->granted.privs.fstore = buff2int(body_part->privs.fstore);\n        if ((result=fast_mpool_alloc_string_ex(mpool, &gpool->username,\n                        body_part->up_pair.username.str,\n                        body_part->up_pair.username.len)) != 0)\n        {\n            return result;\n        }\n\n        proto_pname = (FCFSAuthProtoNameInfo *)(body_part->up_pair.\n                username.str + body_part->up_pair.username.len);\n        if ((result=fast_mpool_alloc_string_ex(mpool, &gpool->pool_name,\n                        proto_pname->str, proto_pname->len)) != 0)\n        {\n            return result;\n        }\n\n        p += sizeof(FCFSAuthProtoGPoolListRespBodyPart)\n            + gpool->username.len + gpool->pool_name.len;\n    }\n\n    if (response.header.body_len != p - buffer->buff) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"response body length: %d != expect: %d\",\n                __LINE__, response.header.body_len,\n                (int)(p - buffer->buff));\n        return EINVAL;\n    }\n\n    array->count += count;\n    return result;\n}\n\nint fcfs_auth_client_proto_gpool_list(FCFSAuthClientContext\n        *client_ctx, ConnectionInfo *conn, const string_t *username,\n        const string_t *poolname, const SFListLimitInfo *limit,\n        struct fast_mpool_man *mpool, FCFSAuthGrantedPoolArray *array)\n{\n    return client_proto_list_wrapper((client_proto_list_func)\n            client_proto_gpool_list_do, client_ctx, conn, username,\n            poolname, limit, mpool, array, &array->count);\n}\n\nint fcfs_auth_client_get_master(FCFSAuthClientContext *client_ctx,\n        FCFSAuthClientServerEntry *master)\n{\n    const bool shared = false;\n    int result;\n    ConnectionInfo *conn;\n    FCFSAuthProtoHeader *header;\n    SFResponseInfo response;\n    FCFSAuthProtoGetServerResp server_resp;\n    char out_buff[sizeof(FCFSAuthProtoHeader)];\n\n    conn = client_ctx->cm.ops.get_connection(\n            &client_ctx->cm, 0, shared, &result);\n    if (conn == NULL) {\n        return result;\n    }\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ,\n            sizeof(out_buff) - sizeof(FCFSAuthProtoHeader));\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_response(conn, out_buff, sizeof(out_buff),\n                    &response, client_ctx->common_cfg.network_timeout,\n                    FCFS_AUTH_SERVICE_PROTO_GET_MASTER_RESP,\n                    (char *)&server_resp, sizeof(FCFSAuthProtoGetServerResp))) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    } else {\n        master->server_id = buff2int(server_resp.server_id);\n        memcpy(master->conn.ip_addr, server_resp.ip_addr, IP_ADDRESS_SIZE);\n        *(master->conn.ip_addr + IP_ADDRESS_SIZE - 1) = '\\0';\n        master->conn.port = buff2short(server_resp.port);\n        master->conn.comm_type = conn->comm_type;\n    }\n\n    SF_CLIENT_RELEASE_CONNECTION(&client_ctx->cm, conn, result);\n    return result;\n}\n\nint fcfs_auth_client_cluster_stat(FCFSAuthClientContext *client_ctx,\n        FCFSAuthClientClusterStatEntry *stats, const int size, int *count)\n{\n    const bool shared = false;\n    FCFSAuthProtoHeader *header;\n    FCFSAuthProtoClusterStatRespBodyHeader *body_header;\n    FCFSAuthProtoClusterStatRespBodyPart *body_part;\n    FCFSAuthProtoClusterStatRespBodyPart *body_end;\n    FCFSAuthClientClusterStatEntry *stat;\n    ConnectionInfo *conn;\n    char out_buff[sizeof(FCFSAuthProtoHeader)];\n    char fixed_buff[8 * 1024];\n    char *in_buff;\n    SFResponseInfo response;\n    int result;\n    int calc_size;\n\n    if ((conn=client_ctx->cm.ops.get_master_connection(&client_ctx->cm,\n                    0, shared, &result)) == NULL)\n    {\n        return result;\n    }\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_REQ,\n            sizeof(out_buff) - sizeof(FCFSAuthProtoHeader));\n\n    response.error.length = 0;\n    in_buff = fixed_buff;\n    if ((result=sf_send_and_check_response_header(conn, out_buff,\n                    sizeof(out_buff), &response, client_ctx->common_cfg.\n                    network_timeout, FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_RESP)) == 0)\n    {\n        if (response.header.body_len > sizeof(fixed_buff)) {\n            in_buff = (char *)fc_malloc(response.header.body_len);\n            if (in_buff == NULL) {\n                response.error.length = sprintf(response.error.message,\n                        \"malloc %d bytes fail\", response.header.body_len);\n                result = ENOMEM;\n            }\n        }\n\n        if (result == 0) {\n            result = tcprecvdata_nb(conn->sock, in_buff,\n                    response.header.body_len, client_ctx->\n                    common_cfg.network_timeout);\n        }\n    }\n\n    body_header = (FCFSAuthProtoClusterStatRespBodyHeader *)in_buff;\n    body_part = (FCFSAuthProtoClusterStatRespBodyPart *)(in_buff +\n            sizeof(FCFSAuthProtoClusterStatRespBodyHeader));\n    if (result == 0) {\n        *count = buff2int(body_header->count);\n\n        calc_size = sizeof(FCFSAuthProtoClusterStatRespBodyHeader) +\n            (*count) * sizeof(FCFSAuthProtoClusterStatRespBodyPart);\n        if (calc_size != response.header.body_len) {\n            response.error.length = sprintf(response.error.message,\n                    \"response body length: %d != calculate size: %d, \"\n                    \"server count: %d\", response.header.body_len,\n                    calc_size, *count);\n            result = EINVAL;\n        } else if (size < *count) {\n            response.error.length = sprintf(response.error.message,\n                    \"entry size %d too small < %d\", size, *count);\n            *count = 0;\n            result = ENOSPC;\n        }\n    } else {\n        *count = 0;\n    }\n\n    if (result != 0) {\n        auth_log_network_error(&response, conn, result);\n    } else {\n        body_end = body_part + (*count);\n        for (stat=stats; body_part<body_end; body_part++, stat++) {\n            stat->is_online = body_part->is_online;\n            stat->is_master = body_part->is_master;\n            stat->server_id = buff2int(body_part->server_id);\n            memcpy(stat->ip_addr, body_part->ip_addr, IP_ADDRESS_SIZE);\n            *(stat->ip_addr + IP_ADDRESS_SIZE - 1) = '\\0';\n            stat->port = buff2short(body_part->port);\n        }\n    }\n\n    SF_CLIENT_RELEASE_CONNECTION(&client_ctx->cm, conn, result);\n    if (in_buff != fixed_buff) {\n        if (in_buff != NULL) {\n            free(in_buff);\n        }\n    }\n\n    return result;\n}\n"
  },
  {
    "path": "src/auth/client/client_proto.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_CLIENT_PROTO_H\n#define _FCFS_AUTH_CLIENT_PROTO_H\n\n#include \"fastcommon/fast_mpool.h\"\n#include \"sf/sf_proto.h\"\n#include \"client_types.h\"\n\ntypedef struct fcfs_auth_client_cluster_stat_entry {\n    int server_id;\n    bool is_online;\n    bool is_master;\n    uint16_t port;\n    char ip_addr[IP_ADDRESS_SIZE];\n} FCFSAuthClientClusterStatEntry;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint fcfs_auth_client_get_master(FCFSAuthClientContext *client_ctx,\n        FCFSAuthClientServerEntry *master);\n\nint fcfs_auth_client_cluster_stat(FCFSAuthClientContext *client_ctx,\n        FCFSAuthClientClusterStatEntry *stats, const int size, int *count);\n\nint fcfs_auth_client_proto_session_subscribe(\n        FCFSAuthClientContext *client_ctx, ConnectionInfo *conn);\n\nint fcfs_auth_client_proto_session_validate(\n        FCFSAuthClientContext *client_ctx, ConnectionInfo *conn,\n        const string_t *session_id, const string_t *validate_key,\n        const FCFSAuthValidatePriviledgeType priv_type,\n        const int64_t pool_id, const int64_t priv_required);\n\nint fcfs_auth_client_proto_user_login(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const string_t *passwd, const string_t *poolname,\n        const int flags);\n\nint fcfs_auth_client_proto_user_create(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const FCFSAuthUserInfo *user);\n\nint fcfs_auth_client_proto_user_passwd(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const string_t *passwd);\n\nint fcfs_auth_client_proto_user_list(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const SFListLimitInfo *limit, struct fast_mpool_man *mpool,\n        FCFSAuthUserArray *array);\n\nint fcfs_auth_client_proto_user_grant(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username, const int64_t priv);\n\nint fcfs_auth_client_proto_user_remove(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username);\n\nint fcfs_auth_client_proto_spool_create(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, FCFSAuthStoragePoolInfo *spool,\n        const int name_size, const bool dryrun);\n\nint fcfs_auth_client_proto_spool_list(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username,\n        const string_t *poolname, const SFListLimitInfo *limit,\n        struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *array);\n\nint fcfs_auth_client_proto_spool_remove(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *poolname);\n\nint fcfs_auth_client_proto_spool_set_quota(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *poolname, const int64_t quota);\n\nint fcfs_auth_client_proto_spool_get_quota(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *poolname, int64_t *quota);\n\nint fcfs_auth_client_proto_gpool_grant(FCFSAuthClientContext *client_ctx,\n        ConnectionInfo *conn, const string_t *username, const string_t\n        *poolname, const FCFSAuthSPoolPriviledges *privs);\n\nint fcfs_auth_client_proto_gpool_withdraw(FCFSAuthClientContext\n        *client_ctx, ConnectionInfo *conn, const string_t *username,\n        const string_t *poolname);\n\nint fcfs_auth_client_proto_gpool_list(FCFSAuthClientContext\n        *client_ctx, ConnectionInfo *conn, const string_t *username,\n        const string_t *poolname, const SFListLimitInfo *limit,\n        struct fast_mpool_man *mpool, FCFSAuthGrantedPoolArray *array);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/client/client_types.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_AUTH_CLIENT_TYPES_H\n#define _FCFS_AUTH_CLIENT_TYPES_H\n\n#include \"fastcommon/common_define.h\"\n#include \"sf/sf_configs.h\"\n#include \"sf/sf_connection_manager.h\"\n#include \"sf/sf_cluster_cfg.h\"\n#include \"auth_types.h\"\n\n#define FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME \"/etc/fastcfs/auth/client.conf\"\n\ntypedef struct fcfs_auth_client_user_key_pair {\n    string_t username;\n    string_t key_filename;\n} FCFSAuthClientUserKeyPair;\n\ntypedef struct fcfs_auth_client_server_entry {\n    int server_id;\n    ConnectionInfo conn;\n    char status;\n} FCFSAuthClientServerEntry;\n\ntypedef struct fcfs_auth_server_group {\n    int alloc_size;\n    int count;\n    ConnectionInfo *servers;\n} FCFSAuthServerGroup;\n\ntypedef struct fcfs_auth_client_common_cfg {\n    string_t username;\n    string_t passwd;\n    string_t secret_key_filename;\n    unsigned char passwd_buff[FCFS_AUTH_PASSWD_LEN];\n    char *buff;  //for username and secret_key_filename\n} FCFSAuthClientCommonCfg;\n\ntypedef struct fcfs_auth_client_context {\n    bool inited;\n    SFClusterConfig cluster;\n    SFConnectionManager cm;\n    FCFSAuthClientCommonCfg auth_cfg;\n    SFClientCommonConfig common_cfg;\n    struct {\n        string_t *poolname;\n        char id[FCFS_AUTH_SESSION_ID_LEN];\n    } session;\n} FCFSAuthClientContext;\n\ntypedef struct fcfs_auth_client_full_context {\n    bool enabled;\n    FCFSAuthClientContext *ctx;\n} FCFSAuthClientFullContext;\n\n#endif\n"
  },
  {
    "path": "src/auth/client/fcfs_auth_client.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"sf/idempotency/client/rpc_wrapper.h\"\n#include \"../common/auth_func.h\"\n#include \"fcfs_auth_client.h\"\n\n#define GET_MASTER_CONNECTION(cm, arg1, result)   \\\n    (cm)->ops.get_master_connection(cm, arg1, true, result)\n\nstatic int load_auth_user_passwd(FCFSAuthClientCommonCfg *auth_cfg,\n        IniContext *ini_context, const char *auth_config_filename)\n{\n    int result;\n    bool need_resolve;\n    string_t username;\n    string_t secret_key_filename;\n    FilenameString new_key_filename;\n    char full_secret_filename[PATH_MAX];\n    string_t new_filename;\n\n    if (g_fcfs_auth_client_vars.client_ctx.auth_cfg.username.str == NULL) {\n        username.str = iniGetStrValue(NULL, \"username\", ini_context);\n        if (username.str == NULL || *(username.str) == '\\0') {\n            username.str = \"admin\";\n        }\n        username.len = strlen(username.str);\n    } else {\n        username = g_fcfs_auth_client_vars.client_ctx.auth_cfg.username;\n    }\n\n    if (g_fcfs_auth_client_vars.client_ctx.auth_cfg.\n            secret_key_filename.str == NULL)\n    {\n        secret_key_filename.str = iniGetStrValue(NULL,\n                \"secret_key_filename\", ini_context);\n        if (secret_key_filename.str == NULL ||\n                *(secret_key_filename.str) == '\\0')\n        {\n            secret_key_filename.str = \"keys/${username}.key\";\n        }\n        secret_key_filename.len = strlen(secret_key_filename.str);\n        need_resolve = true;\n    } else {\n        secret_key_filename = g_fcfs_auth_client_vars.\n            client_ctx.auth_cfg.secret_key_filename;\n        need_resolve = false;\n    }\n\n    auth_cfg->passwd.str = (char *)auth_cfg->passwd_buff;\n    auth_cfg->passwd.len = FCFS_AUTH_PASSWD_LEN;\n    fcfs_auth_replace_filename_with_username(&secret_key_filename,\n            &username, &new_key_filename);\n    if (need_resolve) {\n        new_filename.str = full_secret_filename;\n        new_filename.len = resolve_path(auth_config_filename,\n                FC_FILENAME_STRING_PTR(new_key_filename),\n                full_secret_filename, sizeof(full_secret_filename));\n    } else {\n        new_filename.str = FC_FILENAME_STRING_PTR(new_key_filename);\n        new_filename.len = strlen(new_filename.str);\n    }\n\n    if (g_fcfs_auth_client_vars.need_load_passwd) {\n        if ((result=fcfs_auth_load_passwd_ex(new_filename.str,\n                        auth_cfg->passwd_buff, g_fcfs_auth_client_vars.\n                        ignore_when_passwd_not_exist)) != 0)\n        {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"config file: %s, secret_key_filename: %s, \"\n                    \"load password fail, ret code: %d\", __LINE__,\n                    auth_config_filename, new_filename.str, result);\n            return result;\n        }\n    } else {\n        memset(auth_cfg->passwd_buff, 0, FCFS_AUTH_PASSWD_LEN);\n    }\n\n    auth_cfg->buff = (char *)fc_malloc(username.len + 1 +\n            new_filename.len + 1);\n    if (auth_cfg->buff == NULL) {\n        return ENOMEM;\n    }\n\n    auth_cfg->username.str = auth_cfg->buff;\n    auth_cfg->username.len = username.len;\n    memcpy(auth_cfg->username.str, username.str, username.len + 1);\n\n    auth_cfg->secret_key_filename.str =\n        auth_cfg->username.str + username.len + 1;\n    auth_cfg->secret_key_filename.len = new_filename.len;\n    memcpy(auth_cfg->secret_key_filename.str,\n            new_filename.str, new_filename.len + 1);\n    return 0;\n}\n\nstatic int load_auth_config(FCFSAuthClientFullContext *auth,\n        const char *auth_config_filename,\n        const SFServerGroupIndexType index_type)\n{\n    int result;\n    IniContext ini_context;\n    char *client_config_filename;\n    char full_config_filename[PATH_MAX];\n\n    if ((result=iniLoadFromFile(auth_config_filename, &ini_context)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                __LINE__, auth_config_filename, result);\n        return result;\n    }\n\n    auth->enabled = iniGetBoolValue(NULL, \"auth_enabled\", &ini_context, false);\n    do {\n        if ((!auth->enabled) || auth->ctx->inited) {\n            break;\n        }\n\n        if ((result=load_auth_user_passwd(&auth->ctx->auth_cfg,\n                        &ini_context, auth_config_filename)) != 0)\n        {\n            break;\n        }\n\n        client_config_filename = iniGetStrValue(NULL,\n                \"client_config_filename\", &ini_context);\n        if (client_config_filename == NULL ||\n                *client_config_filename == '\\0')\n        {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"config file: %s, item \\\"client_config_filename\\\" \"\n                    \"not exist or empty\", __LINE__, auth_config_filename);\n            result = ENOENT;\n            break;\n        }\n\n        resolve_path(auth_config_filename, client_config_filename,\n                full_config_filename, sizeof(full_config_filename));\n        result = fcfs_auth_client_init_ex1(auth->ctx, full_config_filename,\n                NULL, index_type);\n    } while (0);\n\n    iniFreeContext(&ini_context);\n    return result;\n}\n\nint fcfs_auth_load_config_ex(FCFSAuthClientFullContext *auth,\n        const char *config_filename, const char *section_name,\n        const SFServerGroupIndexType index_type)\n{\n    int result;\n    IniContext ini_context;\n    char *auth_config_filename;\n    char full_auth_filename[PATH_MAX];\n\n    if ((result=iniLoadFromFile(config_filename, &ini_context)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                __LINE__, config_filename, result);\n        return result;\n    }\n\n    auth_config_filename = iniGetStrValue(section_name,\n            \"auth_config_filename\", &ini_context);\n    if (auth_config_filename == NULL || *auth_config_filename == '\\0') {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"config file: %s, item \\\"auth_config_filename\\\" \"\n                \"not exist or empty\", __LINE__, config_filename);\n        result = ENOENT;\n    } else {\n        resolve_path(config_filename, auth_config_filename,\n                full_auth_filename, sizeof(full_auth_filename));\n        result = load_auth_config(auth, full_auth_filename, index_type);\n    }\n\n    iniFreeContext(&ini_context);\n    return result;\n}\n\nvoid fcfs_auth_config_to_string_ex(const FCFSAuthClientFullContext *auth,\n        const char *caption, char *output, const int size)\n{\n    int len;\n    len = snprintf(output, size, \"%s{enabled: %d\", caption, auth->enabled);\n    if (auth->enabled) {\n        len += snprintf(output + len, size - len, \", username: %s, \"\n                \"secret_key_filename: %s\",\n                auth->ctx->auth_cfg.username.str,\n                auth->ctx->auth_cfg.secret_key_filename.str);\n    }\n    snprintf(output + len, size - len, \"}\");\n}\n\nint fcfs_auth_client_user_login_ex(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *passwd,\n        const string_t *poolname, const int flags)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_login,\n            username, passwd, poolname, flags);\n}\n\nint fcfs_auth_client_session_subscribe(FCFSAuthClientContext *client_ctx)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_session_subscribe);\n}\n\nint fcfs_auth_client_session_validate(FCFSAuthClientContext *client_ctx,\n        const string_t *session_id, const string_t *validate_key,\n        const FCFSAuthValidatePriviledgeType priv_type,\n        const int64_t pool_id, const int64_t priv_required)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_session_validate,\n            session_id, validate_key, priv_type, pool_id, priv_required);\n}\n\nint fcfs_auth_client_user_create(FCFSAuthClientContext *client_ctx,\n        const FCFSAuthUserInfo *user)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_create, user);\n}\n\nint fcfs_auth_client_user_passwd(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *passwd)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_passwd,\n            username, passwd);\n}\n\nint fcfs_auth_client_user_list(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const SFListLimitInfo *limit,\n        struct fast_mpool_man *mpool, FCFSAuthUserArray *array)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_list,\n            username, limit, mpool, array);\n}\n\nint fcfs_auth_client_user_grant(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const int64_t priv)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_grant,\n            username, priv);\n}\n\nint fcfs_auth_client_user_remove(FCFSAuthClientContext *client_ctx,\n        const string_t *username)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_user_remove,\n            username);\n}\n\nint fcfs_auth_client_spool_create(FCFSAuthClientContext *client_ctx,\n        FCFSAuthStoragePoolInfo *spool, const int name_size,\n        const bool dryrun)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_spool_create,\n            spool, name_size, dryrun);\n}\n\nint fcfs_auth_client_spool_list(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *poolname,\n        const SFListLimitInfo *limit, struct fast_mpool_man *mpool,\n        FCFSAuthStoragePoolArray *array)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_spool_list,\n            username, poolname, limit, mpool, array);\n}\n\nint fcfs_auth_client_spool_remove(FCFSAuthClientContext *client_ctx,\n        const string_t *poolname)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_spool_remove,\n            poolname);\n}\n\nint fcfs_auth_client_spool_set_quota(FCFSAuthClientContext *client_ctx,\n        const string_t *poolname, const int64_t quota)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_spool_set_quota,\n            poolname, quota);\n}\n\nint fcfs_auth_client_spool_get_quota(FCFSAuthClientContext *client_ctx,\n        const string_t *poolname, int64_t *quota)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_spool_get_quota,\n            poolname, quota);\n}\n\nint fcfs_auth_client_gpool_grant(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *poolname,\n        const FCFSAuthSPoolPriviledges *privs)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_gpool_grant,\n            username, poolname, privs);\n}\n\nint fcfs_auth_client_gpool_withdraw(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *poolname)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_gpool_withdraw,\n            username, poolname);\n}\n\nint fcfs_auth_client_gpool_list(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *poolname,\n        const SFListLimitInfo *limit, struct fast_mpool_man *mpool,\n        FCFSAuthGrantedPoolArray *array)\n{\n    SF_CLIENT_IDEMPOTENCY_QUERY_WRAPPER(client_ctx, &client_ctx->cm,\n            GET_MASTER_CONNECTION, 0, fcfs_auth_client_proto_gpool_list,\n            username, poolname, limit, mpool, array);\n}\n"
  },
  {
    "path": "src/auth/client/fcfs_auth_client.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_CLIENT_H\n#define _FCFS_AUTH_CLIENT_H\n\n#include \"auth_proto.h\"\n#include \"client_types.h\"\n#include \"client_func.h\"\n#include \"client_global.h\"\n#include \"client_proto.h\"\n#include \"session_regenerate.h\"\n\n#define fcfs_auth_load_config(auth, config_filename)  \\\n    fcfs_auth_load_config_ex(auth, config_filename, NULL, \\\n            sf_server_group_index_type_service)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint fcfs_auth_load_config_ex(FCFSAuthClientFullContext *auth,\n        const char *config_filename, const char *section_name,\n        const SFServerGroupIndexType index_type);\n\nvoid fcfs_auth_config_to_string_ex(const FCFSAuthClientFullContext *auth,\n        const char *caption, char *output, const int size);\n\nstatic inline void fcfs_auth_config_to_string(const FCFSAuthClientFullContext\n        *auth, char *output, const int size)\n{\n    const char *caption = \"auth \";\n    fcfs_auth_config_to_string_ex(auth, caption, output, size);\n}\n\nint fcfs_auth_client_user_login_ex(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *passwd,\n        const string_t *poolname, const int flags);\n\nstatic inline int fcfs_auth_client_user_login(\n        FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *passwd)\n{\n    const string_t poolname = {NULL, 0};\n    const int flags = 0;\n    return fcfs_auth_client_user_login_ex(client_ctx,\n            username, passwd, &poolname, flags);\n}\n\nstatic inline int fcfs_auth_client_session_create_ex(\n        FCFSAuthClientFullContext *auth, string_t *poolname,\n        const bool publish)\n{\n    int result;\n    int flags;\n\n    /*\n       logInfo(\"file: \"__FILE__\", line: %d, \"\n       \"auth_enabled: %d, username: %s, secret_key_filename: %s\",\n       __LINE__, auth->enabled, auth->ctx->auth_cfg.username.str,\n       auth->ctx->auth_cfg.secret_key_filename.str);\n     */\n\n    if (auth->enabled) {\n        auth->ctx->session.poolname = poolname;\n\n        if (publish) {\n            if ((result=session_regenerate_init()) != 0) {\n                return result;\n            }\n            flags = FCFS_AUTH_SESSION_FLAGS_PUBLISH;\n        } else {\n            flags = 0;\n        }\n        return fcfs_auth_client_user_login_ex(auth->ctx, &auth->\n                ctx->auth_cfg.username, &auth->ctx->auth_cfg.passwd,\n                poolname, flags);\n    } else {\n        return 0;\n    }\n}\n\nstatic inline int fcfs_auth_client_session_create(\n        FCFSAuthClientFullContext *auth, const bool publish)\n{\n#define AUTH_EMPTY_POOL_NAME SF_G_EMPTY_STRING\n    return fcfs_auth_client_session_create_ex(auth,\n            &AUTH_EMPTY_POOL_NAME, publish);\n}\n\nint fcfs_auth_client_session_subscribe(FCFSAuthClientContext *client_ctx);\n\nint fcfs_auth_client_session_validate(FCFSAuthClientContext *client_ctx,\n        const string_t *session_id, const string_t *validate_key,\n        const FCFSAuthValidatePriviledgeType priv_type,\n        const int64_t pool_id, const int64_t priv_required);\n\nint fcfs_auth_client_user_create(FCFSAuthClientContext *client_ctx,\n        const FCFSAuthUserInfo *user);\n\nint fcfs_auth_client_user_passwd(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *passwd);\n\nint fcfs_auth_client_user_list(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const SFListLimitInfo *limit,\n        struct fast_mpool_man *mpool, FCFSAuthUserArray *array);\n\nint fcfs_auth_client_user_grant(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const int64_t priv);\n\nint fcfs_auth_client_user_remove(FCFSAuthClientContext *client_ctx,\n        const string_t *username);\n\n/* storage pool operations */\nint fcfs_auth_client_spool_create(FCFSAuthClientContext *client_ctx,\n        FCFSAuthStoragePoolInfo *spool, const int name_size,\n        const bool dryrun);\n\nint fcfs_auth_client_spool_list(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *poolname,\n        const SFListLimitInfo *limit, struct fast_mpool_man *mpool,\n        FCFSAuthStoragePoolArray *array);\n\nint fcfs_auth_client_spool_remove(FCFSAuthClientContext *client_ctx,\n        const string_t *poolname);\n\nint fcfs_auth_client_spool_set_quota(FCFSAuthClientContext *client_ctx,\n        const string_t *poolname, const int64_t quota);\n\nint fcfs_auth_client_spool_get_quota(FCFSAuthClientContext *client_ctx,\n        const string_t *poolname, int64_t *quota);\n\n/* pool granted operations */\nint fcfs_auth_client_gpool_grant(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *poolname,\n        const FCFSAuthSPoolPriviledges *privs);\n\nint fcfs_auth_client_gpool_withdraw(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *poolname);\n\nint fcfs_auth_client_gpool_list(FCFSAuthClientContext *client_ctx,\n        const string_t *username, const string_t *poolname,\n        const SFListLimitInfo *limit, struct fast_mpool_man *mpool,\n        FCFSAuthGrantedPoolArray *array);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/client/fcfs_auth_for_server.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"sf/idempotency/client/rpc_wrapper.h\"\n#include \"../common/auth_func.h\"\n#include \"fcfs_auth_for_server.h\"\n\nint fcfs_auth_for_server_check_priv(FCFSAuthClientContext *client_ctx,\n        SFRequestInfo *request, SFResponseInfo *response,\n        const FCFSAuthValidatePriviledgeType priv_type,\n        const int64_t the_priv)\n{\n    int result;\n    bool validate;\n    ServerSessionIdInfo session;\n    string_t session_id;\n\n    if ((result=sf_server_check_min_body_length(response, request->\n                    header.body_len, FCFS_AUTH_SESSION_ID_LEN)) != 0)\n    {\n        return result;\n    }\n    session.id = buff2long(request->body);\n\n    if (session.fields.publish) {\n        switch (priv_type) {\n            case fcfs_auth_validate_priv_type_user:\n                result = server_session_user_priv_granted(\n                        session.id, the_priv);\n                break;\n            case fcfs_auth_validate_priv_type_pool_fdir:\n                result = server_session_fdir_priv_granted(\n                        session.id, the_priv);\n                break;\n            default:\n                result = server_session_fstore_priv_granted(\n                        session.id, the_priv);\n                break;\n        }\n\n        if (result == SF_SESSION_ERROR_NOT_EXIST) {\n            validate = (g_current_time - session.fields.ts <=\n                    g_server_session_cfg.validate_within_fresh_seconds);\n        } else {\n            validate = false;\n        }\n    } else {\n        validate = true;\n    }\n\n    if (validate) {\n        const int64_t pool_id = 0;\n        FC_SET_STRING_EX(session_id, request->body, FCFS_AUTH_SESSION_ID_LEN);\n        result = fcfs_auth_client_session_validate(client_ctx,\n                &session_id, &g_server_session_cfg.validate_key,\n                priv_type, pool_id, the_priv);\n    }\n\n    /*\n    logInfo(\"check priv cmd: %d, session: %\"PRId64\", validate: %d, \"\n            \"error no: %d\", request->header.cmd, session.id, validate, result);\n            */\n    if (result != 0) {\n        return result;\n    }\n\n    request->body += FCFS_AUTH_SESSION_ID_LEN;\n    request->header.body_len -= FCFS_AUTH_SESSION_ID_LEN;\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/client/fcfs_auth_for_server.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_FOR_SERVER_H\n#define _FCFS_AUTH_FOR_SERVER_H\n\n#include \"fcfs_auth_client.h\"\n#include \"server_session.h\"\n#include \"session_sync.h\"\n\n#define fcfs_auth_for_server_init(auth, ini_ctx, cluster_filename) \\\n    fcfs_auth_for_server_init_ex(auth, ini_ctx, cluster_filename, NULL)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint fcfs_auth_for_server_check_priv(FCFSAuthClientContext *client_ctx,\n        SFRequestInfo *request, SFResponseInfo *response,\n        const FCFSAuthValidatePriviledgeType priv_type,\n        const int64_t the_priv);\n\nstatic inline int fcfs_auth_for_server_init_ex(FCFSAuthClientFullContext\n        *auth, IniFullContext *ini_ctx, const char *cluster_filename,\n        const char *section_name)\n{\n    int result;\n\n    g_fcfs_auth_client_vars.ignore_when_passwd_not_exist = true;\n    if ((result=fcfs_auth_load_config_ex(auth, cluster_filename,\n                    section_name, sf_server_group_index_type_cluster)) != 0)\n    {\n        return result;\n    }\n\n    if (auth->enabled) {\n        if ((result=server_session_init(ini_ctx)) != 0) {\n            return result;\n        }\n        if ((result=session_sync_init()) != 0) {\n            return result;\n        }\n    }\n\n    return result;\n}\n\nstatic inline void fcfs_auth_for_server_cfg_to_string(const\n        FCFSAuthClientFullContext *auth, char *output, const int size)\n{\n    const char *caption = \"\";\n    char sz_session_config[512];\n    char sz_auth_config[1024];\n\n    fcfs_auth_config_to_string_ex(auth, caption,\n            sz_auth_config, sizeof(sz_auth_config));\n    if (auth->enabled) {\n        server_session_cfg_to_string_ex(sz_session_config,\n                sizeof(sz_session_config), true);\n        snprintf(output, size, \"auth {%s, %s}\",\n                sz_auth_config, sz_session_config);\n    } else {\n        snprintf(output, size, \"auth {%s}\", sz_auth_config);\n    }\n}\n\nstatic inline int fcfs_auth_for_server_start(const\n        FCFSAuthClientFullContext *auth)\n{\n    if (auth->enabled) {\n        return session_sync_start();\n    } else {\n        return 0;\n    }\n}\n\nstatic inline void fcfs_auth_for_server_destroy(\n         FCFSAuthClientFullContext *auth)\n{\n    fcfs_auth_client_destroy_ex(auth->ctx);\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/client/session_regenerate.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/pthread_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"sf/sf_proto.h\"\n#include \"sf/sf_global.h\"\n#include \"auth_func.h\"\n#include \"client_global.h\"\n#include \"fcfs_auth_client.h\"\n#include \"session_regenerate.h\"\n\ntypedef struct synced_session_array {\n    bool inited;\n    pthread_mutex_t lock;\n} SessionRegenerateContext;\n\nstatic SessionRegenerateContext session_ctx;\n\nstatic int session_regenerate_do()\n{\n    char old_session_id[FCFS_AUTH_SESSION_ID_LEN];\n    int result;\n\n    memcpy(old_session_id, g_fcfs_auth_client_vars.client_ctx.\n            session.id, FCFS_AUTH_SESSION_ID_LEN);\n\n    PTHREAD_MUTEX_LOCK(&session_ctx.lock);\n    if (memcmp(old_session_id, g_fcfs_auth_client_vars.client_ctx.\n                session.id, FCFS_AUTH_SESSION_ID_LEN) == 0)\n    {\n        result = fcfs_auth_client_user_login_ex(&g_fcfs_auth_client_vars.\n                client_ctx, &g_fcfs_auth_client_vars.client_ctx.auth_cfg.\n                username, &g_fcfs_auth_client_vars.client_ctx.auth_cfg.\n                passwd, g_fcfs_auth_client_vars.client_ctx.session.poolname,\n                FCFS_AUTH_SESSION_FLAGS_PUBLISH);\n    } else {\n        result = 0;\n    }\n    PTHREAD_MUTEX_UNLOCK(&session_ctx.lock);\n\n    return result;\n}\n\nstatic int fcfs_auth_error_handler(const int errnum)\n{\n    if (errnum == SF_SESSION_ERROR_NOT_EXIST) {\n        if (session_regenerate_do() == EPERM) {\n            return EPERM;\n        } else {\n            return SF_RETRIABLE_ERROR_NO_SERVER;\n        }\n    } else {\n        return errnum;\n    }\n}\n\nint session_regenerate_init()\n{\n    int result;\n\n    if (session_ctx.inited) {\n        return 0;\n    }\n\n    if ((result=init_pthread_lock(&session_ctx.lock)) != 0) {\n        return result;\n    }\n\n    sf_set_error_handler(fcfs_auth_error_handler);\n    session_ctx.inited = true;\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/client/session_regenerate.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_SESSION_REGENERATE_H\n#define _FCFS_AUTH_SESSION_REGENERATE_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint session_regenerate_init();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/client/session_sync.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"sf/sf_proto.h\"\n#include \"sf/sf_global.h\"\n#include \"auth_proto.h\"\n#include \"auth_func.h\"\n#include \"server_session.h\"\n#include \"client_global.h\"\n#include \"client_proto.h\"\n#include \"session_sync.h\"\n\ntypedef struct synced_session_entry {\n    char operation;\n    uint64_t session_id;\n    SessionSyncedFields fields;\n} SyncedSessionEntry;\n\ntypedef struct synced_session_array {\n    int alloc;\n    int count;\n    SyncedSessionEntry *entries;\n    SFProtoRecvBuffer buffer;\n} SyncedSessionArray;\n\nstatic SyncedSessionArray session_array;\n\nstatic int synced_session_array_init(SyncedSessionArray *array)\n{\n    int result;\n\n    if ((result=sf_init_recv_buffer(&array->buffer, 0)) != 0) {\n        return result;\n    }\n\n    array->alloc = array->count = 0;\n    array->entries = NULL;\n    return 0;\n}\n\nstatic int check_realloc_session_array(SyncedSessionArray *array,\n        const int target_count)\n{\n    SyncedSessionEntry *new_entries;\n    int new_alloc;\n    int bytes;\n\n    if (target_count <= array->alloc) {\n        return 0;\n    }\n\n    if (array->alloc == 0) {\n        new_alloc = 4 * 1024;\n    } else {\n        new_alloc = 2 * array->alloc;\n    }\n    while (new_alloc < target_count) {\n        new_alloc *= 2;\n    }\n\n    bytes = sizeof(SyncedSessionEntry) * new_alloc;\n    new_entries = (SyncedSessionEntry *)fc_malloc(bytes);\n    if (new_entries == NULL) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"malloc %d bytes fail\", __LINE__, bytes);\n        return ENOMEM;\n    }\n\n    if (array->entries != NULL) {\n        free(array->entries);\n    }\n    array->entries = new_entries;\n    array->alloc = new_alloc;\n    return 0;\n}\n\nstatic int parse_session_push(SFResponseInfo *response)\n{\n    FCFSAuthProtoSessionPushRespBodyHeader *body_header;\n    FCFSAuthProtoSessionPushRespBodyPart *body_part;\n    SyncedSessionEntry *entry;\n    SyncedSessionEntry *end;\n    char *p;\n    int count;\n    int result;\n\n    body_header = (FCFSAuthProtoSessionPushRespBodyHeader *)\n        session_array.buffer.buff;\n    p = (char *)(body_header + 1);\n    count = buff2int(body_header->count);\n    if ((result=check_realloc_session_array(\n                    &session_array, count)) != 0)\n    {\n        return result;\n    }\n\n    end = session_array.entries + count;\n    for (entry=session_array.entries; entry<end; entry++) {\n        if ((p - session_array.buffer.buff) + sizeof(\n                    FCFSAuthProtoSessionPushRespBodyPart) >\n                response->header.body_len)\n        {\n            response->error.length = sprintf(\n                    response->error.message,\n                    \"body length: %d is too small\",\n                    response->header.body_len);\n            return EINVAL;\n        }\n\n        body_part = (FCFSAuthProtoSessionPushRespBodyPart *)p;\n        entry->operation = body_part->operation;\n        entry->session_id = buff2long(body_part->session_id);\n        if (entry->operation == FCFS_AUTH_SESSION_OP_TYPE_CREATE) {\n            entry->fields.user.id = buff2long(body_part->entry->user.id);\n            entry->fields.user.priv = buff2long(body_part->entry->user.priv);\n            entry->fields.pool.id = buff2long(body_part->entry->pool.id);\n            entry->fields.pool.available = body_part->entry->pool.available;\n            entry->fields.pool.privs.fdir = buff2int(\n                    body_part->entry->pool.privs.fdir);\n            entry->fields.pool.privs.fstore = buff2int(\n                    body_part->entry->pool.privs.fstore);\n\n            p += sizeof(FCFSAuthProtoSessionPushRespBodyPart) +\n                sizeof(FCFSAuthProtoSessionPushEntry);\n        } else {\n            p += sizeof(FCFSAuthProtoSessionPushRespBodyPart);\n        }\n    }\n\n    if (response->header.body_len != (p - session_array.buffer.buff)) {\n        response->error.length = sprintf(response->error.message,\n                \"body length: %d != expect: %d\",\n                response->header.body_len,\n                (int)(p - session_array.buffer.buff));\n        return EINVAL;\n    }\n\n    session_array.count = count;\n    return 0;\n}\n\nstatic void deal_session_array()\n{\n    const bool publish = true;\n    ServerSessionEntry session;\n    SyncedSessionEntry *entry;\n    SyncedSessionEntry *end;\n\n    end = session_array.entries + session_array.count;\n    for (entry=session_array.entries; entry<end; entry++) {\n        if (0) {\n            char buff[512];\n            int len;\n            len = sprintf(buff, \"%d. session id: %\"PRId64\", operation: %c\",\n                    (int)(entry - session_array.entries + 1),\n                    entry->session_id, entry->operation);\n            if (entry->operation == FCFS_AUTH_SESSION_OP_TYPE_CREATE) {\n                len += sprintf(buff + len, \", user {id: %\"PRId64\n                        \", priv: %\"PRId64\"}\", entry->fields.user.id,\n                        entry->fields.user.priv);\n                if (entry->fields.pool.id != 0) {\n                    len += sprintf(buff + len, \", pool {id: %\"PRId64\n                            \", avail: %d, privs: {fdir: %d, fstore: %d}}\",\n                            entry->fields.pool.id, entry->fields.pool.available,\n                            entry->fields.pool.privs.fdir,\n                            entry->fields.pool.privs.fstore);\n                }\n            }\n            logInfo(\"%s\", buff);\n        }\n\n        if (entry->operation == FCFS_AUTH_SESSION_OP_TYPE_CREATE) {\n            session.id_info.id = entry->session_id;\n            session.fields = &entry->fields;\n            server_session_add(&session, publish);\n        } else {\n            server_session_delete(entry->session_id);\n        }\n    }\n}\n\nstatic int session_sync(ConnectionInfo *conn)\n{\n    const int network_timeout = 2;\n    int result;\n    SFResponseInfo response;\n\n    response.error.length = 0;\n    while (SF_G_CONTINUE_FLAG) {\n        result = sf_recv_vary_response(conn, &response, network_timeout,\n                FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_REQ, &session_array.\n                buffer, sizeof(FCFSAuthProtoSessionPushRespBodyHeader));\n        if (result == 0) {\n            if ((result=parse_session_push(&response)) != 0) {\n                if (response.error.length > 0) {\n                    auth_log_network_error(&response, conn, result);\n                }\n                break;\n            }\n            deal_session_array();\n        } else if (result == ETIMEDOUT) {\n            if ((result=sf_active_test(conn, &response,\n                            g_fcfs_auth_client_vars.client_ctx.\n                            common_cfg.network_timeout)) != 0)\n            {\n                break;\n            }\n        } else {\n            auth_log_network_error(&response, conn, result);\n            break;\n        }\n    }\n\n    if (SF_G_CONTINUE_FLAG) {\n        server_session_clear();\n    }\n\n    return result;\n}\n\nstatic void *session_sync_thread_func(void *arg)\n{\n    const bool shared = false;\n    SFConnectionManager *cm;\n    ConnectionInfo *conn;\n    int result;\n\n#ifdef OS_LINUX\n    prctl(PR_SET_NAME, \"session-sync\");\n#endif\n\n    cm = &g_fcfs_auth_client_vars.client_ctx.cm;\n    while (SF_G_CONTINUE_FLAG) {\n        if ((conn=cm->ops.get_master_connection(cm, 0,\n                        shared, &result)) == NULL)\n        {\n            sleep(1);\n            continue;\n        }\n\n        if ((result=fcfs_auth_client_proto_session_subscribe(\n                        &g_fcfs_auth_client_vars.client_ctx, conn)) == 0)\n        {\n            session_sync(conn);\n        } else if (result == ENOENT) {\n            sleep(5);\n        } else if (result == EPERM) {\n            if ((result=fcfs_auth_load_passwd_ex(g_fcfs_auth_client_vars.\n                            client_ctx.auth_cfg.secret_key_filename.str,\n                            g_fcfs_auth_client_vars.client_ctx.auth_cfg.\n                            passwd_buff, g_fcfs_auth_client_vars.\n                            ignore_when_passwd_not_exist)) != 0)\n            {\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"secret_key_filename: %s, \"\n                        \"load password fail, ret code: %d\", __LINE__,\n                        g_fcfs_auth_client_vars.client_ctx.auth_cfg.\n                        secret_key_filename.str, result);\n                sleep(30);\n            }\n\n            sleep(10);\n        }\n\n        cm->ops.close_connection(cm, conn);\n        sleep(1);\n    }\n\n    return NULL;\n}\n\nint session_sync_init()\n{\n    int result;\n\n    if ((result=synced_session_array_init(&session_array)) != 0) {\n        return result;\n    }\n\n    return 0;\n}\n\nint session_sync_start()\n{\n    pthread_t tid;\n    return fc_create_thread(&tid, session_sync_thread_func,\n            NULL, SF_G_THREAD_STACK_SIZE);\n}\n"
  },
  {
    "path": "src/auth/client/session_sync.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_SESSION_SYNC_H\n#define _FCFS_AUTH_SESSION_SYNC_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint session_sync_init();\n\nint session_sync_start();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/client/simple_connection_manager.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/connection_pool.h\"\n#include \"client_global.h\"\n#include \"client_func.h\"\n#include \"client_proto.h\"\n#include \"simple_connection_manager.h\"\n\ntypedef struct fcfs_auth_cm_simple_extra {\n    /* master connection cache */\n    struct {\n        volatile bool valid;\n        ConnectionInfo conn;\n    } master_cache;\n\n    ConnectionPool cpool;\n    FCFSAuthClientContext *client_ctx;\n    FCFSAuthServerGroup *cluster_sarray;\n} FCFSAuthCMSimpleExtra;\n\nstatic int check_realloc_group_servers(FCFSAuthServerGroup *server_group)\n{\n    int bytes;\n    int alloc_size;\n    ConnectionInfo *servers;\n\n    if (server_group->alloc_size > server_group->count) {\n        return 0;\n    }\n\n    if (server_group->alloc_size > 0) {\n        alloc_size = server_group->alloc_size * 2;\n    } else {\n        alloc_size = 4;\n    }\n    bytes = sizeof(ConnectionInfo) * alloc_size;\n    servers = (ConnectionInfo *)fc_malloc(bytes);\n    if (servers == NULL) {\n        return errno != 0 ? errno : ENOMEM;\n    }\n    memset(servers, 0, bytes);\n\n    if (server_group->count > 0) {\n        memcpy(servers, server_group->servers,\n                sizeof(ConnectionInfo) * server_group->count);\n    }\n\n    server_group->servers = servers;\n    server_group->alloc_size = alloc_size;\n    return 0;\n}\n\nstatic ConnectionInfo *get_spec_connection(SFConnectionManager *cm,\n        const ConnectionInfo *target, const bool shared, int *err_no)\n{\n    FCFSAuthCMSimpleExtra *extra;\n    FCFSAuthServerGroup *cluster_sarray;\n    ConnectionInfo *conn;\n    ConnectionInfo *end;\n\n    extra = (FCFSAuthCMSimpleExtra *)cm->extra;\n    cluster_sarray = extra->cluster_sarray;\n    end = cluster_sarray->servers + cluster_sarray->count;\n    for (conn=cluster_sarray->servers; conn<end; conn++) {\n        if (FC_CONNECTION_SERVER_EQUAL1(*conn, *target)) {\n            break;\n        }\n    }\n\n    if (conn == end) {\n        if (check_realloc_group_servers(cluster_sarray) != 0) {\n            *err_no = ENOMEM;\n            return NULL;\n        }\n\n        conn = cluster_sarray->servers + cluster_sarray->count++;\n        conn_pool_set_server_info(conn, target->ip_addr, target->port);\n    }\n\n    return conn_pool_get_connection_ex(&extra->cpool,\n            target, \"fauth\", shared, err_no);\n}\n\nstatic ConnectionInfo *get_connection(SFConnectionManager *cm,\n        const int group_index, const bool shared, int *err_no)\n{\n    int index;\n    int i;\n    FCFSAuthServerGroup *cluster_sarray;\n    ConnectionInfo *conn;\n    ConnectionInfo *server;\n\n    cluster_sarray = ((FCFSAuthCMSimpleExtra *)cm->extra)->cluster_sarray;\n    index = rand() % cluster_sarray->count;\n    server = cluster_sarray->servers + index;\n    if ((conn=get_spec_connection(cm, server, shared, err_no)) != NULL) {\n        return conn;\n    }\n\n    i = (index + 1) % cluster_sarray->count;\n    while (i != index) {\n        server = cluster_sarray->servers + i;\n        if ((conn=get_spec_connection(cm, server, shared, err_no)) != NULL) {\n            return conn;\n        }\n\n        i = (i + 1) % cluster_sarray->count;\n    }\n\n    logError(\"file: \"__FILE__\", line: %d, \"\n            \"get_connection fail, configured server count: %d\",\n            __LINE__, cluster_sarray->count);\n    return NULL;\n}\n\nstatic ConnectionInfo *get_master_connection(SFConnectionManager *cm,\n        const int group_index, const bool shared, int *err_no)\n{\n    FCFSAuthCMSimpleExtra *extra;\n    ConnectionInfo *conn;\n    FCFSAuthClientServerEntry master;\n    SFNetRetryIntervalContext net_retry_ctx;\n    int i;\n\n    extra = (FCFSAuthCMSimpleExtra *)cm->extra;\n    if (extra->master_cache.valid) {\n        if ((conn=conn_pool_get_connection_ex(&extra->cpool, &extra->\n                        master_cache.conn, \"fauth\", shared, err_no)) != NULL)\n        {\n            return conn;\n        } else {\n            extra->master_cache.valid = false;\n        }\n    }\n\n    sf_init_net_retry_interval_context(&net_retry_ctx,\n            &cm->common_cfg->net_retry_cfg.interval_mm,\n            &cm->common_cfg->net_retry_cfg.connect);\n    i = 0;\n    while (1) {\n        if ((*err_no=fcfs_auth_client_get_master(extra->\n                        client_ctx, &master)) != 0)\n        {\n            SF_NET_RETRY_CHECK_AND_SLEEP(net_retry_ctx,\n                    cm->common_cfg->net_retry_cfg.\n                    connect.times, ++i, *err_no);\n            continue;\n        }\n\n        if ((conn=get_spec_connection(cm, &master.conn,\n                        shared, err_no)) == NULL)\n        {\n            break;\n        }\n\n        extra->master_cache.valid = true;\n        extra->master_cache.conn = *conn;\n        return conn;\n    }\n\n    logError(\"file: \"__FILE__\", line: %d, \"\n            \"get_master_connection fail, errno: %d\",\n            __LINE__, *err_no);\n    return NULL;\n}\n\nstatic void release_connection(SFConnectionManager *cm,\n        ConnectionInfo *conn)\n{\n    FCFSAuthCMSimpleExtra *extra;\n\n    extra = (FCFSAuthCMSimpleExtra *)cm->extra;\n    conn_pool_close_connection_ex(&extra->cpool, conn, false);\n}\n\nstatic void close_connection(SFConnectionManager *cm,\n        ConnectionInfo *conn)\n{\n    FCFSAuthCMSimpleExtra *extra;\n    extra = (FCFSAuthCMSimpleExtra *)cm->extra;\n    if (extra->master_cache.valid && FC_CONNECTION_SERVER_EQUAL1(\n                extra->master_cache.conn, *conn))\n    {\n        extra->master_cache.valid = false;\n    }\n\n    conn_pool_close_connection_ex(&extra->cpool, conn, true);\n}\n\nstatic void copy_to_server_group_array(FCFSAuthClientContext *client_ctx,\n        FCFSAuthServerGroup *server_group, const int server_group_index)\n{\n    FCServerInfo *server;\n    FCServerInfo *end;\n    ConnectionInfo *conn;\n    int server_count;\n\n    server_count = FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg);\n    conn = server_group->servers;\n    end = FC_SID_SERVERS(client_ctx->cluster.server_cfg) + server_count;\n    for (server=FC_SID_SERVERS(client_ctx->cluster.server_cfg); server<end;\n            server++, conn++)\n    {\n        *conn = server->group_addrs[server_group_index].\n            address_array.addrs[0]->conn;\n    }\n    server_group->count = server_count;\n\n    /*\n    {\n        char formatted_ip[FORMATTED_IP_SIZE];\n        printf(\"auth_server count: %d\\n\", server_group->count);\n        for (conn=server_group->servers; conn<server_group->servers+\n                server_group->count; conn++)\n        {\n            format_ip_address(conn->ip_addr, formatted_ip);\n            printf(\"auth_server=%s:%u\\n\", formatted_ip, conn->port);\n        }\n    }\n    */\n}\n\nint fcfs_auth_simple_connection_manager_init(FCFSAuthClientContext *client_ctx,\n        SFConnectionManager *cm, const int server_group_index)\n{\n    const int max_count_per_entry = 0;\n    const int max_idle_time = 3600;\n    FCFSAuthCMSimpleExtra *extra;\n    FCFSAuthServerGroup *cluster_sarray;\n    int server_count;\n    int htable_init_capacity;\n    int result;\n\n    cluster_sarray = (FCFSAuthServerGroup *)fc_malloc(\n            sizeof(FCFSAuthServerGroup));\n    if (cluster_sarray == NULL) {\n        return ENOMEM;\n    }\n\n    server_count = FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg);\n    if ((result=fcfs_auth_alloc_group_servers(cluster_sarray,\n                    server_count)) != 0)\n    {\n        return result;\n    }\n    copy_to_server_group_array(client_ctx, cluster_sarray, server_group_index);\n\n    extra = (FCFSAuthCMSimpleExtra *)fc_malloc(sizeof(FCFSAuthCMSimpleExtra));\n    if (extra == NULL) {\n        return ENOMEM;\n    }\n    memset(extra, 0, sizeof(FCFSAuthCMSimpleExtra));\n\n    htable_init_capacity = 4 * server_count;\n    if (htable_init_capacity < 256) {\n        htable_init_capacity = 256;\n    }\n    if ((result=conn_pool_init_ex1(&extra->cpool, client_ctx->common_cfg.\n                    connect_timeout, max_count_per_entry, max_idle_time,\n                    htable_init_capacity, NULL, client_ctx,\n                    sf_cm_validate_connection_callback, cm,\n                    sizeof(SFConnectionParameters), NULL)) != 0)\n    {\n        return result;\n    }\n    extra->cluster_sarray = cluster_sarray;\n    extra->client_ctx = client_ctx;\n\n    cm->server_group_index = server_group_index;\n    cm->extra = extra;\n    cm->common_cfg = &client_ctx->common_cfg;\n    cm->ops.get_connection = get_connection;\n    cm->ops.get_spec_connection = get_spec_connection;\n    cm->ops.get_master_connection = get_master_connection;\n    cm->ops.get_readable_connection = get_master_connection;\n    cm->ops.release_connection = release_connection;\n    cm->ops.close_connection = close_connection;\n    cm->ops.get_connection_params = sf_cm_get_connection_params;\n    return 0;\n}\n\nvoid fcfs_auth_simple_connection_manager_destroy(SFConnectionManager *cm)\n{\n    FCFSAuthCMSimpleExtra *extra;\n\n    extra = (FCFSAuthCMSimpleExtra *)cm->extra;\n    if (extra->cluster_sarray != NULL) {\n        if (extra->cluster_sarray->servers != NULL) {\n            free(extra->cluster_sarray->servers);\n            extra->cluster_sarray->servers = NULL;\n            extra->cluster_sarray->count = 0;\n            extra->cluster_sarray->alloc_size = 0;\n        }\n\n        free(extra->cluster_sarray);\n        extra->cluster_sarray = NULL;\n    }\n}\n"
  },
  {
    "path": "src/auth/client/simple_connection_manager.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_AUTH_SIMPLE_CONNECTION_MANAGER_H\n#define _FCFS_AUTH_SIMPLE_CONNECTION_MANAGER_H\n\n#include \"client_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint fcfs_auth_simple_connection_manager_init(FCFSAuthClientContext *client_ctx,\n        SFConnectionManager *cm, const int server_group_index);\n\nvoid fcfs_auth_simple_connection_manager_destroy(SFConnectionManager *cm);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/client/tools/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I../../common -I/usr/local/include\nLIB_PATH = -L.. $(LIBS) -lfcfsauthclient -lfastcommon -lserverframe\nTARGET_PATH = $(TARGET_PREFIX)/bin\n\nSTATIC_OBJS = tool_func.o\n\nALL_PRGS = fcfs_user fcfs_pool fauth_list_servers fauth_cluster_stat\n\nall: $(STATIC_OBJS) $(ALL_PRGS)\n\n.o:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n\ninstall:\n\tmkdir -p $(TARGET_PATH)\n\tcp -f $(ALL_PRGS) $(TARGET_PATH)\n\nclean:\n\trm -f $(STATIC_OBJS) $(ALL_PRGS)\n\n"
  },
  {
    "path": "src/auth/client/tools/fauth_cluster_stat.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"../fcfs_auth_client.h\"\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"Usage: %s [-c config_filename=%s]\\n\",\n            argv[0], FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME);\n}\n\nstatic void output(FCFSAuthClientClusterStatEntry *stats, const int count)\n{\n    FCFSAuthClientClusterStatEntry *stat;\n    FCFSAuthClientClusterStatEntry *end;\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n    end = stats + count;\n    for (stat=stats; stat<end; stat++) {\n        format_ip_address(stat->ip_addr, formatted_ip);\n        printf( \"server_id: %d, host: %s:%u, \"\n                \"is_online: %d, is_master: %d\\n\",\n                stat->server_id, formatted_ip, stat->port,\n                stat->is_online, stat->is_master);\n    }\n    printf(\"\\nserver count: %d\\n\\n\", count);\n}\n\nint main(int argc, char *argv[])\n{\n#define CLUSTER_MAX_SERVER_COUNT  16\n\tint ch;\n    const char *config_filename = FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME;\n    int count;\n    FCFSAuthClientClusterStatEntry stats[CLUSTER_MAX_SERVER_COUNT];\n\tint result;\n\n    while ((ch=getopt(argc, argv, \"hc:\")) != -1) {\n        switch (ch) {\n            case 'h':\n                usage(argv);\n                break;\n            case 'c':\n                config_filename = optarg;\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    log_init();\n    //g_log_context.log_level = LOG_DEBUG;\n\n    if ((result=fcfs_auth_client_init(config_filename)) != 0) {\n        return result;\n    }\n\n    if ((result=fcfs_auth_client_cluster_stat(&g_fcfs_auth_client_vars.client_ctx,\n                    stats, CLUSTER_MAX_SERVER_COUNT, &count)) != 0)\n    {\n        fprintf(stderr, \"fcfs_auth_client_cluster_stat fail, \"\n                \"errno: %d, error info: %s\\n\", result, STRERROR(result));\n        return result;\n    }\n\n    output(stats, count);\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/client/tools/fauth_list_servers.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"sf/sf_cluster_cfg.h\"\n#include \"../client_types.h\"\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"\\nUsage: %s <cluster_config_filename>\\n\", argv[0]);\n}\n\nint main(int argc, char *argv[])\n{\n    const bool share_between_groups = false;\n    const bool calc_sign = false;\n    const char *config_filename;\n    SFClusterConfig cluster;\n    FCServerInfo *server;\n    FCServerInfo *end;\n    FCAddressInfo **addrs;\n    int i;\n    int result;\n\n    if (argc < 2) {\n        usage(argv);\n        return EINVAL;\n    }\n\n    config_filename = argv[1];\n    log_init();\n\n    if ((result=sf_load_cluster_config_by_file(&cluster, config_filename,\n                    FCFS_AUTH_DEFAULT_CLUSTER_PORT, share_between_groups,\n                    calc_sign)) != 0)\n    {\n        return result;\n    }\n\n    printf(\"fauth_group=(\");\n\n    end = FC_SID_SERVERS(cluster.server_cfg) +\n        FC_SID_SERVER_COUNT(cluster.server_cfg);\n    for (server=FC_SID_SERVERS(cluster.server_cfg), i=0;\n            server<end; server++, i++)\n    {\n        addrs = server->group_addrs[cluster.cluster_group_index].\n            address_array.addrs;\n        if (i > 0) {\n            printf(\" \");\n        }\n        printf(\"%s\", (*addrs)->conn.ip_addr);\n    }\n\n    printf(\")\\n\");\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/client/tools/fcfs_pool.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"tool_func.h\"\n#include \"../../common/auth_func.h\"\n#include \"../fcfs_auth_client.h\"\n\n#define PARAM_OPTION_INCLUDED   1\n#define PARAM_OPTION_REQUIRED   2\n\n#define DRY_RUN_OPTION_STR  \"--dryrun\"\n\nconst int mpool_alloc_size_once = 16 * 1024;\nconst int mpool_discard_size = 8;\nconst SFListLimitInfo limit = {0, 0};\n\nstatic int current_index;\nstatic FCFSAuthStoragePoolInfo spool;\nstatic struct {\n    string_t fdir;\n    string_t fstore;\n} privs;\nstatic string_t username = {0};\nstatic bool dryrun;\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"\\nUsage: %s [-c config_filename=%s]\\n\"\n            \"\\t[-u admin_username=admin]\\n\"\n            \"\\t[-k admin_secret_key_filename=/etc/fastcfs/auth/keys/\"\n            \"${admin_username}.key]\\n\"\n            \"\\t[-d fastdir_access=rw]\\n\"\n            \"\\t[-s faststore_access=rw]\\n\"\n            \"\\t<operation> [username] [pool_name] [quota]\\n\\n\"\n            \"\\tthe operations and following parameters: \\n\"\n            \"\\t  create [pool_name] <quota> [%s]\\n\"\n            \"\\t  quota <pool_name> <quota>\\n\"\n            \"\\t  delete | remove <pool_name>\\n\"\n            \"\\t  plist | pool-list [username] [pool_name]\\n\"\n            \"\\t  grant <username> <pool_name>\\n\"\n            \"\\t  cancel | withdraw <username> <pool_name>\\n\"\n            \"\\t  glist | grant-list | granted-list [username] [pool_name]\\n\\n\"\n            \"\\t* the pool name can contain %s for auto generated id when \"\n            \"create pool, such as 'pool-%s'\\n\"\n            \"\\t  the pool name template configurated in the server side \"\n            \"will be used when not specify the pool name\\n\"\n            \"\\t  you can set the initial value of auto increment id and \"\n            \"the pool name template in server.conf of the server side\\n\\n\"\n            \"\\t* the quota parameter is required for create and quota operations\\n\"\n            \"\\t  the default unit of quota is GiB, %s for no limit\\n\\n\"\n            \"\\tFastDIR and FastStore accesses are:\\n\"\n            \"\\t  %c:  read only\\n\"\n            \"\\t  %c:  write only\\n\"\n            \"\\t  %c%c: read and write\\n\\n\",\n            argv[0], FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME,\n            DRY_RUN_OPTION_STR, FCFS_AUTH_AUTO_ID_TAG_STR,\n            FCFS_AUTH_AUTO_ID_TAG_STR, FCFS_AUTH_UNLIMITED_QUOTA_STR,\n            POOL_ACCESS_NAME_READ_CHR, POOL_ACCESS_NAME_WRITE_CHR,\n            POOL_ACCESS_NAME_READ_CHR, POOL_ACCESS_NAME_WRITE_CHR);\n}\n\nstatic int create_spool(int argc, char *argv[])\n{\n    int result;\n    FILE *fp;\n    char name_buff[NAME_MAX];\n    char prompt[32];\n\n    if (spool.name.len > sizeof(name_buff)) {\n        fprintf(stderr, \"pool name length: %d is too large, exceeds %d\",\n                spool.name.len, (int)sizeof(name_buff));\n        return ENAMETOOLONG;\n    }\n\n    memcpy(name_buff, spool.name.str, spool.name.len);\n    spool.name.str = name_buff;\n    if ((result=fcfs_auth_client_spool_create(&g_fcfs_auth_client_vars.\n                    client_ctx, &spool, sizeof(name_buff), dryrun)) == 0)\n    {\n        strcpy(prompt, \"success\");\n        fp = stdout;\n    } else {\n        strcpy(prompt, \"fail\");\n        fp = stderr;\n    }\n\n    fprintf(fp, \"%screate pool %.*s %s\\n\", (dryrun ? \"[DRYRUN] \" : \"\"),\n            spool.name.len, spool.name.str, prompt);\n    return result;\n}\n\nstatic int quota_spool(int argc, char *argv[])\n{\n    int result;\n    char buff[64];\n\n    if ((result=fcfs_auth_client_spool_set_quota(&g_fcfs_auth_client_vars.\n                    client_ctx, &spool.name, spool.quota)) == 0)\n    {\n        if (spool.quota == FCFS_AUTH_UNLIMITED_QUOTA_VAL) {\n            strcpy(buff, FCFS_AUTH_UNLIMITED_QUOTA_STR);\n        } else {\n            long_to_comma_str(spool.quota / (1024 * 1024 * 1024), buff);\n            strcat(buff, \"GiB\");\n        }\n        printf(\"pool %s, set quota to %s success\\n\", spool.name.str, buff);\n    } else {\n        fprintf(stderr, \"pool %s, set quota fail\\n\", spool.name.str);\n    }\n\n    return result;\n}\n\nstatic inline int parse_pool_access(const string_t *privs,\n        const char *caption, int *pv)\n{\n    if (privs->str == NULL) {\n         *pv = FCFS_AUTH_POOL_ACCESS_ALL;\n         return 0;\n    } else {\n        return fcfs_auth_parse_pool_access(privs, pv);\n    }\n}\n\nstatic int grant_privilege(int argc, char *argv[])\n{\n    int result;\n    FILE *fp;\n    char prompt[32];\n    FCFSAuthSPoolPriviledges pvs;\n\n    if ((result=parse_pool_access(&privs.fdir, \"FastDIR\", &pvs.fdir)) != 0) {\n        usage(argv);\n        return result;\n    }\n\n    if ((result=parse_pool_access(&privs.fstore, \"FastStore\",\n                    &pvs.fstore)) != 0)\n    {\n        usage(argv);\n        return result;\n    }\n\n    if ((pvs.fdir == FCFS_AUTH_POOL_ACCESS_NONE) && \n            (pvs.fstore == FCFS_AUTH_POOL_ACCESS_NONE))\n    {\n        fprintf(stderr, \"no access priviledges to grant\\n\");\n        usage(argv);\n        return ENOENT;\n    }\n\n    if ((result=fcfs_auth_client_gpool_grant(&g_fcfs_auth_client_vars.\n                    client_ctx, &username, &spool.name, &pvs)) == 0)\n    {\n        strcpy(prompt, \"success\");\n        fp = stdout;\n    } else {\n        strcpy(prompt, \"fail\");\n        fp = stderr;\n    }\n\n    fprintf(fp, \"grant access priviledge of pool %.*s to user %.*s %s\\n\",\n            spool.name.len, spool.name.str, username.len, username.str, prompt);\n    return result;\n}\n\nstatic int withdraw_privilege(int argc, char *argv[])\n{\n    int result;\n    FILE *fp;\n    char prompt[32];\n\n    if ((result=fcfs_auth_client_gpool_withdraw(&g_fcfs_auth_client_vars.\n                    client_ctx, &username, &spool.name)) == 0)\n    {\n        strcpy(prompt, \"success\");\n        fp = stdout;\n    } else {\n        strcpy(prompt, \"fail\");\n        fp = stderr;\n    }\n\n    fprintf(fp, \"withdraw access priviledge of pool %.*s from user %.*s %s\\n\",\n            spool.name.len, spool.name.str, username.len, username.str, prompt);\n    return result;\n}\n\nstatic int remove_spool(int argc, char *argv[])\n{\n    int result;\n\n    if ((result=fcfs_auth_client_spool_remove(&g_fcfs_auth_client_vars.\n                    client_ctx, &spool.name)) == 0)\n    {\n        printf(\"remove pool %s success\\n\", spool.name.str);\n    } else {\n        fprintf(stderr, \"remove pool %s fail\\n\", spool.name.str);\n    }\n\n    return result;\n}\n\nstatic void output_spools(FCFSAuthStoragePoolArray *array)\n{\n    FCFSAuthStoragePoolInfo *spool;\n    FCFSAuthStoragePoolInfo *end;\n    char buff[64];\n\n    printf(\"%5s %50s %16s\\n\", \"No.\", \"pool_name\", \"quota (GiB)\");\n    end = array->spools + array->count;\n    for (spool=array->spools; spool<end; spool++) {\n        if (spool->quota == FCFS_AUTH_UNLIMITED_QUOTA_VAL) {\n            strcpy(buff, FCFS_AUTH_UNLIMITED_QUOTA_STR);\n        } else {\n            long_to_comma_str(spool->quota / (1024 * 1024 * 1024), buff);\n        }\n        printf(\"%4d. %50.*s %16s\\n\", (int)((spool - array->spools) + 1),\n                spool->name.len, spool->name.str, buff);\n    }\n}\n\nstatic void output_gpools(FCFSAuthGrantedPoolArray *array)\n{\n    FCFSAuthGrantedPoolFullInfo *gpool;\n    FCFSAuthGrantedPoolFullInfo *end;\n    char buff1[64];\n    char buff2[64];\n    string_t priv_names_fdir;\n    string_t priv_names_fstore;\n\n    printf(\"%5s %32s %50s %16s %16s\\n\", \"No.\", \"owner_name\", \"pool_name\",\n            \"fdir_priv\", \"fstore_priv\");\n    priv_names_fdir.str = buff1;\n    priv_names_fstore.str = buff2;\n    end = array->gpools + array->count;\n    for (gpool=array->gpools; gpool<end; gpool++) {\n        printf(\"%4d. %32.*s %50.*s %16s %16s\\n\",\n                (int)((gpool - array->gpools) + 1),\n                gpool->username.len, gpool->username.str,\n                gpool->pool_name.len, gpool->pool_name.str,\n                fcfs_auth_pool_access_to_string(gpool->\n                    granted.privs.fdir, &priv_names_fdir),\n                fcfs_auth_pool_access_to_string(gpool->\n                    granted.privs.fstore, &priv_names_fstore));\n    }\n}\n\nstatic int list_spool(int argc, char *argv[])\n{\n    struct fast_mpool_man mpool;\n    FCFSAuthStoragePoolArray spool_array;\n    int result;\n\n    if ((result=fast_mpool_init(&mpool, mpool_alloc_size_once,\n                    mpool_discard_size)) != 0)\n    {\n        return result;\n    }\n\n    fcfs_auth_spool_init_array(&spool_array);\n    if ((result=fcfs_auth_client_spool_list(&g_fcfs_auth_client_vars.\n                    client_ctx, &username, &spool.name, &limit,\n                    &mpool, &spool_array)) == 0)\n    {\n        output_spools(&spool_array);\n    } else {\n        fprintf(stderr, \"list storage pool fail\\n\");\n    }\n\n    fcfs_auth_spool_free_array(&spool_array);\n    fast_mpool_destroy(&mpool);\n    return result;\n}\n\nstatic int list_gpool(int argc, char *argv[])\n{\n    struct fast_mpool_man mpool;\n    FCFSAuthGrantedPoolArray gpool_array;\n    int result;\n\n    if ((result=fast_mpool_init(&mpool, mpool_alloc_size_once,\n                    mpool_discard_size)) != 0)\n    {\n        return result;\n    }\n\n    fcfs_auth_granted_init_array(&gpool_array);\n    if ((result=fcfs_auth_client_gpool_list(&g_fcfs_auth_client_vars.\n                    client_ctx, &username, &spool.name, &limit,\n                    &mpool, &gpool_array)) == 0)\n    {\n        output_gpools(&gpool_array);\n    } else {\n        fprintf(stderr, \"list storage pool fail\\n\");\n    }\n\n    fcfs_auth_granted_free_array(&gpool_array);\n    fast_mpool_destroy(&mpool);\n    return result;\n}\n\nint main(int argc, char *argv[])\n{\n    int ch;\n    int op_type;\n    const char *config_filename = FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME;\n    char *operation;\n    FCFSAuthClientUserKeyPair admin;\n    unsigned char passwd_buff[FCFS_AUTH_PASSWD_LEN + 1];\n    FilenameString admin_key_filename;\n    string_t passwd;\n    int username_options;\n    int poolname_options;\n    bool need_quota;\n    int result;\n\n    if (argc < 2) {\n        usage(argv);\n        return 1;\n    }\n\n    FC_SET_STRING(admin.username, \"admin\");\n    FC_SET_STRING(admin.key_filename,\n            \"/etc/fastcfs/auth/keys/${username}.key\");\n    FC_SET_STRING_NULL(privs.fdir);\n    FC_SET_STRING_NULL(privs.fstore);\n    while ((ch=getopt(argc, argv, \"hc:u:k:d:s:\")) != -1) {\n        switch (ch) {\n            case 'h':\n                usage(argv);\n                return 0;\n            case 'c':\n                config_filename = optarg;\n                break;\n            case 'u':\n                FC_SET_STRING_EX(admin.username, optarg, strlen(optarg));\n                break;\n            case 'k':\n                FC_SET_STRING_EX(admin.key_filename, optarg, strlen(optarg));\n                break;\n            case 'd':\n                FC_SET_STRING(privs.fdir, optarg);\n                break;\n            case 's':\n                FC_SET_STRING(privs.fstore, optarg);\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    current_index = optind;\n    if (current_index >= argc) {\n        fprintf(stderr, \"expect operation\\n\");\n        usage(argv);\n        return 1;\n    }\n\n    srand(time(NULL));\n    log_init();\n    //g_log_context.log_level = LOG_DEBUG;\n\n    operation = argv[current_index++];\n    if (current_index < argc) {\n        char *last;\n        last = argv[argc - 1];\n        dryrun = (strcasecmp(last, DRY_RUN_OPTION_STR) == 0);\n        if (dryrun) {\n            --argc;  //remove the last parameter\n        }\n    }\n\n    if (strcasecmp(operation, \"create\") == 0) {\n        op_type = FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ;\n        username_options = 0;\n        if (current_index + 1 < argc) {\n            poolname_options = PARAM_OPTION_INCLUDED |\n                PARAM_OPTION_REQUIRED;\n        } else {\n            poolname_options = 0;\n        }\n        need_quota = true;\n    } else if (strcasecmp(operation, \"grant\") == 0) {\n        op_type = FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ;\n        username_options = PARAM_OPTION_INCLUDED |\n            PARAM_OPTION_REQUIRED;\n        poolname_options = PARAM_OPTION_INCLUDED |\n            PARAM_OPTION_REQUIRED;\n        need_quota = false;\n    } else if (strcasecmp(operation, \"quota\") == 0) {\n        op_type = FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ;\n        username_options = 0;\n        poolname_options = PARAM_OPTION_INCLUDED |\n            PARAM_OPTION_REQUIRED;\n        need_quota = true;\n    } else if (strcasecmp(operation, \"delete\") == 0 ||\n            strcasecmp(operation, \"remove\") == 0)\n    {\n        op_type = FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ;\n        username_options = 0;\n        poolname_options = PARAM_OPTION_INCLUDED |\n            PARAM_OPTION_REQUIRED;\n        need_quota = false;\n    } else if (strcasecmp(operation, \"plist\") == 0 ||\n            strcasecmp(operation, \"pool-list\") == 0 ||\n            strcasecmp(operation, \"pool_list\") == 0)\n    {\n        op_type = FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ;\n        username_options = PARAM_OPTION_INCLUDED;\n        poolname_options = PARAM_OPTION_INCLUDED;\n        need_quota = false;\n    } else if (strcasecmp(operation, \"withdraw\") == 0 ||\n            strcasecmp(operation, \"cancel\") == 0)\n    {\n        op_type = FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ;\n        username_options = PARAM_OPTION_INCLUDED |\n            PARAM_OPTION_REQUIRED;\n        poolname_options = PARAM_OPTION_INCLUDED |\n            PARAM_OPTION_REQUIRED;\n        need_quota = false;\n    } else if (strcasecmp(operation, \"glist\") == 0 ||\n            strcasecmp(operation, \"grant-list\") == 0 ||\n            strcasecmp(operation, \"grant_list\") == 0 ||\n            strcasecmp(operation, \"granted-list\") == 0 ||\n            strcasecmp(operation, \"granted_list\") == 0)\n    {\n        op_type = FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ;\n        username_options = PARAM_OPTION_INCLUDED;\n        poolname_options = PARAM_OPTION_INCLUDED;\n        need_quota = false;\n    } else if (strcasecmp(operation, \"config-setid\") == 0 ||\n            strcasecmp(operation, \"config_setid\") == 0 ||\n            strcasecmp(operation, \"cfg-setid\") == 0 ||\n            strcasecmp(operation, \"cfg_setid\") == 0)\n    {\n        op_type = FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ;\n        username_options = PARAM_OPTION_INCLUDED;\n        poolname_options = PARAM_OPTION_INCLUDED;\n        need_quota = false;\n    } else {\n        fprintf(stderr, \"unknow operation: %s\\n\", operation);\n        usage(argv);\n        return 1;\n    }\n\n    if ((username_options & PARAM_OPTION_INCLUDED) != 0) {\n        if (current_index < argc) {\n            FC_SET_STRING(username, argv[current_index]);\n            current_index++;\n        } else if ((username_options & PARAM_OPTION_REQUIRED) != 0) {\n            fprintf(stderr, \"expect username\\n\");\n            usage(argv);\n            return 1;\n        }\n    }\n\n    if ((poolname_options & PARAM_OPTION_INCLUDED) != 0) {\n        if (current_index < argc) {\n            FC_SET_STRING(spool.name, argv[current_index]);\n            current_index++;\n        } else if ((poolname_options & PARAM_OPTION_REQUIRED) != 0) {\n            fprintf(stderr, \"expect pool name\\n\");\n            usage(argv);\n            return 1;\n        } else {\n            FC_SET_STRING_NULL(spool.name);\n        }\n    }\n\n    if (current_index < argc) {\n        if (strcasecmp(argv[current_index],\n                    FCFS_AUTH_UNLIMITED_QUOTA_STR) == 0)\n        {\n            spool.quota = FCFS_AUTH_UNLIMITED_QUOTA_VAL;\n        } else if ((result=parse_bytes(argv[current_index],\n                        1024 * 1024 * 1024, &spool.quota)) != 0)\n        {\n            fprintf(stderr, \"parse quota field fail!\\n\");\n            if (op_type == FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ) {\n                fprintf(stderr, \"\\nUsage: %s create [pool_name] <quota> \"\n                        \"[%s]\\nFor example: %s create %s %s %s\\n\\n\",\n                        argv[0], DRY_RUN_OPTION_STR, argv[0],\n                        (spool.name.str != NULL ? spool.name.str :\n                         argv[current_index]), FCFS_AUTH_UNLIMITED_QUOTA_STR,\n                        (dryrun ? DRY_RUN_OPTION_STR : \"\"));\n            }\n            return 1;\n        }\n\n        current_index++;\n    } else if (need_quota) {\n        fprintf(stderr, \"expect quota\\n\");\n        usage(argv);\n        return 1;\n    }\n\n    passwd.str = (char *)passwd_buff;\n    passwd.len = FCFS_AUTH_PASSWD_LEN;\n    fcfs_auth_replace_filename_with_username(&admin.key_filename,\n            &admin.username, &admin_key_filename);\n    if ((result=fcfs_auth_load_passwd(\n                    FC_FILENAME_STRING_PTR(admin_key_filename),\n                    passwd_buff)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_auth_client_init(config_filename)) != 0) {\n        return result;\n    }\n\n    if ((result=fcfs_auth_client_user_login(&g_fcfs_auth_client_vars.\n                    client_ctx, &admin.username, &passwd)) != 0)\n    {\n        return result;\n    }\n\n    switch (op_type) {\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ:\n            return create_spool(argc, argv);\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ:\n            return list_spool(argc, argv);\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ:\n            return remove_spool(argc, argv);\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ:\n            return quota_spool(argc, argv);\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ:\n            return grant_privilege(argc, argv);\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ:\n            return withdraw_privilege(argc, argv);\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ:\n            return list_gpool(argc, argv);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/client/tools/fcfs_user.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"tool_func.h\"\n#include \"../../common/auth_func.h\"\n#include \"../fcfs_auth_client.h\"\n\nconst int mpool_alloc_size_once = 16 * 1024;\nconst int mpool_discard_size = 8;\nconst SFListLimitInfo limit = {0, 0};\n\nstatic int current_index;\nstatic FCFSAuthUserInfo user;\nstatic bool priv_set = false;\nstatic bool confirmed = false;  //for option -y\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"\\nUsage: %s [-c config_filename=%s]\\n\"\n            \"\\t[-u admin_username=admin]\\n\"\n            \"\\t[-k admin_secret_key_filename=/etc/fastcfs/auth/keys/\"\n            \"${username}.key]\\n\"\n            \"\\t[-p priviledges=%s]\\n\"\n            \"\\t<operation> [username]\\n\"\n            \"\\t[user_secret_key_filename=keys/${username}.key]\\n\\n\"\n            \"\\tthe operations and parameters are: \\n\"\n            \"\\t  create <username> [user_secret_key_filename]\\n\"\n            \"\\t  passwd | secret-key <username> [user_secret_key_filename] \"\n            \"[-y]: regenerate user's secret key\\n\"\n            \"\\t  grant <username>, the option <-p priviledges> is required\\n\"\n            \"\\t  delete | remove <username>\\n\"\n            \"\\t  list [username]\\n\\n\"\n            \"\\t[user_secret_key_filename]: specify the filename to store the \"\n            \"generated secret key of the user\\n\"\n            \"\\t[priviledges]: the granted priviledges seperate by comma, \"\n            \"priviledges:\\n\"\n            \"\\t  %s: user management\\n\"\n            \"\\t  %s: create storage pool\\n\"\n            \"\\t  %s: monitor cluster\\n\"\n            \"\\t  %s: subscribe session for FastDIR and FastStore server side\\n\"\n            \"\\t  %s: for all priviledges\\n\\n\",\n            argv[0], FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME,\n            USER_PRIV_NAME_CREATE_POOL_STR,\n            USER_PRIV_NAME_USER_MANAGE_STR,\n            USER_PRIV_NAME_CREATE_POOL_STR,\n            USER_PRIV_NAME_MONITOR_CLUSTER_STR,\n            USER_PRIV_NAME_SUBSCRIBE_SESSION_STR,\n            USER_PRIV_NAME_ALL_PRIVS_STR);\n}\n\nstatic int user_create_or_passwd(int argc, char *argv[], const int cmd)\n{\n    string_t input_key_filename;\n    FilenameString user_key_filename;\n    unsigned char passwd_buff[FCFS_AUTH_PASSWD_LEN + 1];\n    const char *caption;\n    char *filename;\n    char abs_path[PATH_MAX];\n    int result;\n\n    if (current_index < argc) {\n        FC_SET_STRING(input_key_filename, argv[current_index]);\n    } else {\n        FC_SET_STRING(input_key_filename, \"keys/${username}.key\");\n    }\n\n    fcfs_auth_replace_filename_with_username(&input_key_filename,\n            &user.name, &user_key_filename);\n\n    filename = FC_FILENAME_STRING_PTR(user_key_filename);\n    getAbsolutePath(filename, abs_path, sizeof(abs_path));\n    if ((result=fc_mkdirs(abs_path, 0755)) != 0) {\n        return result;\n    }\n\n    fcfs_auth_generate_passwd(passwd_buff);\n    FC_SET_STRING_EX(user.passwd, (char *)passwd_buff, FCFS_AUTH_PASSWD_LEN);\n\n    if (cmd == FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ) {\n        caption = \"create user\";\n        result = fcfs_auth_client_user_create(&g_fcfs_auth_client_vars.\n                client_ctx, &user);\n    } else {\n        caption = \"regenerate secret key for user\";\n        result = fcfs_auth_client_user_passwd(&g_fcfs_auth_client_vars.\n                client_ctx, &user.name, &user.passwd);\n    }\n    if (result == 0) {\n        if ((result=fcfs_auth_save_passwd(filename, passwd_buff)) == 0) {\n            printf(\"%s %s success, secret key store to file: %s\\n\",\n                    caption, user.name.str, filename);\n        } else {\n            char hex_buff[2 * FCFS_AUTH_PASSWD_LEN + 1];\n\n            bin2hex((char *)passwd_buff, FCFS_AUTH_PASSWD_LEN, hex_buff);\n            printf(\"%s %s success, but secret key store to \"\n                    \"file: %s fail, the secret key is:\\n%s\\n\",\n                    caption, user.name.str, filename, hex_buff);\n        }\n    } else {\n        fprintf(stderr, \"%s %s fail\\n\", caption, user.name.str);\n    }\n\n    return result;\n}\n\nstatic inline int create_user(int argc, char *argv[])\n{\n    return user_create_or_passwd(argc, argv,\n            FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ);\n}\n\nstatic inline int passwd_user(int argc, char *argv[])\n{\n    return user_create_or_passwd(argc, argv,\n            FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ);\n}\n\nstatic int grant_privilege(int argc, char *argv[])\n{\n    int result;\n\n    if (!priv_set) {\n        fprintf(stderr, \"expect parameter: granted priviledges!\\n\");\n        usage(argv);\n        return EINVAL;\n    }\n\n    if ((result=fcfs_auth_client_user_grant(&g_fcfs_auth_client_vars.\n                    client_ctx, &user.name, user.priv)) == 0)\n    {\n        printf(\"grant user priviledge success\\n\");\n    } else {\n        fprintf(stderr, \"grant user priviledge fail\\n\");\n    }\n\n    return result;\n}\n\nstatic int remove_user(int argc, char *argv[])\n{\n    int result;\n\n    if ((result=fcfs_auth_client_user_remove(&g_fcfs_auth_client_vars.\n                    client_ctx, &user.name)) == 0)\n    {\n        printf(\"remove user %s success\\n\", user.name.str);\n    } else {\n        fprintf(stderr, \"remove user %s fail\\n\", user.name.str);\n    }\n\n    return result;\n}\n\nstatic void output_users(FCFSAuthUserArray *array)\n{\n    FCFSAuthUserInfo *user;\n    FCFSAuthUserInfo *end;\n    char buff[256];\n    string_t priv_names;\n\n    printf(\"%5s %32s %32s\\n\", \"No.\", \"username\", \"priviledges\");\n    priv_names.str = buff;\n    end = array->users + array->count;\n    for (user=array->users; user<end; user++) {\n        printf(\"%4d. %32.*s %32s\\n\", (int)((user - array->users) + 1),\n                user->name.len, user->name.str,\n                fcfs_auth_user_priv_to_string(user->priv, &priv_names));\n    }\n}\n\nstatic int list_user(int argc, char *argv[])\n{\n    struct fast_mpool_man mpool;\n    FCFSAuthUserArray user_array;\n    int result;\n\n    if ((result=fast_mpool_init(&mpool, mpool_alloc_size_once,\n                    mpool_discard_size)) != 0)\n    {\n        return result;\n    }\n\n    fcfs_auth_user_init_array(&user_array);\n    if ((result=fcfs_auth_client_user_list(&g_fcfs_auth_client_vars.\n                    client_ctx, &user.name, &limit, &mpool,\n                    &user_array)) == 0)\n    {\n        output_users(&user_array);\n    } else {\n        fprintf(stderr, \"list user fail\\n\");\n    }\n\n    fcfs_auth_user_free_array(&user_array);\n    fast_mpool_destroy(&mpool);\n    return result;\n}\n\nint main(int argc, char *argv[])\n{\n    int ch;\n    const char *config_filename = FCFS_AUTH_CLIENT_DEFAULT_CONFIG_FILENAME;\n    char *operation;\n    FCFSAuthClientUserKeyPair admin;\n    unsigned char passwd_buff[FCFS_AUTH_PASSWD_LEN + 1];\n    FilenameString admin_key_filename;\n    string_t passwd;\n    string_t privs;\n    bool need_username;\n    int result;\n\n    if (argc < 2) {\n        usage(argv);\n        return 1;\n    }\n\n    FC_SET_STRING(admin.username, \"admin\");\n    FC_SET_STRING(admin.key_filename,\n            \"/etc/fastcfs/auth/keys/${username}.key\");\n    FC_SET_STRING_NULL(privs);\n    while ((ch=getopt(argc, argv, \"hc:u:k:p:y\")) != -1) {\n        switch (ch) {\n            case 'h':\n                usage(argv);\n                return 0;\n            case 'c':\n                config_filename = optarg;\n                break;\n            case 'u':\n                FC_SET_STRING_EX(admin.username, optarg, strlen(optarg));\n                break;\n            case 'k':\n                FC_SET_STRING_EX(admin.key_filename, optarg, strlen(optarg));\n                break;\n            case 'p':\n                FC_SET_STRING(privs, optarg);\n                break;\n            case 'y':\n                confirmed = true;\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    current_index = optind;\n    if (current_index >= argc) {\n        fprintf(stderr, \"expect operation\\n\");\n        usage(argv);\n        return 1;\n    }\n\n    srand(time(NULL));\n    log_init();\n    //g_log_context.log_level = LOG_DEBUG;\n\n    operation = argv[current_index++];\n    if (strcasecmp(operation, \"create\") == 0) {\n        need_username = true;\n    } else if (strcasecmp(operation, \"passwd\") == 0 ||\n            strcasecmp(operation, \"secret-key\") == 0)\n    {\n        need_username = true;\n    } else if (strcasecmp(operation, \"grant\") == 0) {\n        need_username = true;\n    } else if (strcasecmp(operation, \"delete\") == 0 ||\n            strcasecmp(operation, \"remove\") == 0)\n    {\n        need_username = true;\n    } else if (strcasecmp(operation, \"list\") == 0) {\n        need_username = false;\n    } else {\n        fprintf(stderr, \"unknow operation: %s\\n\", operation);\n        usage(argv);\n        return 1;\n    }\n\n    if (current_index < argc) {\n        FC_SET_STRING(user.name, argv[current_index]);\n        current_index++;\n    } else if (need_username) {\n        fprintf(stderr, \"expect username\\n\");\n        usage(argv);\n        return 1;\n    } else {\n        FC_SET_STRING_NULL(user.name);\n    }\n\n    if (privs.str == NULL) {\n        user.priv = FCFS_AUTH_USER_PRIV_CREATE_POOL;\n    } else {\n        if ((result=fcfs_auth_parse_user_priv(&privs, &user.priv)) != 0) {\n            usage(argv);\n            return 1;\n        }\n        priv_set = true;\n    }\n\n    passwd.str = (char *)passwd_buff;\n    passwd.len = FCFS_AUTH_PASSWD_LEN;\n    fcfs_auth_replace_filename_with_username(&admin.key_filename,\n            &admin.username, &admin_key_filename);\n    if ((result=fcfs_auth_load_passwd(\n                    FC_FILENAME_STRING_PTR(admin_key_filename),\n                    passwd_buff)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_auth_client_init(config_filename)) != 0) {\n        return result;\n    }\n\n    if ((result=fcfs_auth_client_user_login(&g_fcfs_auth_client_vars.\n                    client_ctx, &admin.username, &passwd)) != 0)\n    {\n        return result;\n    }\n\n    if (strcasecmp(operation, \"create\") == 0) {\n        return create_user(argc, argv);\n    } else if (strcasecmp(operation, \"passwd\") == 0 ||\n            strcasecmp(operation, \"secret-key\") == 0)\n    {\n        if (fc_string_equal(&admin.username, &user.name) && !confirmed) {\n            fprintf(stderr, \"you MUST use -y option to regenerate \"\n                    \"the secret key \\nwhen the user is same with \"\n                    \"the admin user!\\n\\n\");\n            return EAGAIN;\n        }\n        return passwd_user(argc, argv);\n    } else if (strcasecmp(operation, \"grant\") == 0) {\n        return grant_privilege(argc, argv);\n    } else if (strcasecmp(operation, \"delete\") == 0 ||\n            strcasecmp(operation, \"remove\") == 0)\n    {\n        return remove_user(argc, argv);\n    } else if (strcasecmp(operation, \"list\") == 0) {\n        return list_user(argc, argv);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/client/tools/tool_func.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"tool_func.h\"\n\n#define FCFS_USER_PRIV_ARRAY_COUNT  (FCFS_AUTH_USER_PRIV_COUNT + 1)\n\nstatic id_name_pair_t user_priv_list[FCFS_USER_PRIV_ARRAY_COUNT] = {\n    {FCFS_AUTH_USER_PRIV_USER_MANAGE,\n        {USER_PRIV_NAME_USER_MANAGE_STR,\n            USER_PRIV_NAME_USER_MANAGE_LEN}},\n\n    {FCFS_AUTH_USER_PRIV_CREATE_POOL,\n        {USER_PRIV_NAME_CREATE_POOL_STR,\n            USER_PRIV_NAME_CREATE_POOL_LEN}},\n\n    {FCFS_AUTH_USER_PRIV_SUBSCRIBE_SESSION,\n        {USER_PRIV_NAME_SUBSCRIBE_SESSION_STR,\n            USER_PRIV_NAME_SUBSCRIBE_SESSION_LEN}},\n\n    {FCFS_AUTH_USER_PRIV_MONITOR_CLUSTER,\n        {USER_PRIV_NAME_MONITOR_CLUSTER_STR,\n            USER_PRIV_NAME_MONITOR_CLUSTER_LEN}},\n\n    {FCFS_AUTH_USER_PRIV_ALL,\n        {USER_PRIV_NAME_ALL_PRIVS_STR,\n            USER_PRIV_NAME_ALL_PRIVS_LEN}}\n};\n\nstatic inline int64_t fcfs_auth_get_user_priv(const string_t *str)\n{\n    id_name_pair_t *pair;\n    id_name_pair_t *end;\n\n    end = user_priv_list + FCFS_USER_PRIV_ARRAY_COUNT;\n    for (pair=user_priv_list; pair<end; pair++) {\n        if (fc_string_case_equal(&pair->name, str)) {\n            return pair->id;\n        }\n    }\n\n    return FCFS_AUTH_USER_PRIV_NONE;\n}\n\nstatic inline const string_t *fcfs_auth_get_user_priv_name(const int64_t priv)\n{\n    id_name_pair_t *pair;\n    id_name_pair_t *end;\n\n    end = user_priv_list + FCFS_USER_PRIV_ARRAY_COUNT;\n    for (pair=user_priv_list; pair<end; pair++) {\n        if (pair->id == priv) {\n            return &pair->name;\n        }\n    }\n\n    return NULL;\n}\n\nint fcfs_auth_parse_user_priv(const string_t *str, int64_t *priv)\n{\n    const bool ignore_empty = true;\n    string_t parts[2 * FCFS_AUTH_USER_PRIV_COUNT];\n    string_t *p;\n    string_t *end;\n    int64_t n;\n    int count;\n\n    *priv = 0;\n    count = split_string_ex(str, ',', parts, sizeof(parts) /\n            sizeof(string_t), ignore_empty);\n    end = parts + count;\n    for (p=parts; p<end; p++) {\n        if ((n=fcfs_auth_get_user_priv(p)) == \n                FCFS_AUTH_USER_PRIV_NONE)\n        {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"unkown priviledge: %*s\", __LINE__,\n                    p->len, p->str);\n            return EINVAL;\n        }\n\n        *priv |= n;\n    }\n\n    return 0;\n}\n\nconst char *fcfs_auth_user_priv_to_string(\n        const int64_t priv, string_t *str)\n{\n    id_name_pair_t *pair;\n    id_name_pair_t *end;\n    char *p;\n    const string_t *name;\n\n    if ((name=fcfs_auth_get_user_priv_name(priv)) != NULL) {\n        memcpy(str->str, name->str, name->len);\n        str->len = name->len;\n    } else {\n        p = str->str;\n        end = user_priv_list + FCFS_USER_PRIV_ARRAY_COUNT;\n        for (pair=user_priv_list; pair<end; pair++) {\n            if ((priv & pair->id) == pair->id) {\n                memcpy(p, pair->name.str, pair->name.len);\n                p += pair->name.len;\n                *p++ = ',';\n            }\n        }\n\n        str->len = p - str->str;\n        if (str->len > 0) {\n            str->len--;  //remove last comma\n        }\n    }\n\n    *(str->str + str->len) = '\\0';\n    return str->str;\n}\n\nint fcfs_auth_parse_pool_access(const string_t *str, int *priv)\n{\n    const char *p;\n    const char *end;\n\n    *priv = FCFS_AUTH_POOL_ACCESS_NONE;\n    end = str->str + str->len;\n    for (p=str->str; p<end; p++) {\n        switch (*p) {\n            case POOL_ACCESS_NAME_READ_CHR:\n                *priv |= FCFS_AUTH_POOL_ACCESS_READ;\n                break;\n            case POOL_ACCESS_NAME_WRITE_CHR:\n                *priv |= FCFS_AUTH_POOL_ACCESS_WRITE;\n                break;\n            default:\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"unkown pool access: %c (0x%02x)\",\n                        __LINE__, *p, *p);\n                return EINVAL;\n        }\n    }\n\n    return 0;\n}\n\nconst char *fcfs_auth_pool_access_to_string(const int priv, string_t *str)\n{\n    char *p;\n\n    p = str->str;\n    if ((priv & FCFS_AUTH_POOL_ACCESS_READ) != 0) {\n        *p++ = POOL_ACCESS_NAME_READ_CHR;\n    }\n    if ((priv & FCFS_AUTH_POOL_ACCESS_WRITE) != 0) {\n        *p++ = POOL_ACCESS_NAME_WRITE_CHR;\n    }\n\n    *p = '\\0';\n    str->len = p - str->str;\n    return str->str;\n}\n"
  },
  {
    "path": "src/auth/client/tools/tool_func.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_AUTH_TOOL_FUNC_H\n#define _FCFS_AUTH_TOOL_FUNC_H\n\n#include \"sf/sf_proto.h\"\n#include \"auth_types.h\"\n\n#define USER_PRIV_NAME_USER_MANAGE_STR       \"user\"\n#define USER_PRIV_NAME_CREATE_POOL_STR       \"pool\"\n#define USER_PRIV_NAME_SUBSCRIBE_SESSION_STR \"session\"\n#define USER_PRIV_NAME_MONITOR_CLUSTER_STR   \"cluster\"\n#define USER_PRIV_NAME_ALL_PRIVS_STR         \"*\"\n\n#define USER_PRIV_NAME_USER_MANAGE_LEN  \\\n    (sizeof(USER_PRIV_NAME_USER_MANAGE_STR) - 1)\n\n#define USER_PRIV_NAME_CREATE_POOL_LEN  \\\n    (sizeof(USER_PRIV_NAME_CREATE_POOL_STR) - 1)\n\n#define USER_PRIV_NAME_SUBSCRIBE_SESSION_LEN  \\\n    (sizeof(USER_PRIV_NAME_SUBSCRIBE_SESSION_STR) - 1)\n\n#define USER_PRIV_NAME_MONITOR_CLUSTER_LEN  \\\n    (sizeof(USER_PRIV_NAME_MONITOR_CLUSTER_STR) - 1)\n\n#define USER_PRIV_NAME_ALL_PRIVS_LEN  \\\n    (sizeof(USER_PRIV_NAME_ALL_PRIVS_STR) - 1)\n\n\n#define POOL_ACCESS_NAME_READ_CHR       'r'\n#define POOL_ACCESS_NAME_WRITE_CHR      'w'\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    int fcfs_auth_parse_user_priv(const string_t *str, int64_t *priv);\n\n    const char *fcfs_auth_user_priv_to_string(\n            const int64_t priv, string_t *str);\n\n    int fcfs_auth_parse_pool_access(const string_t *str, int *priv);\n\n    const char *fcfs_auth_pool_access_to_string(const int priv, string_t *str);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/common/auth_func.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"fastcommon/system_info.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"fastcommon/md5.h\"\n#include \"fastcommon/http_func.h\"\n#include \"sf/sf_global.h\"\n#include \"auth_func.h\"\n\nvoid fcfs_auth_generate_passwd(unsigned char passwd[16])\n{\n    struct {\n        time_t current_time;\n        pid_t pid;\n        int random;\n#if defined(OS_LINUX) || defined(OS_FREEBSD)\n        struct fast_sysinfo si;\n#endif\n        int hash_codes[4];\n    } input;\n\n    input.current_time = get_current_time();\n    input.pid = getpid();\n    input.random = rand();\n\n#if defined(OS_LINUX) || defined(OS_FREEBSD)\n    get_sysinfo(&input.si);\n#endif\n\n    /*\n       logInfo(\"procs: %d, pid: %d, random: %d\",\n        input.si.procs, input.pid, input.random);\n       */\n\n    INIT_HASH_CODES4(input.hash_codes);\n    CALC_HASH_CODES4(&input, sizeof(input), input.hash_codes);\n    FINISH_HASH_CODES4(input.hash_codes);\n\n    my_md5_buffer((char *)&input, sizeof(input), passwd);\n}\n\nint fcfs_auth_save_passwd(const char *filename, unsigned char passwd[16])\n{\n    char hex_buff[2 * FCFS_AUTH_PASSWD_LEN + 1];\n\n    bin2hex((char *)passwd, FCFS_AUTH_PASSWD_LEN, hex_buff);\n    return safeWriteToFile(filename, hex_buff,\n            FCFS_AUTH_PASSWD_LEN * 2);\n}\n\nint fcfs_auth_load_passwd_ex(const char *filename,\n        unsigned char passwd[16], const bool ignore_enoent)\n{\n    int result;\n    int len;\n    int64_t file_size;\n    char hex_buff[2 * FCFS_AUTH_PASSWD_LEN + 4];\n\n    file_size = sizeof(hex_buff);\n    if (IS_URL_RESOURCE(filename)) {\n        int http_status;\n        int content_len;\n        char buff[8 * 1024];\n        char *content;\n        char error_info[512];\n\n        content = buff;\n        content_len = sizeof(buff);\n        if ((result=get_url_content_ex(filename, strlen(filename),\n                        SF_G_CONNECT_TIMEOUT, SF_G_NETWORK_TIMEOUT,\n                        &http_status, &content, &content_len,\n                        error_info)) != 0)\n        {\n            if (*error_info != '\\0') {\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"%s fetch fail, %s\", __LINE__,\n                        filename, error_info);\n            } else {\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"%s fetch fail, errno: %d, error info: %s\",\n                        __LINE__, filename, result, STRERROR(result));\n            }\n            return result;\n        }\n\n        if (http_status != 200) {\n            if (http_status == 404 && ignore_enoent) {\n                memset(passwd, 0, FCFS_AUTH_PASSWD_LEN);\n                return 0;\n            }\n\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"HTTP status code: %d != 200, url: %s\",\n                    __LINE__, http_status, filename);\n            return http_status == 404 ? ENOENT : EACCES;\n        }\n\n        if (content_len >= sizeof(hex_buff)) {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"%s is not a valid secret file because the content \"\n                    \"length: %d >= %d\", __LINE__, filename, content_len,\n                    (int)sizeof(hex_buff));\n            return EOVERFLOW;\n        }\n        memcpy(hex_buff, content, content_len + 1);\n        file_size = content_len;\n    } else {\n        if (access(filename, F_OK) != 0) {\n            if (errno == ENOENT && ignore_enoent) {\n                memset(passwd, 0, FCFS_AUTH_PASSWD_LEN);\n                return 0;\n            }\n        }\n\n        if ((result=getFileContentEx(filename,\n                        hex_buff, 0, &file_size)) != 0)\n        {\n            return result;\n        }\n    }\n\n    if (file_size > 2 * FCFS_AUTH_PASSWD_LEN) {\n        trim_right(hex_buff);\n        file_size = strlen(hex_buff);\n    }\n    if (file_size != 2 * FCFS_AUTH_PASSWD_LEN) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"invalid secret filename: %s whose file size MUST be: %d\",\n                __LINE__, filename, 2 * FCFS_AUTH_PASSWD_LEN);\n        return EINVAL;\n    }\n\n    hex2bin(hex_buff, (char *)passwd, &len);\n    return 0;\n}\n\nint fcfs_auth_replace_filename_with_username(const string_t *src,\n        const string_t *username, FilenameString *new_filename)\n{\n#define USERNAME_VARIABLE_STR  \"${username}\"\n#define USERNAME_VARIABLE_LEN  (sizeof(USERNAME_VARIABLE_STR) - 1)\n    string_t tag;\n\n    FC_INIT_FILENAME_STRING(*new_filename);\n    FC_SET_STRING_EX(tag, USERNAME_VARIABLE_STR, USERNAME_VARIABLE_LEN);\n    return str_replace(src, &tag, username, &new_filename->s,\n            FC_FILENAME_BUFFER_SIZE(*new_filename));\n}\n\nint fcfs_auth_user_check_realloc_array(FCFSAuthUserArray *array,\n        const int target_count)\n{\n    int new_alloc;\n    FCFSAuthUserInfo *new_users;\n\n    if (array->alloc >= target_count) {\n        return 0;\n    }\n\n    new_alloc = array->alloc;\n    while (new_alloc < target_count) {\n        new_alloc *= 2;\n    }\n\n    new_users = (FCFSAuthUserInfo *)fc_malloc(\n            sizeof(FCFSAuthUserInfo) * new_alloc);\n    if (new_users == NULL) {\n        return ENOMEM;\n    }\n\n    if (array->count > 0) {\n        int bytes;\n        bytes = sizeof(FCFSAuthUserInfo) * array->count;\n        memcpy(new_users, array->users, bytes);\n    }\n    if (array->users != array->fixed) {\n        free(array->users);\n    }\n\n    array->users = new_users;\n    array->alloc = new_alloc;\n    return 0;\n}\n\nint fcfs_auth_spool_check_realloc_array(FCFSAuthStoragePoolArray *array,\n        const int target_count)\n{\n    int new_alloc;\n    FCFSAuthStoragePoolInfo *new_spools;\n\n    if (array->alloc >= target_count) {\n        return 0;\n    }\n\n    new_alloc = array->alloc;\n    while (new_alloc < target_count) {\n        new_alloc *= 2;\n    }\n\n    new_spools = (FCFSAuthStoragePoolInfo *)fc_malloc(\n            sizeof(FCFSAuthStoragePoolInfo) * new_alloc);\n    if (new_spools == NULL) {\n        return ENOMEM;\n    }\n\n    if (array->count > 0) {\n        int bytes;\n        bytes = sizeof(FCFSAuthStoragePoolInfo) * array->count;\n        memcpy(new_spools, array->spools, bytes);\n    }\n    if (array->spools != array->fixed) {\n        free(array->spools);\n    }\n\n    array->spools = new_spools;\n    array->alloc = new_alloc;\n    return 0;\n}\n\nint fcfs_auth_gpool_check_realloc_array(FCFSAuthGrantedPoolArray *array,\n        const int target_count)\n{\n    int new_alloc;\n    FCFSAuthGrantedPoolFullInfo *new_gpools;\n\n    if (array->alloc >= target_count) {\n        return 0;\n    }\n\n    new_alloc = array->alloc;\n    while (new_alloc < target_count) {\n        new_alloc *= 2;\n    }\n\n    new_gpools = (FCFSAuthGrantedPoolFullInfo *)fc_malloc(\n            sizeof(FCFSAuthGrantedPoolFullInfo) * new_alloc);\n    if (new_gpools == NULL) {\n        return ENOMEM;\n    }\n\n    if (array->count > 0) {\n        int bytes;\n        bytes = sizeof(FCFSAuthGrantedPoolFullInfo) * array->count;\n        memcpy(new_gpools, array->gpools, bytes);\n    }\n    if (array->gpools != array->fixed) {\n        free(array->gpools);\n    }\n\n    array->gpools = new_gpools;\n    array->alloc = new_alloc;\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/common/auth_func.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_AUTH_FUNC_H\n#define _FCFS_AUTH_FUNC_H\n\n#include \"sf/sf_proto.h\"\n#include \"auth_types.h\"\n\n#define fcfs_auth_load_passwd(filename, passwd) \\\n    fcfs_auth_load_passwd_ex(filename, passwd, false)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    static inline void fcfs_auth_user_init_array(FCFSAuthUserArray *array)\n    {\n        array->users = array->fixed;\n        array->alloc = FCFS_AUTH_FIXED_USER_COUNT;\n        array->count = 0;\n    }\n\n    static inline void fcfs_auth_user_free_array(FCFSAuthUserArray *array)\n    {\n        if (array->users != array->fixed) {\n            if (array->users != NULL) {\n                free(array->users);\n            }\n            array->users = array->fixed;\n            array->alloc = FCFS_AUTH_FIXED_USER_COUNT;\n        }\n    }\n\n    static inline void fcfs_auth_spool_init_array(\n            FCFSAuthStoragePoolArray *array)\n    {\n        array->spools = array->fixed;\n        array->alloc = FCFS_AUTH_FIXED_USER_COUNT;\n        array->count = 0;\n    }\n\n    static inline void fcfs_auth_spool_free_array(\n            FCFSAuthStoragePoolArray *array)\n    {\n        if (array->spools != array->fixed) {\n            if (array->spools != NULL) {\n                free(array->spools);\n            }\n            array->spools = array->fixed;\n            array->alloc = FCFS_AUTH_FIXED_USER_COUNT;\n        }\n    }\n\n    static inline void fcfs_auth_granted_init_array(\n            FCFSAuthGrantedPoolArray *array)\n    {\n        array->gpools = array->fixed;\n        array->alloc = FCFS_AUTH_FIXED_USER_COUNT;\n        array->count = 0;\n    }\n\n    static inline void fcfs_auth_granted_free_array(\n            FCFSAuthGrantedPoolArray *array)\n    {\n        if (array->gpools != array->fixed) {\n            if (array->gpools != NULL) {\n                free(array->gpools);\n            }\n            array->gpools = array->fixed;\n            array->alloc = FCFS_AUTH_FIXED_USER_COUNT;\n        }\n    }\n\n    int fcfs_auth_user_check_realloc_array(FCFSAuthUserArray *array,\n            const int target_count);\n\n    int fcfs_auth_spool_check_realloc_array(FCFSAuthStoragePoolArray *array,\n            const int target_count);\n\n    int fcfs_auth_gpool_check_realloc_array(FCFSAuthGrantedPoolArray *array,\n            const int target_count);\n\n    void fcfs_auth_generate_passwd(unsigned char passwd[16]);\n\n    int fcfs_auth_save_passwd(const char *filename, unsigned char passwd[16]);\n\n    int fcfs_auth_load_passwd_ex(const char *filename,\n            unsigned char passwd[16], const bool ignore_enoent);\n\n    int fcfs_auth_replace_filename_with_username(const string_t *src,\n            const string_t *username, FilenameString *new_filename);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/common/auth_global.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"auth_global.h\"\n\nFCFSAuthGlobalVars g_fcfs_auth_global_vars = {\n    {5, 5, 0}\n};\n"
  },
  {
    "path": "src/auth/common/auth_global.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_GLOBAL_H\n#define _FCFS_AUTH_GLOBAL_H\n\n#include \"fastcommon/common_define.h\"\n\ntypedef struct fcfs_auth_global_vars {\n    Version version;\n} FCFSAuthGlobalVars;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern FCFSAuthGlobalVars g_fcfs_auth_global_vars;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/common/auth_proto.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"auth_proto.h\"\n\nvoid fcfs_auth_proto_init()\n{\n}\n\nconst char *fcfs_auth_get_cmd_caption(const int cmd)\n{\n    switch (cmd) {\n        case FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_REQ:\n            return \"USER_LOGIN_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_RESP:\n            return \"USER_LOGIN_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_REQ:\n            return \"SESSION_SUBSCRIBE_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_RESP:\n            return \"SESSION_SUBSCRIBE_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_REQ:\n            return \"SESSION_PUSH_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_RESP:\n            return \"SESSION_PUSH_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_REQ:\n            return \"SESSION_VALIDATE_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_RESP:\n            return \"SESSION_VALIDATE_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ:\n            return \"USER_CREATE_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_CREATE_RESP:\n            return \"USER_CREATE_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_LIST_REQ:\n            return \"USER_LIST_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_LIST_RESP:\n            return \"USER_LIST_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_GRANT_REQ:\n            return \"USER_GRANT_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_GRANT_RESP:\n            return \"USER_GRANT_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_REQ:\n            return \"USER_REMOVE_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_RESP:\n            return \"USER_REMOVE_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ:\n            return \"USER_PASSWD_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_RESP:\n            return \"USER_PASSWD_RESP\";\n\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ:\n            return \"SPOOL_CREATE_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_RESP:\n            return \"SPOOL_CREATE_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ:\n            return \"SPOOL_LIST_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_RESP:\n            return \"SPOOL_LIST_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ:\n            return \"SPOOL_REMOVE_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_RESP:\n            return \"SPOOL_REMOVE_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ:\n            return \"SPOOL_SET_QUOTA_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_RESP:\n            return \"SPOOL_SET_QUOTA_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_REQ:\n            return \"SPOOL_GET_QUOTA_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_RESP:\n            return \"SPOOL_GET_QUOTA_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ:\n            return \"GPOOL_GRANT_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_RESP:\n            return \"GPOOL_GRANT_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ:\n            return \"GPOOL_WITHDRAW_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_RESP:\n            return \"GPOOL_WITHDRAW_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ:\n            return \"GPOOL_LIST_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_RESP:\n            return \"GPOOL_LIST_RESP\";\n\n        case FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ:\n            return \"GET_MASTER_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_GET_MASTER_RESP:\n            return \"GET_MASTER_RESP\";\n        case FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_REQ:\n            return \"CLUSTER_STAT_REQ\";\n        case FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_RESP:\n            return \"CLUSTER_STAT_RESP\";\n        case FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_REQ:\n            return \"GET_SERVER_STATUS_REQ\";\n        case FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_RESP:\n            return \"GET_SERVER_STATUS_RESP\";\n        case FCFS_AUTH_CLUSTER_PROTO_JOIN_MASTER:\n            return \"JOIN_MASTER\";\n        case FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_REQ:\n            return \"PING_MASTER_REQ\";\n        case FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_RESP:\n            return \"PING_MASTER_RESP\";\n        case FCFS_AUTH_CLUSTER_PROTO_PRE_SET_NEXT_MASTER:\n            return \"PRE_SET_NEXT_MASTER\";\n        case FCFS_AUTH_CLUSTER_PROTO_COMMIT_NEXT_MASTER:\n            return \"COMMIT_NEXT_MASTER\";\n        default:\n            return sf_get_cmd_caption(cmd);\n    }\n}\n"
  },
  {
    "path": "src/auth/common/auth_proto.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_AUTH_PROTO_H\n#define _FCFS_AUTH_PROTO_H\n\n#include \"sf/sf_proto.h\"\n#include \"auth_types.h\"\n\n#define FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_REQ             9\n#define FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_RESP           10\n\n#define FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_REQ     11\n#define FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_RESP    12\n#define FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_REQ          13\n#define FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_RESP         14\n#define FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_REQ      15\n#define FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_RESP     16\n\n#define FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ           21\n#define FCFS_AUTH_SERVICE_PROTO_USER_CREATE_RESP          22\n#define FCFS_AUTH_SERVICE_PROTO_USER_LIST_REQ             23\n#define FCFS_AUTH_SERVICE_PROTO_USER_LIST_RESP            24\n#define FCFS_AUTH_SERVICE_PROTO_USER_GRANT_REQ            25\n#define FCFS_AUTH_SERVICE_PROTO_USER_GRANT_RESP           26\n#define FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_REQ           27\n#define FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_RESP          28\n#define FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ           29\n#define FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_RESP          30\n\n#define FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ            61\n#define FCFS_AUTH_SERVICE_PROTO_GET_MASTER_RESP           62\n#define FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_REQ          63\n#define FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_RESP         64\n\n#define FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ          71\n#define FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_RESP         72\n#define FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ            73\n#define FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_RESP           74\n#define FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ          75\n#define FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_RESP         76\n#define FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ       77\n#define FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_RESP      78\n#define FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_REQ       79\n#define FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_RESP      80\n\n#define FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ           91\n#define FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_RESP          92\n#define FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ        93\n#define FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_RESP       94\n#define FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ            95\n#define FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_RESP           96\n\n//cluster commands\n#define FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_REQ    201\n#define FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_RESP   202\n#define FCFS_AUTH_CLUSTER_PROTO_JOIN_MASTER              203  //slave  -> master\n#define FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_REQ          205\n#define FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_RESP         206\n#define FCFS_AUTH_CLUSTER_PROTO_PRE_SET_NEXT_MASTER      207  //notify next leader to other servers\n#define FCFS_AUTH_CLUSTER_PROTO_COMMIT_NEXT_MASTER       208  //commit next leader to other servers\n\ntypedef SFCommonProtoHeader  FCFSAuthProtoHeader;\n\ntypedef struct fcfs_auth_proto_name_info {\n    unsigned char len;\n    char str[0];\n} FCFSAuthProtoNameInfo;\n\ntypedef struct fcfs_auth_proto_user_passwd_pair {\n    char passwd[FCFS_AUTH_PASSWD_LEN];\n    FCFSAuthProtoNameInfo username;\n} FCFSAuthProtoUserPasswdPair;\n\ntypedef struct fcfs_auth_proto_user_pool_pair {\n    FCFSAuthProtoNameInfo username;\n    FCFSAuthProtoNameInfo poolname;\n} FCFSAuthProtoUserPoolPair;\n\ntypedef struct fcfs_auth_proto_pool_priviledges {\n    char fdir[4];\n    char fstore[4];\n} FCFSAuthProtoPoolPriviledges;\n\ntypedef struct fcfs_auth_proto_user_login_req {\n    unsigned char flags;\n    FCFSAuthProtoUserPasswdPair up_pair;\n    FCFSAuthProtoNameInfo poolname;\n} FCFSAuthProtoUserLoginReq;\n\ntypedef struct fcfs_auth_proto_user_login_resp {\n    char session_id[FCFS_AUTH_SESSION_ID_LEN];\n} FCFSAuthProtoUserLoginResp;\n\ntypedef struct fcfs_auth_proto_session_subscribe_req {\n    FCFSAuthProtoUserPasswdPair up_pair;\n} FCFSAuthProtoSessionSubscribeReq;\n\ntypedef struct fcfs_auth_proto_session_validate_req {\n    char session_id[FCFS_AUTH_SESSION_ID_LEN];\n    char validate_key[FCFS_AUTH_PASSWD_LEN];\n    char priv_type;\n    char padding[7];\n    char pool_id[8];\n    char priv_required[8];\n} FCFSAuthProtoSessionValidateReq;\n\ntypedef struct fcfs_auth_proto_session_validate_resp {\n    char result[4];\n} FCFSAuthProtoSessionValidateResp;\n\ntypedef struct fcfs_auth_proto_session_push_resp_body_header {\n    char count[4];\n} FCFSAuthProtoSessionPushRespBodyHeader;\n\ntypedef struct fcfs_auth_proto_session_push_entry {\n    struct {\n        char available;\n        char id[8];\n        FCFSAuthProtoPoolPriviledges privs;\n    } pool;\n\n    struct {\n        char id[8];\n        char priv[8];\n    } user;\n} FCFSAuthProtoSessionPushEntry;\n\ntypedef struct fcfs_auth_proto_session_push_resp_body_part {\n    char session_id[FCFS_AUTH_SESSION_ID_LEN];\n    char operation;\n    FCFSAuthProtoSessionPushEntry entry[0];\n} FCFSAuthProtoSessionPushRespBodyPart;\n\ntypedef struct fcfs_auth_proto_user_create_req {\n    char priv[8];\n    FCFSAuthProtoUserPasswdPair up_pair;\n} FCFSAuthProtoUserCreateReq;\n\ntypedef struct fcfs_auth_proto_user_passwd_req {\n    FCFSAuthProtoUserPasswdPair up_pair;\n} FCFSAuthProtoUserPasswdReq;\n\ntypedef struct fcfs_auth_proto_user_list_req {\n    SFProtoLimitInfo limit;\n    FCFSAuthProtoNameInfo username;\n} FCFSAuthProtoUserListReq;\n\ntypedef struct fcfs_auth_proto_list_resp_header {\n    char count[4];\n    char is_last;\n    char padding[3];\n} FCFSAuthProtoListRespHeader;\n\ntypedef struct fcfs_auth_proto_user_list_resp_body_part {\n    char priv[8];\n    FCFSAuthProtoNameInfo username;\n} FCFSAuthProtoUserListRespBodyPart;\n\ntypedef struct fcfs_auth_proto_user_grant_req {\n    char priv[8];\n    FCFSAuthProtoNameInfo username;\n} FCFSAuthProtoUserGrantReq;\n\ntypedef struct fcfs_auth_proto_user_remove_req {\n    FCFSAuthProtoNameInfo username;\n} FCFSAuthProtoUserRemoveReq;\n\n\ntypedef struct fcfs_auth_proto_spool_create_req {\n    char quota[8];\n    char dryrun;\n    FCFSAuthProtoNameInfo poolname;\n} FCFSAuthProtoSPoolCreateReq;\n\ntypedef struct fcfs_auth_proto_spool_create_resp {\n    FCFSAuthProtoNameInfo poolname;\n} FCFSAuthProtoSPoolCreateResp;\n\ntypedef struct fcfs_auth_proto_spool_list_req {\n    SFProtoLimitInfo limit;\n    FCFSAuthProtoUserPoolPair up_pair;\n} FCFSAuthProtoSPoolListReq;\n\ntypedef struct fcfs_auth_proto_spool_list_resp_body_part {\n    char quota[8];\n    FCFSAuthProtoNameInfo poolname;\n} FCFSAuthProtoSPoolListRespBodyPart;\n\ntypedef struct fcfs_auth_proto_spool_remove_req {\n    FCFSAuthProtoNameInfo poolname;\n} FCFSAuthProtoSPoolRemoveReq;\n\ntypedef struct fcfs_auth_proto_spool_set_quota_req {\n    char quota[8];\n    FCFSAuthProtoNameInfo poolname;\n} FCFSAuthProtoSPoolSetQuotaReq;\n\ntypedef struct fcfs_auth_proto_spool_get_quota_req {\n    FCFSAuthProtoNameInfo poolname;\n} FCFSAuthProtoSPoolGetQuotaReq;\n\ntypedef struct fcfs_auth_proto_spool_get_quota_resp {\n    char quota[8];\n} FCFSAuthProtoSPoolGetQuotaResp;\n\ntypedef struct fcfs_auth_proto_spool_grant_req {\n    FCFSAuthProtoPoolPriviledges privs;\n    FCFSAuthProtoUserPoolPair up_pair;\n} FCFSAuthProtoSPoolGrantReq;\n\ntypedef struct fcfs_auth_proto_spool_withdraw_req {\n    FCFSAuthProtoUserPoolPair up_pair;\n} FCFSAuthProtoSPoolWithdrawReq;\n\ntypedef FCFSAuthProtoSPoolListReq FCFSAuthProtoGPoolListReq;\n\ntypedef struct fcfs_auth_proto_gpool_list_resp_body_part {\n    FCFSAuthProtoPoolPriviledges privs;\n    FCFSAuthProtoUserPoolPair up_pair;\n} FCFSAuthProtoGPoolListRespBodyPart;\n\ntypedef struct fcfs_auth_proto_get_server_resp {\n    char server_id[4];\n    char port[2];\n    char padding[2];\n    char ip_addr[IP_ADDRESS_SIZE];\n} FCFSAuthProtoGetServerResp;\n\ntypedef struct fcfs_auth_proto_get_server_status_req {\n    char server_id[4];\n    char config_sign[SF_CLUSTER_CONFIG_SIGN_LEN];\n} FCFSAuthProtoGetServerStatusReq;\n\ntypedef struct fcfs_auth_proto_get_server_status_resp {\n    char server_id[4];\n    char is_master;\n    char padding[3];\n} FCFSAuthProtoGetServerStatusResp;\n\ntypedef struct fcfs_auth_proto_join_master_req {\n    char server_id[4];     //the slave server id\n    char padding[4];\n    char config_sign[SF_CLUSTER_CONFIG_SIGN_LEN];\n} FCFSAuthProtoJoinMasterReq;\n\ntypedef struct fcfs_auth_proto_cluster_stat_resp_body_header {\n    char count[4];\n    char padding[4];\n} FCFSAuthProtoClusterStatRespBodyHeader;\n\ntypedef struct fcfs_auth_proto_cluster_stat_resp_body_part {\n    char server_id[4];\n    char is_online;\n    char is_master;\n    char padding[1];\n    char port[2];\n    char ip_addr[IP_ADDRESS_SIZE];\n} FCFSAuthProtoClusterStatRespBodyPart;\n\n#define auth_log_network_error_ex(response, conn, result, log_level) \\\n    sf_log_network_error_ex(response, conn, \"fauth\", result, log_level)\n\n#define auth_log_network_error(response, conn, result) \\\n    sf_log_network_error(response, conn, \"fauth\", result)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid fcfs_auth_proto_init();\n\nconst char *fcfs_auth_get_cmd_caption(const int cmd);\n\nstatic inline void fcfs_auth_parse_user_pool_pair(FCFSAuthProtoUserPoolPair\n        *up_pair, string_t *username, string_t *poolname)\n{\n    FCFSAuthProtoNameInfo *proto_pname;\n\n    FC_SET_STRING_EX(*username, up_pair->username.str,\n            up_pair->username.len);\n    proto_pname = (FCFSAuthProtoNameInfo *)(up_pair->username.str +\n            up_pair->username.len);\n    FC_SET_STRING_EX(*poolname, proto_pname->str, proto_pname->len);\n}\n\nstatic inline void fcfs_auth_pack_user_pool_pair(const string_t *username,\n        const string_t *poolname, FCFSAuthProtoUserPoolPair *up_pair)\n{\n    FCFSAuthProtoNameInfo *proto_pname;\n\n    up_pair->username.len = username->len;\n    if (username->len > 0) {\n        memcpy(up_pair->username.str, username->str, username->len);\n    }\n\n    proto_pname = (FCFSAuthProtoNameInfo *)(up_pair->\n            username.str + username->len);\n    proto_pname->len = poolname->len;\n    if (poolname->len > 0) {\n        memcpy(proto_pname->str, poolname->str, poolname->len);\n    }\n}\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/common/auth_types.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_AUTH_TYPES_H\n#define _FCFS_AUTH_TYPES_H\n\n#include \"fastcommon/common_define.h\"\n#include \"sf/sf_types.h\"\n\n#define FCFS_AUTH_DEFAULT_CLUSTER_PORT  31011\n#define FCFS_AUTH_DEFAULT_SERVICE_PORT  31012\n\n#define FCFS_AUTH_SESSION_FLAGS_PUBLISH     1\n\n#define FCFS_AUTH_USERNAME_MAX_LEN   64\n#define FCFS_AUTH_PASSWD_LEN         16\n#define FCFS_AUTH_SESSION_ID_LEN      8\n\n#define FCFS_AUTH_FIXED_USER_COUNT  256\n#define FCFS_AUTH_FIXED_POOL_COUNT  256\n\n#define FCFS_AUTH_USER_STATUS_NORMAL   0\n#define FCFS_AUTH_USER_STATUS_DELETED  1\n\n#define FCFS_AUTH_POOL_STATUS_NORMAL   0\n#define FCFS_AUTH_POOL_STATUS_DELETED  1\n\n#define FCFS_AUTH_USER_PRIV_NONE              0\n#define FCFS_AUTH_USER_PRIV_USER_MANAGE       (1 << 0)\n#define FCFS_AUTH_USER_PRIV_CREATE_POOL       (1 << 1)\n#define FCFS_AUTH_USER_PRIV_MONITOR_CLUSTER   (1 << 2)\n#define FCFS_AUTH_USER_PRIV_SUBSCRIBE_SESSION (1 << 3)\n\n#define FCFS_AUTH_USER_PRIV_ALL  (FCFS_AUTH_USER_PRIV_USER_MANAGE | \\\n        FCFS_AUTH_USER_PRIV_CREATE_POOL |     \\\n        FCFS_AUTH_USER_PRIV_MONITOR_CLUSTER | \\\n        FCFS_AUTH_USER_PRIV_SUBSCRIBE_SESSION)\n\n#define FCFS_AUTH_USER_PRIV_COUNT      4\n\n#define FCFS_AUTH_POOL_ACCESS_NONE       0\n#define FCFS_AUTH_POOL_ACCESS_WRITE      (1 << 1)\n#define FCFS_AUTH_POOL_ACCESS_READ       (1 << 2)\n\n#define FCFS_AUTH_POOL_ACCESS_ALL  (FCFS_AUTH_POOL_ACCESS_WRITE | \\\n        FCFS_AUTH_POOL_ACCESS_READ)\n#define FCFS_AUTH_POOL_ACCESS_COUNT      2\n\n#define FCFS_AUTH_UNLIMITED_QUOTA_STR  \"unlimited\"\n#define FCFS_AUTH_UNLIMITED_QUOTA_VAL  -1\n\n#define FCFS_AUTH_AUTO_ID_TAG_STR  \"${auto_id}\"\n#define FCFS_AUTH_AUTO_ID_TAG_LEN  (sizeof(FCFS_AUTH_AUTO_ID_TAG_STR) - 1)\n\n#define FCFS_AUTH_PUSH_OPERATION_CREATE_SESSION   'C'\n#define FCFS_AUTH_PUSH_OPERATION_UPDATE_SESSION   'U'\n#define FCFS_AUTH_PUSH_OPERATION_REMOVE_SESSION   'R'\n\n#define FCFS_AUTH_POOL_AVAILABLE(pool)  \\\n    ((pool.quota == FCFS_AUTH_UNLIMITED_QUOTA_VAL) || \\\n     (pool.used < pool.quota))\n\ntypedef enum {\n    fcfs_auth_validate_priv_type_user = 'u',\n    fcfs_auth_validate_priv_type_pool_fdir = 'd',\n    fcfs_auth_validate_priv_type_pool_fstore = 's'\n} FCFSAuthValidatePriviledgeType;\n\ntypedef struct fcfs_auth_spool_priviledges {\n    int fdir;\n    int fstore;\n} FCFSAuthSPoolPriviledges;\n\ntypedef struct fcfs_auth_storage_pool_info {\n    int64_t id;\n    string_t name;\n    int64_t quota; //bytes\n    int64_t used;  //bytes\n    int status;\n} FCFSAuthStoragePoolInfo;\n\ntypedef struct fcfs_auth_granted_pool_info {\n    int64_t id;\n    int64_t pool_id;\n    FCFSAuthSPoolPriviledges privs;\n} FCFSAuthGrantedPoolInfo;\n\ntypedef struct fcfs_auth_granted_pool_full_info {\n    FCFSAuthGrantedPoolInfo granted;\n    string_t username;\n    string_t pool_name;\n} FCFSAuthGrantedPoolFullInfo;\n\ntypedef struct fcfs_auth_user_info {\n    int64_t id;\n    string_t name;\n    string_t passwd;\n    int64_t priv;\n    int status;\n} FCFSAuthUserInfo;\n\ntypedef struct fcfs_auth_storage_pool_array {\n    FCFSAuthStoragePoolInfo *spools;\n    FCFSAuthStoragePoolInfo fixed[FCFS_AUTH_FIXED_POOL_COUNT];\n    int count;\n    int alloc;\n} FCFSAuthStoragePoolArray;\n\ntypedef struct fcfs_auth_granted_pool_array {\n    FCFSAuthGrantedPoolFullInfo *gpools;\n    FCFSAuthGrantedPoolFullInfo fixed[FCFS_AUTH_FIXED_POOL_COUNT];\n    int count;\n    int alloc;\n} FCFSAuthGrantedPoolArray;\n\ntypedef struct fcfs_auth_user_array {\n    FCFSAuthUserInfo *users;\n    FCFSAuthUserInfo fixed[FCFS_AUTH_FIXED_USER_COUNT];\n    int count;\n    int alloc;\n} FCFSAuthUserArray;\n\n#endif\n"
  },
  {
    "path": "src/auth/common/server_session.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"fastcommon/fast_mblock.h\"\n#include \"auth_func.h\"\n#include \"server_session.h\"\n\n#define SESSION_MIN_SHARED_ALLOCATOR_COUNT          1\n#define SESSION_MAX_SHARED_ALLOCATOR_COUNT       1000\n#define SESSION_DEFAULT_SHARED_ALLOCATOR_COUNT     11\n\n#define SESSION_MIN_HASHTABLE_CAPACITY           1361\n#define SESSION_MAX_HASHTABLE_CAPACITY       11229331\n#define SESSION_DEFAULT_HASHTABLE_CAPACITY      10949\n\n#define SESSION_MIN_SHARED_LOCK_COUNT               1\n#define SESSION_MAX_SHARED_LOCK_COUNT           10000\n#define SESSION_DEFAULT_SHARED_LOCK_COUNT         163\n\n#define SESSION_MIN_VALIDATE_WITHIN_FRESH_SECONDS            1\n#define SESSION_MAX_VALIDATE_WITHIN_FRESH_SECONDS     31536000\n#define SESSION_DEFAULT_VALIDATE_WITHIN_FRESH_SECONDS        5\n\ntypedef struct {\n    ServerSessionHashEntry **buckets;\n    int capacity;\n} ServerSessionHashtable;\n\ntypedef struct {\n    int count;\n    volatile uint32_t next;\n    struct fast_mblock_man *allocators;\n} ServerSessionAllocatorArray;\n\ntypedef struct {\n    int count;\n    pthread_mutex_t *locks;\n} ServerSessionLockArray;\n\ntypedef struct {\n    int fields_size;\n    volatile uint16_t sn;  //for generate session id\n    ServerSessionAllocatorArray allocator_array;\n    ServerSessionLockArray lock_array;\n    ServerSessionHashtable htable;\n    ServerSessionCallbacks callbacks;\n} ServerSessionContext;\n\nServerSessionConfig g_server_session_cfg;\nstatic ServerSessionContext session_ctx = {0, 0, {0, 0, NULL}};\n\n#define SESSION_SET_HASHTABLE_LOCK(htable, session_id) \\\n    int32_t bucket_index;  \\\n    pthread_mutex_t *lock; \\\n    bucket_index = session_id % (htable).capacity; \\\n    lock = session_ctx.lock_array.locks + (bucket_index %  \\\n            session_ctx.lock_array.count)\n\n#define SESSION_SET_BUCKET_AND_LOCK(htable, session_id) \\\n    ServerSessionHashEntry **bucket; \\\n    SESSION_SET_HASHTABLE_LOCK(htable, session_id);  \\\n    bucket = (htable).buckets + bucket_index\n\nvoid server_session_cfg_to_string_ex(char *buff,\n        const int size, const bool output_all)\n{\n    int len;\n    len = snprintf(buff, size, \"session {shared_allocator_count: %d, \"\n            \"shared_lock_count: %d, hashtable_capacity: %d, \"\n            \"validate_key_filename: %s\",\n            g_server_session_cfg.shared_allocator_count,\n            g_server_session_cfg.shared_lock_count,\n            g_server_session_cfg.hashtable_capacity,\n            g_server_session_cfg.validate_key_filename.str);\n    if (output_all) {\n        snprintf(buff + len, size - len,\n                \", validate_within_fresh_seconds: %d}\",\n                g_server_session_cfg.validate_within_fresh_seconds);\n    } else {\n        snprintf(buff + len, size - len, \"}\");\n    }\n}\n\nstatic int load_session_validate_key(IniFullContext *ini_ctx)\n{\n    int result;\n    char *key_filename;\n    char full_key_filename[PATH_MAX];\n    string_t validate_key_filename;\n\n    key_filename = iniGetStrValue(ini_ctx->section_name,\n            \"validate_key_filename\", ini_ctx->context);\n    if (key_filename == NULL || *key_filename == '\\0') {\n        key_filename = \"keys/session_validate.key\";\n    }\n\n    validate_key_filename.str = full_key_filename;\n    validate_key_filename.len = resolve_path(ini_ctx->filename,\n            key_filename, full_key_filename, sizeof(full_key_filename));\n    if ((result=fcfs_auth_load_passwd(validate_key_filename.str,\n                    g_server_session_cfg.validate_key_buff)) != 0)\n    {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"config file: %s, validate_key_filename: %s, \"\n                \"load password fail, ret code: %d\", __LINE__,\n                ini_ctx->filename, validate_key_filename.str, result);\n        return result;\n    }\n    g_server_session_cfg.validate_key.str = (char *)\n        g_server_session_cfg.validate_key_buff;\n    g_server_session_cfg.validate_key.len = FCFS_AUTH_PASSWD_LEN;\n\n    g_server_session_cfg.validate_key_filename.str = (char *)\n        fc_malloc(validate_key_filename.len + 1);\n    if (g_server_session_cfg.validate_key_filename.str == NULL) {\n        return ENOMEM;\n    }\n\n    g_server_session_cfg.validate_key_filename.len =\n        validate_key_filename.len;\n    memcpy(g_server_session_cfg.validate_key_filename.str,\n            validate_key_filename.str,\n            validate_key_filename.len + 1);\n    return 0;\n}\n\nstatic int do_load_session_cfg(const char *session_filename)\n{\n    IniContext ini_context;\n    IniFullContext ini_ctx;\n    int result;\n\n    if ((result=iniLoadFromFile(session_filename, &ini_context)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load session config file \\\"%s\\\" fail, ret code: %d\",\n                __LINE__, session_filename, result);\n        return result;\n    }\n\n    FAST_INI_SET_FULL_CTX_EX(ini_ctx, session_filename, NULL, &ini_context);\n    session_ctx.allocator_array.count = iniGetIntCorrectValue(\n            &ini_ctx, \"shared_allocator_count\",\n            SESSION_DEFAULT_SHARED_ALLOCATOR_COUNT,\n            SESSION_MIN_SHARED_ALLOCATOR_COUNT,\n            SESSION_MAX_SHARED_ALLOCATOR_COUNT);\n\n    session_ctx.lock_array.count = iniGetInt64CorrectValue(\n            &ini_ctx, \"shared_lock_count\",\n            SESSION_DEFAULT_SHARED_LOCK_COUNT,\n            SESSION_MIN_SHARED_LOCK_COUNT,\n            SESSION_MAX_SHARED_LOCK_COUNT);\n\n    session_ctx.htable.capacity = iniGetIntCorrectValue(\n            &ini_ctx, \"hashtable_capacity\",\n            SESSION_DEFAULT_HASHTABLE_CAPACITY,\n            SESSION_MIN_HASHTABLE_CAPACITY,\n            SESSION_MAX_HASHTABLE_CAPACITY);\n\n    g_server_session_cfg.validate_within_fresh_seconds =\n        iniGetIntCorrectValue(&ini_ctx, \"validate_within_fresh_seconds\",\n            SESSION_DEFAULT_VALIDATE_WITHIN_FRESH_SECONDS,\n            SESSION_MIN_VALIDATE_WITHIN_FRESH_SECONDS,\n            SESSION_MAX_VALIDATE_WITHIN_FRESH_SECONDS);\n\n    g_server_session_cfg.shared_allocator_count =\n        session_ctx.allocator_array.count;\n    g_server_session_cfg.shared_lock_count = session_ctx.lock_array.count;\n    g_server_session_cfg.hashtable_capacity = session_ctx.htable.capacity;\n\n    result = load_session_validate_key(&ini_ctx);\n    iniFreeContext(&ini_context);\n    return result;\n}\n\nstatic int load_session_config(IniFullContext *ini_ctx)\n{\n    char *session_config_filename;\n    char full_session_filename[PATH_MAX];\n\n    session_config_filename = iniGetStrValue(NULL,\n            \"session_config_filename\", ini_ctx->context);\n    if (session_config_filename == NULL || *session_config_filename == '\\0') {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"config file: %s, item \\\"session_config_filename\\\" \"\n                \"not exist or empty\", __LINE__, ini_ctx->filename);\n        return ENOENT;\n    }\n\n    resolve_path(ini_ctx->filename, session_config_filename,\n            full_session_filename, sizeof(full_session_filename));\n    return do_load_session_cfg(full_session_filename);\n}\n\nstatic int server_session_alloc_init(ServerSessionHashEntry *session,\n        struct fast_mblock_man *allocator)\n{\n    session->allocator = allocator;\n    session->entry.fields = session + 1;\n    return 0;\n}\n\nstatic int init_allocator_array(ServerSessionAllocatorArray *array)\n{\n    int result;\n    int bytes;\n    int element_size;\n    struct fast_mblock_man *mblock;\n    struct fast_mblock_man *end;\n\n    bytes = sizeof(struct fast_mblock_man) * array->count;\n    array->allocators = (struct fast_mblock_man *)fc_malloc(bytes);\n    if (array->allocators == NULL) {\n        return ENOMEM;\n    }\n\n    element_size = sizeof(ServerSessionHashEntry) + session_ctx.fields_size;\n    end = array->allocators + array->count;\n    for (mblock=array->allocators; mblock<end; mblock++) {\n        if ((result=fast_mblock_init_ex1(mblock, \"session_entry\",\n                        element_size, 4096, 0,\n                        (fast_mblock_object_init_func)\n                        server_session_alloc_init,\n                        mblock, true)) != 0)\n        {\n            return result;\n        }\n    }\n\n    return 0;\n}\n\nstatic int init_lock_array(ServerSessionLockArray *array)\n{\n    int result;\n    int bytes;\n    pthread_mutex_t *lock;\n    pthread_mutex_t *end;\n\n    bytes = sizeof(pthread_mutex_t) * array->count;\n    array->locks = (pthread_mutex_t *)fc_malloc(bytes);\n    if (array->locks == NULL) {\n        return ENOMEM;\n    }\n\n    end = array->locks + array->count;\n    for (lock=array->locks; lock<end; lock++) {\n        if ((result=init_pthread_lock(lock)) != 0) {\n            return result;\n        }\n    }\n\n    return 0;\n}\n\nstatic int init_hashtable(ServerSessionHashtable *htable)\n{\n    int bytes;\n\n    bytes = sizeof(ServerSessionHashEntry *) * htable->capacity;\n    htable->buckets = (ServerSessionHashEntry **)fc_malloc(bytes);\n    if (htable->buckets == NULL) {\n        return ENOMEM;\n    }\n    memset(htable->buckets, 0, bytes);\n\n    return 0;\n}\n\nint server_session_init_ex(IniFullContext *ini_ctx, const int fields_size,\n        ServerSessionCallbacks *callbacks)\n{\n    int result;\n\n    if ((result=load_session_config(ini_ctx)) != 0) {\n        return result;\n    }\n\n    session_ctx.fields_size = fields_size;\n    if ((result=init_allocator_array(&session_ctx.allocator_array)) != 0) {\n        return result;\n    }\n\n    if ((result=init_lock_array(&session_ctx.lock_array)) != 0) {\n        return result;\n    }\n\n    if ((result=init_hashtable(&session_ctx.htable)) != 0) {\n        return result;\n    }\n\n    if (callbacks != NULL) {\n        session_ctx.callbacks =  *callbacks;\n    } else {\n        session_ctx.callbacks.add_func = NULL;\n        session_ctx.callbacks.del_func = NULL;\n    }\n\n    srand(time(NULL));\n    return 0;\n}\n\nstatic inline ServerSessionHashEntry *session_htable_find(ServerSessionHashEntry\n        **bucket, const uint64_t session_id, ServerSessionHashEntry **prev)\n{\n    ServerSessionHashEntry *current;\n\n    if (*bucket == NULL) {\n        *prev = NULL;\n        return NULL;\n    } else if ((*bucket)->entry.id_info.id == session_id) {\n        *prev = NULL;\n        return *bucket;\n    } else if ((*bucket)->entry.id_info.id > session_id) {\n        *prev = NULL;\n        return NULL;\n    }\n\n    *prev = *bucket;\n    while ((current=(*prev)->next) != NULL) {\n        if (current->entry.id_info.id == session_id) {\n            return current;\n        } else if (current->entry.id_info.id > session_id) {\n            return NULL;\n        }\n\n        *prev = current;\n    }\n\n    return NULL;\n}\n\nstatic int session_htable_insert(ServerSessionHashEntry *se, const bool replace)\n{\n    int result;\n    ServerSessionHashEntry *previous;\n    ServerSessionHashEntry *found;\n\n    SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, se->entry.id_info.id);\n    PTHREAD_MUTEX_LOCK(lock);\n    if ((found=session_htable_find(bucket, se->entry.\n                    id_info.id, &previous)) == NULL)\n    {\n        if (previous == NULL) {\n            se->next = *bucket;\n            *bucket = se;\n        } else {\n            se->next = previous->next;\n            previous->next = se;\n        }\n        result = 0;\n    } else {\n        if (replace) {\n            found->entry = se->entry;\n            fast_mblock_free_object(se->allocator, se);\n            result = 0;\n        } else {\n            result = EEXIST;\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(lock);\n\n    return result;\n}\n\nServerSessionEntry *server_session_add_ex(const ServerSessionEntry *entry,\n        const bool publish, const bool persistent)\n{\n    int result;\n    bool replace;\n    struct fast_mblock_man *allocator;\n    ServerSessionHashEntry *se;\n\n    allocator = session_ctx.allocator_array.allocators +\n        (__sync_fetch_and_add(&session_ctx.allocator_array.next, 1) %\n         session_ctx.allocator_array.count);\n    se = (ServerSessionHashEntry *)fast_mblock_alloc_object(allocator);\n    if (se == NULL) {\n        return NULL;\n    }\n\n    memcpy(se->entry.fields, entry->fields, session_ctx.fields_size);\n    if (entry->id_info.id == 0) {\n        replace = false;\n        do {\n            se->entry.id_info.fields.ts = g_current_time;\n            se->entry.id_info.fields.publish = (publish ? 1 : 0);\n            se->entry.id_info.fields.persistent = (persistent ? 1 : 0);\n            se->entry.id_info.fields.rn = (int64_t)rand() * 16384LL / RAND_MAX;\n            se->entry.id_info.fields.sn = __sync_add_and_fetch(\n                    &session_ctx.sn, 1);\n\n            /*\n            logInfo(\"session_id: %\"PRId64\", ts: %d, publish: %d, \"\n                    \"rand: %u, sn: %u\", se->entry.id_info.id,\n                    se->entry.id_info.fields.ts, publish,\n                    se->entry.id_info.fields.rn,\n                    se->entry.id_info.fields.sn);\n                    */\n            result = session_htable_insert(se, replace);\n        } while (result == EEXIST);\n    } else {\n        replace = true;\n        se->entry.id_info.id = entry->id_info.id;\n        session_htable_insert(se, replace);\n    }\n\n    if (session_ctx.callbacks.add_func != NULL) {\n        session_ctx.callbacks.add_func(&se->entry);\n    }\n    return &se->entry;\n}\n\nint server_session_get_fields(const uint64_t session_id, void *fields)\n{\n    int result;\n    ServerSessionHashEntry *previous;\n    ServerSessionHashEntry *found;\n\n    SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, session_id);\n    PTHREAD_MUTEX_LOCK(lock);\n    if ((found=session_htable_find(bucket, session_id,\n                    &previous)) != NULL)\n    {\n        memcpy(fields, found->entry.fields, session_ctx.fields_size);\n        result = 0;\n    } else {\n        result = SF_SESSION_ERROR_NOT_EXIST;\n    }\n    PTHREAD_MUTEX_UNLOCK(lock);\n\n    return result;\n}\n\nint server_session_user_priv_granted(const uint64_t session_id,\n        const int64_t the_priv)\n{\n    int result;\n    ServerSessionHashEntry *previous;\n    ServerSessionHashEntry *found;\n    SessionSyncedFields *fields;\n\n    SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, session_id);\n    PTHREAD_MUTEX_LOCK(lock);\n    if ((found=session_htable_find(bucket, session_id,\n                    &previous)) != NULL)\n    {\n        fields = (SessionSyncedFields *)found->entry.fields;\n        if ((fields->user.priv & the_priv) == the_priv) {\n            result = 0;\n        } else {\n            result = EPERM;\n        }\n    } else {\n        result = SF_SESSION_ERROR_NOT_EXIST;\n    }\n    PTHREAD_MUTEX_UNLOCK(lock);\n\n    return result;\n}\n\nint server_session_fstore_priv_granted(const uint64_t session_id,\n        const int the_priv)\n{\n    int result;\n    ServerSessionHashEntry *previous;\n    ServerSessionHashEntry *found;\n    SessionSyncedFields *fields;\n\n    SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, session_id);\n    PTHREAD_MUTEX_LOCK(lock);\n    if ((found=session_htable_find(bucket, session_id,\n                    &previous)) != NULL)\n    {\n        fields = (SessionSyncedFields *)found->entry.fields;\n        if ((fields->pool.privs.fstore & the_priv) == the_priv) {\n            result = 0;\n        } else {\n            result = EPERM;\n        }\n    } else {\n        result = SF_SESSION_ERROR_NOT_EXIST;\n    }\n    PTHREAD_MUTEX_UNLOCK(lock);\n\n    return result;\n}\n\nint server_session_fdir_priv_granted(const uint64_t session_id,\n        const int the_priv)\n{\n    int result;\n    ServerSessionHashEntry *previous;\n    ServerSessionHashEntry *found;\n    SessionSyncedFields *fields;\n\n    SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, session_id);\n    PTHREAD_MUTEX_LOCK(lock);\n    if ((found=session_htable_find(bucket, session_id,\n                    &previous)) != NULL)\n    {\n        fields = (SessionSyncedFields *)found->entry.fields;\n        if ((fields->pool.privs.fdir & the_priv) == the_priv) {\n            result = 0;\n        } else {\n            result = EPERM;\n        }\n    } else {\n        result = SF_SESSION_ERROR_NOT_EXIST;\n    }\n    PTHREAD_MUTEX_UNLOCK(lock);\n\n    return result;\n}\n\nstatic inline void free_hash_entry(ServerSessionHashEntry *entry)\n{\n    if (session_ctx.callbacks.del_func != NULL) {\n        session_ctx.callbacks.del_func(&entry->entry);\n    }\n\n    fast_mblock_free_object(entry->allocator, entry);\n}\n\nint server_session_delete(const uint64_t session_id)\n{\n    ServerSessionHashEntry *previous;\n    ServerSessionHashEntry *found;\n\n    SESSION_SET_BUCKET_AND_LOCK(session_ctx.htable, session_id);\n    PTHREAD_MUTEX_LOCK(lock);\n    if ((found=session_htable_find(bucket, session_id,\n                    &previous)) != NULL)\n    {\n        if (previous == NULL) {\n            *bucket = (*bucket)->next;\n        } else {\n            previous->next = found->next;\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(lock);\n\n    if (found != NULL) {\n        free_hash_entry(found);\n        return 0;\n    } else {\n        return ENOENT;\n    }\n}\n\n#define SERVER_SESSION_ADD_TO_CHAIN(chain, node) \\\n    if (chain.tail == NULL) {  \\\n        chain.head = node;  \\\n    } else {  \\\n        chain.tail->next = node;  \\\n    } \\\n    chain.tail = node\n\nvoid server_session_clear()\n{\n    typedef struct {\n        ServerSessionHashEntry *head;\n        ServerSessionHashEntry *tail;\n    } ServerSessionHashChain;\n\n    ServerSessionHashEntry *current;\n    ServerSessionHashEntry *deleted;\n    ServerSessionHashEntry **bucket;\n    ServerSessionHashEntry **end;\n    ServerSessionHashChain keep;\n    ServerSessionHashChain remove;\n    pthread_mutex_t *lock;\n    int64_t keep_count;\n    int64_t remove_count;\n\n    keep_count = remove_count = 0;\n    current = NULL;\n    end = session_ctx.htable.buckets + session_ctx.htable.capacity;\n    for (bucket=session_ctx.htable.buckets; bucket<end; bucket++) {\n        lock = session_ctx.lock_array.locks + ((bucket - session_ctx.\n                    htable.buckets) % session_ctx.lock_array.count);\n        PTHREAD_MUTEX_LOCK(lock);\n        if (*bucket != NULL) {\n            keep.head = keep.tail = NULL;\n            remove.head = remove.tail = NULL;\n            current = *bucket;\n            do {\n                if (current->entry.id_info.fields.persistent) {\n                    SERVER_SESSION_ADD_TO_CHAIN(keep, current);\n                    keep_count++;\n                } else {\n                    SERVER_SESSION_ADD_TO_CHAIN(remove, current);\n                    remove_count++;\n                }\n\n                current = current->next;\n            } while (current != NULL);\n\n            *bucket = keep.head;\n            if (keep.tail != NULL) {\n                keep.tail->next = NULL;\n            }\n\n            current = remove.head;\n            if (remove.tail != NULL) {\n                remove.tail->next = NULL;\n            }\n        }\n        PTHREAD_MUTEX_UNLOCK(lock);\n\n        while (current != NULL) {\n            deleted = current;\n            current = current->next;\n\n            free_hash_entry(deleted);\n        }\n    }\n\n    if (keep_count > 0 || remove_count > 0) {\n        char keep_buff[256];\n        char remove_buff[256];\n\n        if (keep_count > 0) {\n            sprintf(keep_buff, \"%\"PRId64\" sessions persisted\", keep_count);\n        } else {\n            *keep_buff = '\\0';\n        }\n        if (remove_count > 0) {\n            sprintf(remove_buff, \"%\"PRId64\" sessions cleared\", remove_count);\n        } else {\n            *remove_buff = '\\0';\n        }\n\n        if (keep_count > 0 && remove_count > 0) {\n            logInfo(\"file: \"__FILE__\", line: %d, \"\n                    \"%s, %s\", __LINE__, keep_buff, remove_buff);\n        } else {\n            logInfo(\"file: \"__FILE__\", line: %d, \"\n                    \"%s\", __LINE__, (*keep_buff != '\\0') ?\n                    keep_buff : remove_buff);\n        }\n    }\n}\n"
  },
  {
    "path": "src/auth/common/server_session.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_SERVER_SESSION_H\n#define _FCFS_AUTH_SERVER_SESSION_H\n\n#include \"fastcommon/ini_file_reader.h\"\n#include \"auth_types.h\"\n\n#define FCFS_AUTH_SESSION_OP_TYPE_CREATE   'C'\n#define FCFS_AUTH_SESSION_OP_TYPE_REMOVE   'R'\n\ntypedef struct server_session_config {\n    int shared_allocator_count;\n    int shared_lock_count;\n    int hashtable_capacity;\n    int validate_within_fresh_seconds;\n    string_t validate_key;\n    string_t validate_key_filename;\n    unsigned char validate_key_buff[FCFS_AUTH_PASSWD_LEN];\n} ServerSessionConfig;\n\ntypedef union server_session_id_info {\n    uint64_t id;\n    struct {\n        unsigned int ts;\n        bool publish : 1;\n        bool persistent : 1;\n        unsigned int rn : 14;\n        unsigned int sn : 16;\n    } fields;\n} ServerSessionIdInfo;\n\ntypedef struct session_synced_fields {\n    struct {\n        int64_t id;\n        int64_t priv;\n    } user;\n\n    struct {\n        int64_t id;\n        bool available;\n        FCFSAuthSPoolPriviledges privs;\n    } pool;\n} SessionSyncedFields;\n\ntypedef struct server_session_entry {\n    ServerSessionIdInfo id_info;\n    void *fields;\n} ServerSessionEntry;\n\ntypedef struct server_session_hash_entry {\n    ServerSessionEntry entry;\n    struct fast_mblock_man *allocator;  //for free\n    struct server_session_hash_entry *next;  //for hashtable\n} ServerSessionHashEntry;\n\ntypedef void (*server_session_callback)(ServerSessionEntry *session);\ntypedef struct server_session_callbacks {\n    server_session_callback add_func;\n    server_session_callback del_func;\n} ServerSessionCallbacks;\n\n#define FCFS_AUTH_SERVER_SESSION_BY_FIELDS(fields)  \\\n    &((ServerSessionHashEntry *)fields - 1)->entry\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern ServerSessionConfig g_server_session_cfg;\n\n#define server_session_init(ini_ctx) \\\n    server_session_init_ex(ini_ctx, sizeof(SessionSyncedFields), NULL)\n\n#define server_session_cfg_to_string(buff, size) \\\n    server_session_cfg_to_string_ex(buff, size, false)\n\n#define server_session_add(entry, publish) \\\n    server_session_add_ex(entry, publish, false)\n\nint server_session_init_ex(IniFullContext *ini_ctx, const int fields_size,\n        ServerSessionCallbacks *callbacks);\n\nvoid server_session_cfg_to_string_ex(char *buff,\n        const int size, const bool output_all);\n\nServerSessionEntry *server_session_add_ex(const ServerSessionEntry *entry,\n        const bool publish, const bool persistent);\n\nint server_session_get_fields(const uint64_t session_id, void *fields);\n\nint server_session_user_priv_granted(const uint64_t session_id,\n        const int64_t the_priv);\n\nint server_session_fstore_priv_granted(const uint64_t session_id,\n        const int the_priv);\n\nint server_session_fdir_priv_granted(const uint64_t session_id,\n        const int the_priv);\n\nint server_session_delete(const uint64_t session_id);\n\nvoid server_session_clear();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/conf/auth.conf",
    "content": "# enable / disable authentication\n# default value is false\nauth_enabled = false\n\n# the username for login\n# default value is admin\nusername = admin\n\n# the secret key filename of the user\n# variable ${username} will be replaced with the value of username\n# default value is keys/${username}.key\nsecret_key_filename = keys/${username}.key\n\n# the config filename of auth client\nclient_config_filename = client.conf\n"
  },
  {
    "path": "src/auth/conf/client.conf",
    "content": "# the base path to store log files\nbase_path = /opt/fastcfs/auth\n\n# config the cluster servers\ncluster_config_filename = cluster.conf\n"
  },
  {
    "path": "src/auth/conf/cluster.conf",
    "content": "\n[master-election]\n# the quorum for master election\n# set quorum to majority to avoid brain-split\n# value list:\n##  any: no requirement\n##  majority: more than half\n##  auto: set to majority when the number of nodes is odd,\n##        otherwise set to any\n# default value is auto\nquorum = auto\n\n# if enable vote node when the number of servers is even\n# the default value is false\nvote_node_enabled = false\n\n# the cluster config filename of the vote node\n# this parameter is valid when vote_node_enabled is true\nvote_node_cluster_filename = ../vote/cluster.conf\n\n\n[group-cluster]\n# the default cluster port\nport = 31011\n\n[group-service]\n# the default service port\nport = 31012\n\n## Important:server group mark, don't modify this line.\n\n# config a server\n# section format: [server-$id]\n# server id is a 32 bits natural number (1, 2, 3 etc.),\n[server-1]\n\n# format: host[:port]\n# host can be an IP address or a hostname, IPv6 is supported\n# IP address is recommended\n# can occur more than once\nhost = 172.16.168.128\n"
  },
  {
    "path": "src/auth/conf/full/auth.conf",
    "content": "# enable / disable authentication\n# default value is false\nauth_enabled = false\n\n# the username for login\n# default value is admin\nusername = admin\n\n# the secret key filename of the user\n# variable ${username} will be replaced with the value of username\n# default value is keys/${username}.key\nsecret_key_filename = keys/${username}.key\n\n# the config filename of auth client\nclient_config_filename = client.conf\n"
  },
  {
    "path": "src/auth/conf/full/client.conf",
    "content": "# connect timeout in seconds\n# default value is 10\n# Note: in the intranet network (LAN), 10 seconds is enough.\nconnect_timeout = 10\n\n# network timeout in seconds\n# default value is 60\nnetwork_timeout = 60\n\n# the base path to store log files\nbase_path = /opt/fastcfs/auth\n\n# config the cluster servers\ncluster_config_filename = cluster.conf\n\n#standard log level as syslog, case insensitive, value list:\n### emerg for emergency\n### alert\n### crit for critical\n### error\n### warn for warning\n### notice\n### info\n### debug\nlog_level = info\n"
  },
  {
    "path": "src/auth/conf/full/cluster.conf",
    "content": "\n[master-election]\n# the quorum for master election\n# set quorum to majority to avoid brain-split\n# value list:\n##  any: no requirement\n##  majority: more than half\n##  auto: set to majority when the number of nodes is odd,\n##        otherwise set to any\n# default value is auto\nquorum = auto\n\n# the timeout to determinate master lost\n# the default value is 3 seconds\nmaster_lost_timeout = 3\n\n# the max wait time for master election\n# this parameter is for the master restart\n# the default value is 5 seconds\nmax_wait_time = 5\n\n# if enable vote node when the number of servers is even\n# the default value is false\nvote_node_enabled = false\n\n# the cluster config filename of the vote node\n# this parameter is valid when vote_node_enabled is true\nvote_node_cluster_filename = ../vote/cluster.conf\n\n\n[group-cluster]\n# the default cluster port\nport = 31011\n\n[group-service]\n# the default service port\nport = 31012\n\n## Important:server group mark, don't modify this line.\n\n# config a server\n# section format: [server-$id]\n# server id is a 32 bits natural number (1, 2, 3 etc.),\n[server-1]\n\n# format: host[:port]\n# host can be an IP address or a hostname, IPv6 is supported\n# IP address is recommended\n# can occur more than once\nhost = 172.16.168.128\n"
  },
  {
    "path": "src/auth/conf/full/server.conf",
    "content": "# connect timeout in seconds\n# default value is 10\n# Note: in the intranet network (LAN), 10 seconds is enough.\n# do NOT set to 1 second because of time accuracy!\nconnect_timeout = 10\n\n# network timeout in seconds for send and recv\n# default value is 60\nnetwork_timeout = 60\n\n# the base path to store log files\n# this path must be exist\nbase_path = /opt/fastcfs/auth\n\n# max concurrent connections this server support\n# you should set this parameter larger, eg. 10240\n# default value is 256\nmax_connections = 10240\n\n# the min network buff size\n# default value 8KB\nmin_buff_size = 64KB\n\n# the max network buff size\n# default value 128KB\nmax_buff_size = 256KB\n\n# max pkg size\n# default value is 16K\nmax_pkg_size = 256KB\n\n# if io_uring send with zero copy when Linux kernel version >= 5.19\n# set to true when the NIC support zero copy\n# can be overwritten in sections: [cluster] and [service]\n# default value is true\nuse_send_zc = true\n\n# TCP quick ack for Linux (setsockopt with TCP_QUICKACK option)\n# default value is true\ntcp_quick_ack = true\n\n# config the cluster servers and groups\ncluster_config_filename = cluster.conf\n\n# the session config filename\nsession_config_filename = session.conf\n\n#standard log level as syslog, case insensitive, value list:\n### emerg for emergency\n### alert\n### crit for critical\n### error\n### warn for warning\n### notice\n### info\n### debug\nlog_level = info\n\n#unix group name to run this program, \n#not set (empty) means run by the group of current user\nrun_by_group=\n\n#unix username to run this program,\n#not set (empty) means run by current user\nrun_by_user =\n\n# thread stack size, should >= 64KB\n# default value is 256KB\nthread_stack_size = 256KB\n\n\n# NOTE: following global parameters for error log and slow log\n# which can be overwritten in [error-log] and [slow-log] sections\n\n# sync log buff to disk every interval seconds\n# default value is 1 seconds\nsync_log_buff_interval = 1\n\n# if rotate the log file every day\n# set to true for rotate the log file anyway at the rotate time\n# default value is true\nlog_file_rotate_everyday = true\n\n# the time to rotate the log file, format is Hour:Minute\n# Hour from 0 to 23, Minute from 0 to 59\n# valid only when log_file_rotate_everyday is true\n# default value is 00:00\nlog_file_rotate_time = 00:00\n\n# if compress the old log file by gzip\n# default value is false\nlog_file_compress_old = false\n\n# compress the log file days before\n# default value is 1\nlog_file_compress_days_before = 7\n\n# rotate the log file when the log file exceeds this size\n# 0 means never rotates log file by log file size\n# default value is 0\nlog_file_rotate_on_size = 0\n\n# keep days of the log files\n# 0 means do not delete the old log files\n# default value is 15\nlog_file_keep_days = 15\n\n# the time to delete the old log files, format is Hour:Minute\n# Hour from 0 to 23, Minute from 0 to 59\n# valid only when log_file_keep_days > 0\n# default value is 01:30\nlog_file_delete_old_time = 01:30\n\n\n[error-log]\n# global log parameters can be overwritten here for error log\n\n[slow-log]\n# global log parameters can be overwritten here for slow log\n\n# if enable the slow log\n# default value is false\nenabled = true\n\n# the filename prefix of the slow log\n# default value is slow\nfilename_prefix = slow\n\n# log the request to the slow log whose response time exceeds this parameter\n# default value is 100ms\nlog_slower_than_ms = 50\n\n\n[cluster]\n# bind an address of this host\n# empty for bind all addresses of this host\nbind_addr =\n\n# the listen port\nport = 31011\n\n# the accept thread count\n# default value is 1 which is recommended\naccept_threads = 1\n\n# the network thread count\n# these threads deal network io\n# dispatched by the incoming socket fd\n# default value is 4\nwork_threads = 4\n\n# if io_uring send with zero copy when Linux kernel version >= 5.19\n# set to true when the NIC support zero copy\n# default value set by global config\nuse_send_zc = true\n\n[service]\nbind_addr =\nport = 31012\naccept_threads = 1\nwork_threads = 4\nuse_send_zc = true\n\n[admin-generate]\n# the generate mode for creating the admin user when not exist, value list:\n##  first: the new created user will be the first user (no other users)\n##  always: always create this admin user when not exist\n# default value is first\nmode = first\n\n# the admin user to auto generate\n# you should set this parameter to what you want before authd first run\n# default value is admin\nusername = admin\n\n# the filename of the secret key which can be absolute path or relative path\n# the relative path based on the path of this config file,\n# such as /etc/fastcfs/auth/server.conf\n# ${username} will be replaced with the value of above \"username\" parameter\n# Note: this secret key file will be auto generated, do NOT modify it!\nsecret_key_filename = keys/${username}.key\n\n\n[pool-generate]\n# the initial/first value for auto increment id referred as ${auto_id}\n# eg. set 100001 to generate six digital id (the first generated id is 100001)\n# default value is 1\nauto_id_initial = 1\n\n# the pool name template can contain ${auto_id} for creating pool,\n# such as pool-${auto_id}\n# default value is ${auto_id}\npool_name_template = ${auto_id}\n\n\n[FastDIR]\n# config the FastDIR client config filename\nclient_config_filename = ../fdir/client.conf\n\n# the interval to refresh/fetch the usage (used bytes) of the storage pools\n# unit: seconds\n# default value is 3\npool_usage_refresh_interval = 3\n"
  },
  {
    "path": "src/auth/conf/full/session.conf",
    "content": "\n# the capacity (bucket count) of session hashtable\n# default value is 10949\nhashtable_capacity = 10949\n\n# the shared allocators for hashtabe entry\n# NO more than the CPU cores is recommended\n# default value is 11\nshared_allocator_count = 11\n\n# the shared locks for session hashtable\n# default value is 163\nshared_lock_count = 163\n\n# the fresh seconds/threshold to validate session from the auth server\n# this parameter solves the problem of the new created session\n# synchronization delay\n#\n# if the session does not exist in local and this session generated\n# within X seconds, then request the auth server to validate the session\n#\n# default value is 5\nvalidate_within_fresh_seconds = 5\n\n# the secret key filename for session validation\n# the file content is 32 bytes hex characters\nvalidate_key_filename = keys/session_validate.key\n"
  },
  {
    "path": "src/auth/conf/keys/session_validate.key",
    "content": "dd7702749e369c7e8139d6ca3bf443ce"
  },
  {
    "path": "src/auth/conf/server.conf",
    "content": "# the base path to store log files\n# this path must be exist\nbase_path = /opt/fastcfs/auth\n\n# max concurrent connections this server support\n# you should set this parameter larger, eg. 10240\n# default value is 256\nmax_connections = 10240\n\n# config the cluster servers and groups\ncluster_config_filename = cluster.conf\n\n# the session config filename\nsession_config_filename = session.conf\n\n\n[cluster]\n# the listen port\nport = 31011\n\n# the network thread count\n# these threads deal network io\n# dispatched by the incoming socket fd\n# default value is 4\nwork_threads = 4\n\n\n[service]\n# the listen port\nport = 31012\n\n# the network thread count\n# these threads deal network io\n# dispatched by the incoming socket fd\n# default value is 4\nwork_threads = 4\n\n\n[admin-generate]\n# the generate mode for creating the admin user when not exist, value list:\n##  first: the new created user will be the first user (no other users)\n##  always: always create this admin user when not exist\n# default value is first\nmode = first\n\n# the admin user to auto generate\n# you should set this parameter to what you want before authd first run\n# default value is admin\nusername = admin\n\n# the filename of the secret key which can be absolute path or relative path\n# the relative path based on the path of this config file,\n# such as /etc/fastcfs/auth/server.conf\n# ${username} will be replaced with the value of above \"username\" parameter\n# Note: this secret key file will be auto generated, do NOT modify it!\nsecret_key_filename = keys/${username}.key\n\n\n[FastDIR]\n# config the FastDIR client config filename\nclient_config_filename = ../fdir/client.conf\n"
  },
  {
    "path": "src/auth/conf/session.conf",
    "content": "# the secret key filename for session validation\n# the file content is 32 bytes hex characters\nvalidate_key_filename = keys/session_validate.key\n"
  },
  {
    "path": "src/auth/server/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I.. -I../common -I../../include\nLIB_PATH = $(LIBS) -lfdirclient -lfastcommon -lserverframe -lfcfsauthclient -lfcfsvoteclient\nTARGET_PATH = $(TARGET_PREFIX)/bin\n\nSTATIC_OBJS = ../common/auth_global.o ../common/auth_proto.o   \\\n              ../common/auth_func.o ../common/server_session.o \\\n              db/dao/dao.o db/dao/func.o db/dao/user.o \\\n              db/dao/storage_pool.o db/dao/granted_pool.o \\\n              db/auth_db.o db/pool_usage_updater.o server_global.o \\\n              server_func.o common_handler.o service_handler.o \\\n              cluster_handler.o session_subscribe.o cluster_relationship.o \\\n              cluster_info.o\n\nALL_PRGS = fcfs_authd\n\nall: $(STATIC_OBJS) $(ALL_PRGS)\n\n$(ALL_PRGS): $(STATIC_OBJS)\n\n.o:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n\ninstall:\n\tmkdir -p $(TARGET_PATH)\n\tcp -f $(ALL_PRGS) $(TARGET_PATH)\n\nclean:\n\trm -f $(STATIC_OBJS) $(ALL_PRGS)\n"
  },
  {
    "path": "src/auth/server/cluster_handler.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//cluster_handler.c\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <sys/stat.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <limits.h>\n#include <fcntl.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"fastcommon/ioevent_loop.h\"\n#include \"sf/sf_util.h\"\n#include \"sf/sf_func.h\"\n#include \"sf/sf_nio.h\"\n#include \"sf/sf_global.h\"\n#include \"common/auth_proto.h\"\n#include \"db/auth_db.h\"\n#include \"server_global.h\"\n#include \"server_func.h\"\n#include \"session_subscribe.h\"\n#include \"cluster_info.h\"\n#include \"cluster_relationship.h\"\n#include \"common_handler.h\"\n#include \"cluster_handler.h\"\n\nint cluster_handler_init()\n{\n    return 0;\n}\n\nint cluster_handler_destroy()\n{   \n    return 0;\n}\n\nint cluster_recv_timeout_callback(struct fast_task_info *task)\n{\n    if (SERVER_TASK_TYPE == AUTH_SERVER_TASK_TYPE_RELATIONSHIP &&\n            CLUSTER_PEER != NULL)\n    {\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"cluster client ip: %s, server id: %d, recv timeout\",\n                __LINE__, task->client_ip, CLUSTER_PEER->server->id);\n        return ETIMEDOUT;\n    } else if (SERVER_TASK_TYPE == AUTH_SERVER_TASK_TYPE_SUBSCRIBE &&\n            SESSION_SUBSCRIBER != NULL)\n    {\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"subscribe client ip: %s, recv timeout\",\n                __LINE__, task->client_ip);\n        return ETIMEDOUT;\n    }\n\n    return 0;\n}\n\nstatic void session_subscriber_cleanup(AuthServerContext *server_ctx,\n        ServerSessionSubscriber *subscriber)\n{\n    session_subscribe_unregister(subscriber);\n\n    if (__sync_bool_compare_and_swap(&subscriber->nio.in_queue, 1, 0)) {\n        locked_list_del(&subscriber->nio.dlink,\n                &server_ctx->cluster.subscribers);\n    }\n\n    session_subscribe_release(subscriber);\n}\n\nvoid cluster_task_finish_cleanup(struct fast_task_info *task)\n{\n    char formatted_ip[FORMATTED_IP_SIZE];\n    switch (SERVER_TASK_TYPE) {\n        case AUTH_SERVER_TASK_TYPE_RELATIONSHIP:\n            if (CLUSTER_PEER != NULL) {\n                CLUSTER_PEER->is_online = false;\n                cluster_relationship_add_to_inactive_sarray(CLUSTER_PEER);\n                CLUSTER_PEER = NULL;\n            } else {\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"mistake happen! task: %p, SERVER_TASK_TYPE: %d, \"\n                        \"CLUSTER_PEER is NULL\", __LINE__, task,\n                        SERVER_TASK_TYPE);\n            }\n            SERVER_TASK_TYPE = SF_SERVER_TASK_TYPE_NONE;\n            break;\n        case AUTH_SERVER_TASK_TYPE_SUBSCRIBE:\n            if (SESSION_SUBSCRIBER != NULL) {\n                session_subscriber_cleanup(SERVER_CTX, SESSION_SUBSCRIBER);\n                SESSION_SUBSCRIBER = NULL;\n            }\n\n            format_ip_address(task->client_ip, formatted_ip);\n            logInfo(\"file: \"__FILE__\", line: %d, \"\n                    \"session subscriber %s:%u offline\", __LINE__,\n                    formatted_ip, task->port);\n            SERVER_TASK_TYPE = SF_SERVER_TASK_TYPE_NONE;\n            break;\n        default:\n            break;\n    }\n\n    sf_task_finish_clean_up(task);\n}\n\nvoid cluster_subscriber_queue_push_ex(ServerSessionSubscriber *subscriber,\n        const bool notify)\n{\n    AuthServerContext *server_ctx;\n\n    if (__sync_bool_compare_and_swap(&subscriber->nio.in_queue, 0, 1)) {\n        server_ctx = (AuthServerContext *)subscriber->\n            nio.task->thread_data->arg;\n        locked_list_add_tail(&subscriber->nio.dlink,\n                &server_ctx->cluster.subscribers);\n        if (notify) {\n            ioevent_notify_thread(subscriber->nio.task->thread_data);\n        }\n    }\n}\n\nstatic inline ServerSessionSubscriber *cluster_subscriber_queue_pop(\n        AuthServerContext *server_context)\n{\n    ServerSessionSubscriber *subscriber;\n\n    PTHREAD_MUTEX_LOCK(&server_context->cluster.subscribers.lock);\n    subscriber = fc_list_first_entry(&server_context->cluster.subscribers.head,\n            ServerSessionSubscriber, nio.dlink);\n    if (subscriber != NULL) {\n        fc_list_del_init(&subscriber->nio.dlink);\n        __sync_bool_compare_and_swap(&subscriber->nio.in_queue, 1, 0);\n    }\n    PTHREAD_MUTEX_UNLOCK(&server_context->cluster.subscribers.lock);\n\n    return subscriber;\n}\n\nstatic int cluster_deal_session_subscribe(struct fast_task_info *task)\n{\n    FCFSAuthProtoSessionSubscribeReq *req;\n    const DBUserInfo *dbuser;\n    char formatted_ip[FORMATTED_IP_SIZE];\n    string_t username;\n    string_t passwd;\n    int result;\n\n    if ((result=server_check_min_body_length(sizeof(\n                        FCFSAuthProtoSessionSubscribeReq) + 1)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoSessionSubscribeReq *)REQUEST.body;\n    FC_SET_STRING_EX(username, req->up_pair.username.str,\n            req->up_pair.username.len);\n    FC_SET_STRING_EX(passwd, req->up_pair.passwd,\n            FCFS_AUTH_PASSWD_LEN);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoSessionSubscribeReq)\n                    + username.len)) != 0)\n    {\n        return result;\n    }\n\n    if (SERVER_TASK_TYPE != SF_SERVER_TASK_TYPE_NONE) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"task type: %d != %d\", SERVER_TASK_TYPE,\n                SF_SERVER_TASK_TYPE_NONE);\n        return EEXIST;\n    }\n\n    if (!((dbuser=adb_user_get(SERVER_CTX, &username)) != NULL &&\n            fc_string_equal(&dbuser->user.passwd, &passwd)))\n    {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"user login fail, username or password not correct\");\n        return EPERM;\n    }\n\n    if ((dbuser->user.priv & FCFS_AUTH_USER_PRIV_SUBSCRIBE_SESSION) == 0) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"username: %.*s, permission denied for session \"\n                \"subscription\", username.len, username.str);\n        return EPERM;\n    }\n\n    if ((SESSION_SUBSCRIBER=session_subscribe_alloc()) == NULL) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"session subscribe register fail\");\n        return ENOMEM;\n    }\n\n\n    SESSION_SUBSCRIBER->nio.task = task;\n    session_subscribe_register(SESSION_SUBSCRIBER);\n    if (!fc_queue_empty(&SESSION_SUBSCRIBER->queue)) {\n        cluster_subscriber_queue_push_ex(SESSION_SUBSCRIBER, false);\n    }\n\n    format_ip_address(task->client_ip, formatted_ip);\n    logInfo(\"file: \"__FILE__\", line: %d, \"\n            \"session subscriber %s:%u joined\", __LINE__,\n            formatted_ip, task->port);\n\n    SERVER_TASK_TYPE = AUTH_SERVER_TASK_TYPE_SUBSCRIBE;\n    RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_RESP;\n    return 0;\n}\n\nstatic int session_validate(const int64_t session_id,\n        const FCFSAuthValidatePriviledgeType priv_type,\n        const int64_t pool_id, const int64_t priv_required)\n{\n    int result;\n    int64_t session_priv;\n    ServerSessionFields fields;\n\n    if ((result=server_session_get_fields(session_id, &fields)) != 0) {\n        return result;\n    }\n\n    switch (priv_type) {\n        case fcfs_auth_validate_priv_type_user:\n            session_priv = (fields.dbuser != NULL) ? fields.dbuser->\n                user.priv : (FCFS_AUTH_USER_PRIV_MONITOR_CLUSTER |\n                        FCFS_AUTH_USER_PRIV_SUBSCRIBE_SESSION);\n            break;\n        case fcfs_auth_validate_priv_type_pool_fdir:\n            /*\n            //TODO\n            if (fields.dbpool != NULL) {\n                if (fields.dbpool->pool.id != pool_id) {\n                    return EACCES;\n                }\n            }\n            */\n            session_priv = fields.pool_privs.fdir;\n            break;\n        case fcfs_auth_validate_priv_type_pool_fstore:\n            session_priv = fields.pool_privs.fstore;\n            break;\n        default:\n            session_priv = 0;\n            break;\n    }\n\n    /*\n    logInfo(\"session_id: %\"PRId64\", session_priv: %\"PRId64\", \"\n            \"priv_required: %\"PRId64\", check result: %d\",\n            session_id, session_priv, priv_required,\n            ((session_priv & priv_required) == priv_required) ? 0 : EPERM);\n            */\n\n    return ((session_priv & priv_required) == priv_required) ? 0 : EPERM;\n}\n\nstatic int cluster_deal_session_validate(struct fast_task_info *task)\n{\n    FCFSAuthProtoSessionValidateReq *req;\n    FCFSAuthProtoSessionValidateResp *resp;\n    int64_t session_id;\n    int64_t pool_id;\n    string_t validate_key;\n    int64_t priv_required;\n    FCFSAuthValidatePriviledgeType priv_type;\n    int result;\n\n    if ((result=server_expect_body_length(sizeof(*req))) != 0) {\n        return result;\n    }\n\n    req = (FCFSAuthProtoSessionValidateReq *)REQUEST.body;\n    session_id = buff2long(req->session_id);\n    FC_SET_STRING_EX(validate_key, req->validate_key, FCFS_AUTH_PASSWD_LEN);\n    priv_type = req->priv_type;\n    pool_id = buff2long(req->pool_id);\n    priv_required = buff2long(req->priv_required);\n    if (!fc_string_equal(&validate_key, &g_server_session_cfg.validate_key)) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"session validate key not correct\");\n        return EACCES;\n    }\n\n    if ((result=session_validate(session_id, priv_type, pool_id,\n                    priv_required)) == SF_SESSION_ERROR_NOT_EXIST)\n    {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"session id: %\"PRId64\" not exist\", session_id);\n        TASK_CTX.common.log_level = LOG_WARNING;\n        return result;\n    }\n\n    resp = (FCFSAuthProtoSessionValidateResp *)SF_PROTO_SEND_BODY(task);\n    int2buff(result, resp->result);\n    RESPONSE.header.body_len = sizeof(*resp);\n    RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_RESP;\n    TASK_ARG->context.common.response_done = true;\n    return 0;\n}\n\nstatic int cluster_check_config_sign(struct fast_task_info *task,\n        const int server_id, const char *config_sign)\n{\n    if (memcmp(config_sign, CLUSTER_CONFIG_SIGN_BUF,\n                SF_CLUSTER_CONFIG_SIGN_LEN) != 0)\n    {\n        char peer_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1];\n        char my_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1];\n\n        bin2hex(config_sign, SF_CLUSTER_CONFIG_SIGN_LEN, peer_hex);\n        bin2hex((const char *)CLUSTER_CONFIG_SIGN_BUF,\n                SF_CLUSTER_CONFIG_SIGN_LEN, my_hex);\n\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"server #%d 's cluster config md5: %s != mine: %s\",\n                server_id, peer_hex, my_hex);\n        return EFAULT;\n    }\n\n    return 0;\n}\n\nstatic int cluster_deal_get_server_status(struct fast_task_info *task)\n{\n    int result;\n    int server_id;\n    FCFSAuthProtoGetServerStatusReq *req;\n    FCFSAuthProtoGetServerStatusResp *resp;\n\n    if ((result=server_expect_body_length(sizeof(\n                        FCFSAuthProtoGetServerStatusReq))) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoGetServerStatusReq *)REQUEST.body;\n    server_id = buff2int(req->server_id);\n    if ((result=cluster_check_config_sign(task, server_id,\n                    req->config_sign)) != 0)\n    {\n        return result;\n    }\n\n    resp = (FCFSAuthProtoGetServerStatusResp *)SF_PROTO_SEND_BODY(task);\n    resp->is_master = MYSELF_IS_MASTER;\n    int2buff(CLUSTER_MY_SERVER_ID, resp->server_id);\n\n    RESPONSE.header.body_len = sizeof(FCFSAuthProtoGetServerStatusResp);\n    RESPONSE.header.cmd = FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_RESP;\n    TASK_CTX.common.response_done = true;\n    return 0;\n}\n\nstatic int cluster_deal_join_master(struct fast_task_info *task)\n{\n    int result;\n    int server_id;\n    FCFSAuthProtoJoinMasterReq *req;\n    FCFSAuthClusterServerInfo *peer;\n\n    if ((result=server_expect_body_length(sizeof(\n                        FCFSAuthProtoJoinMasterReq))) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoJoinMasterReq *)REQUEST.body;\n    server_id = buff2int(req->server_id);\n    peer = fcfs_auth_get_server_by_id(server_id);\n    if (peer == NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"peer server id: %d not exist\", server_id);\n        return ENOENT;\n    }\n\n    if ((result=cluster_check_config_sign(task, server_id,\n                    req->config_sign)) != 0)\n    {\n        return result;\n    }\n\n    if (CLUSTER_MYSELF_PTR != CLUSTER_MASTER_ATOM_PTR &&\n            CLUSTER_MYSELF_PTR != CLUSTER_NEXT_MASTER)\n    {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"i am not master\");\n        return SF_RETRIABLE_ERROR_NOT_MASTER;\n    }\n\n    if (peer == CLUSTER_MYSELF_PTR) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"can't join self\");\n        return EINVAL;\n    }\n\n    if (CLUSTER_PEER != NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"peer server id: %d already joined\", server_id);\n        return EEXIST;\n    }\n\n    SERVER_TASK_TYPE = AUTH_SERVER_TASK_TYPE_RELATIONSHIP;\n    CLUSTER_PEER = peer;\n    CLUSTER_PEER->is_online = true;\n    cluster_relationship_remove_from_inactive_sarray(peer);\n    return 0;\n}\n\nstatic int cluster_deal_ping_master(struct fast_task_info *task)\n{\n    int result;\n\n    if ((result=server_expect_body_length(0)) != 0) {\n        return result;\n    }\n\n    if (CLUSTER_PEER == NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"please join first\");\n        return EINVAL;\n    }\n\n    if (CLUSTER_MYSELF_PTR != CLUSTER_MASTER_ATOM_PTR &&\n            CLUSTER_MYSELF_PTR != CLUSTER_NEXT_MASTER)\n    {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"i am not master\");\n        return SF_RETRIABLE_ERROR_NOT_MASTER;\n    }\n\n    RESPONSE.header.cmd = FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_RESP;\n    return 0;\n}\n\nstatic int cluster_deal_next_master(struct fast_task_info *task)\n{\n    int result;\n    int master_id;\n    FCFSAuthClusterServerInfo *master;\n\n    if ((result=server_expect_body_length(4)) != 0) {\n        return result;\n    }\n\n    if (CLUSTER_MYSELF_PTR == CLUSTER_MASTER_ATOM_PTR) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"i am already master\");\n        cluster_relationship_trigger_reselect_master();\n        return EEXIST;\n    }\n\n    master_id = buff2int(REQUEST.body);\n    master = fcfs_auth_get_server_by_id(master_id);\n    if (master == NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"master server id: %d not exist\", master_id);\n        return ENOENT;\n    }\n\n    if (REQUEST.header.cmd == FCFS_AUTH_CLUSTER_PROTO_PRE_SET_NEXT_MASTER) {\n        return cluster_relationship_pre_set_master(master);\n    } else {\n        return cluster_relationship_commit_master(master);\n    }\n}\n\nstatic int cluster_process(struct fast_task_info *task)\n{\n    int result;\n    int cmd;\n\n    cmd = REQUEST.header.cmd;\n    if (!MYSELF_IS_MASTER) {\n        if (cmd == FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_REQ) {\n            RESPONSE.error.length = sprintf(\n                    RESPONSE.error.message,\n                    \"i am not master\");\n            return SF_RETRIABLE_ERROR_NOT_MASTER;\n        }\n\n        if (cmd == FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_REQ) {\n            if (CLUSTER_MYSELF_PTR != CLUSTER_NEXT_MASTER) {\n                RESPONSE.error.length = sprintf(\n                        RESPONSE.error.message,\n                        \"i am not the next master\");\n                return SF_RETRIABLE_ERROR_NOT_MASTER;\n            }\n        }\n    }\n\n    switch (cmd) {\n        case SF_PROTO_ACTIVE_TEST_REQ:\n            if (SERVER_TASK_TYPE == AUTH_SERVER_TASK_TYPE_SUBSCRIBE &&\n                    !MYSELF_IS_MASTER)\n            {\n                RESPONSE.error.length = sprintf(\n                        RESPONSE.error.message,\n                        \"i am not master\");\n                return SF_RETRIABLE_ERROR_NOT_MASTER;\n            }\n            RESPONSE.header.cmd = SF_PROTO_ACTIVE_TEST_RESP;\n            return sf_proto_deal_active_test(task, &REQUEST, &RESPONSE);\n        case FCFS_AUTH_SERVICE_PROTO_SESSION_SUBSCRIBE_REQ:\n            return  cluster_deal_session_subscribe(task);\n        case FCFS_AUTH_SERVICE_PROTO_SESSION_VALIDATE_REQ:\n            return cluster_deal_session_validate(task);\n        case FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_REQ:\n            return cluster_deal_get_server_status(task);\n        case FCFS_AUTH_CLUSTER_PROTO_PRE_SET_NEXT_MASTER:\n        case FCFS_AUTH_CLUSTER_PROTO_COMMIT_NEXT_MASTER:\n            return cluster_deal_next_master(task);\n        case FCFS_AUTH_CLUSTER_PROTO_JOIN_MASTER:\n            return cluster_deal_join_master(task);\n        case FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_REQ:\n            return cluster_deal_ping_master(task);\n        case FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ:\n            if ((result=fcfs_auth_deal_get_master(task,\n                            CLUSTER_GROUP_INDEX)) == 0)\n            {\n                RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_GET_MASTER_RESP;\n            }\n            return result;\n        case SF_SERVICE_PROTO_GET_LEADER_REQ:\n            if ((result=fcfs_auth_deal_get_master(task,\n                            CLUSTER_GROUP_INDEX)) == 0)\n            {\n                RESPONSE.header.cmd = SF_SERVICE_PROTO_GET_LEADER_RESP;\n            }\n            return result;\n        default:\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"unkown cmd: %d\", REQUEST.header.cmd);\n            return -EINVAL;\n    }\n}\n\nint cluster_deal_task(struct fast_task_info *task, const int stage)\n{\n    int result;\n\n    if (stage == SF_NIO_STAGE_CONTINUE) {\n        if (task->continue_callback != NULL) {\n            result = task->continue_callback(task);\n        } else {\n            result = RESPONSE_STATUS;\n            if (result == TASK_STATUS_CONTINUE) {\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"unexpect status: %d\", __LINE__, result);\n                result = EBUSY;\n            }\n        }\n    } else {\n        sf_proto_init_task_context(task, &TASK_CTX.common);\n        result = cluster_process(task);\n    }\n\n    if (result == TASK_STATUS_CONTINUE) {\n        return 0;\n    } else {\n        RESPONSE_STATUS = result;\n        return sf_proto_deal_task_done(task, \"cluster\", &TASK_CTX.common);\n    }\n}\n\nstatic int cluster_deal_queue(AuthServerContext *server_context,\n        ServerSessionSubscriber *subscriber)\n{\n    struct fast_task_info *task;\n    struct fc_queue_info qinfo;\n    ServerSessionSubscribeEntry *previous;\n    ServerSessionSubscribeEntry *entry;\n    FCFSAuthProtoHeader *proto_header;\n    FCFSAuthProtoSessionPushRespBodyHeader *body_header;\n    FCFSAuthProtoSessionPushRespBodyPart *body_part;\n    char *p;\n    char *end;\n    int count;\n    int result;\n\n    task = subscriber->nio.task;\n    if (ioevent_is_canceled(task) || !sf_nio_task_send_done(\n                subscriber->nio.task))\n    {\n        return EAGAIN;\n    }\n\n    fc_queue_try_pop_to_queue(&subscriber->queue, &qinfo);\n    if (qinfo.head == NULL) {\n        return 0;\n    }\n\n    previous = NULL;\n    entry = (ServerSessionSubscribeEntry *)qinfo.head;\n    proto_header = (FCFSAuthProtoHeader *)task->send.ptr->data;\n    body_header = (FCFSAuthProtoSessionPushRespBodyHeader *)\n        (proto_header + 1);\n    p = (char *)(body_header + 1);\n    end = SF_SEND_BUFF_END(task);\n    count = 0;\n    while (entry != NULL) {\n        body_part = (FCFSAuthProtoSessionPushRespBodyPart *)p;\n        if (p + sizeof(FCFSAuthProtoSessionPushRespBodyPart) +\n            sizeof(FCFSAuthProtoSessionPushEntry) > end)\n        {\n            break;\n        }\n\n        long2buff(entry->session_id, body_part->session_id);\n        body_part->operation = entry->operation;\n        if (entry->operation == FCFS_AUTH_SESSION_OP_TYPE_CREATE) {\n            long2buff(entry->fields.user.id, body_part->entry->user.id);\n            long2buff(entry->fields.user.priv, body_part->entry->user.priv);\n            long2buff(entry->fields.pool.id, body_part->entry->pool.id);\n            body_part->entry->pool.available = entry->fields.pool.available;\n            int2buff(entry->fields.pool.privs.fdir,\n                    body_part->entry->pool.privs.fdir);\n            int2buff(entry->fields.pool.privs.fstore,\n                    body_part->entry->pool.privs.fstore);\n\n            p += sizeof(FCFSAuthProtoSessionPushRespBodyPart) +\n                sizeof(FCFSAuthProtoSessionPushEntry);\n        } else {\n            p += sizeof(FCFSAuthProtoSessionPushRespBodyPart);\n        }\n\n        ++count;\n        previous = entry;\n        entry = entry->next;\n    }\n\n    if (entry == NULL) {\n        result = 0;\n    } else {\n        struct fc_queue_info remain_qinfo;\n\n        previous->next = NULL;\n        remain_qinfo.head = entry;\n        remain_qinfo.tail = qinfo.tail;\n        fc_queue_push_queue_to_head_silence(\n                &subscriber->queue, &remain_qinfo);\n        result = EAGAIN;\n    }\n    session_subscribe_free_entries(qinfo.head);\n\n    int2buff(count, body_header->count);\n    task->send.ptr->length = p - task->send.ptr->data;\n    SF_PROTO_SET_HEADER(proto_header, FCFS_AUTH_SERVICE_PROTO_SESSION_PUSH_REQ,\n            task->send.ptr->length - sizeof(FCFSAuthProtoHeader));\n    sf_send_add_event(task);\n    return result;\n}\n\nint cluster_thread_loop_callback(struct nio_thread_data *thread_data)\n{\n    AuthServerContext *server_context;\n    ServerSessionSubscriber *subscriber;\n    int count;\n    int i;\n\n    server_context = (AuthServerContext *)thread_data->arg;\n    count = locked_list_count(&server_context->cluster.subscribers);\n    for (i=0; i<count; i++) {\n        if ((subscriber=cluster_subscriber_queue_pop(\n                        server_context)) == NULL)\n        {\n            break;\n        }\n\n        if (cluster_deal_queue(server_context, subscriber) != 0) {\n            cluster_subscriber_queue_push_ex(subscriber, false);\n        }\n    }\n\n    return 0;\n}\n\nvoid *cluster_alloc_thread_extra_data(const int thread_index)\n{\n    int alloc_size;\n    AuthServerContext *server_context;\n\n    alloc_size = sizeof(AuthServerContext);\n    server_context = (AuthServerContext *)fc_malloc(alloc_size);\n    if (server_context == NULL) {\n        return NULL;\n    }\n\n    memset(server_context, 0, alloc_size);\n    if (locked_list_init(&server_context->cluster.subscribers) != 0) {\n        return NULL;\n    }\n\n    return server_context;\n}\n"
  },
  {
    "path": "src/auth/server/cluster_handler.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//cluster_handler.h\n\n#ifndef FCFS_AUTH_CLUSTER_HANDLER_H\n#define FCFS_AUTH_CLUSTER_HANDLER_H\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"fastcommon/fast_task_queue.h\"\n#include \"server_types.h\"\n\n#ifdef __cplusplus\n\nextern \"C\" {\n#endif\n\nint cluster_handler_init();\nint cluster_handler_destroy();\nint cluster_deal_task(struct fast_task_info *task, const int stage);\nvoid cluster_task_finish_cleanup(struct fast_task_info *task);\nvoid *cluster_alloc_thread_extra_data(const int thread_index);\nint cluster_thread_loop_callback(struct nio_thread_data *thread_data);\nint cluster_recv_timeout_callback(struct fast_task_info *task);\n\nvoid cluster_subscriber_queue_push_ex(ServerSessionSubscriber *subscriber,\n        const bool notify);\n\n#define cluster_subscriber_queue_push(subscriber)  \\\n    cluster_subscriber_queue_push_ex(subscriber, true)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/cluster_info.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <limits.h>\n#include <pthread.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"fastcommon/local_ip_func.h\"\n#include \"server_global.h\"\n#include \"cluster_info.h\"\n\nstatic int init_cluster_server_array()\n{\n    int bytes;\n    FCFSAuthClusterServerInfo *cs;\n    FCServerInfo *server;\n    FCServerInfo *end;\n\n    bytes = sizeof(FCFSAuthClusterServerInfo) *\n        FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG);\n    if ((CLUSTER_SERVER_ARRAY.servers=(FCFSAuthClusterServerInfo *)\n                fc_malloc(bytes)) == NULL)\n    {\n        return ENOMEM;\n    }\n    memset(CLUSTER_SERVER_ARRAY.servers, 0, bytes);\n\n    end = FC_SID_SERVERS(CLUSTER_SERVER_CONFIG) +\n        FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG);\n    for (server=FC_SID_SERVERS(CLUSTER_SERVER_CONFIG),\n            cs=CLUSTER_SERVER_ARRAY.servers; server<end; server++, cs++)\n    {\n        cs->server = server;\n    }\n\n    CLUSTER_SERVER_ARRAY.count = FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG);\n    return 0;\n}\n\nstatic int find_myself_in_cluster_config(const char *filename)\n{\n    const char *local_ip;\n    struct {\n        const char *ip_addr;\n        int port;\n    } found;\n    FCServerInfo *server;\n    FCFSAuthClusterServerInfo *myself;\n    SFNetworkHandler *service_handler;\n    SFNetworkHandler *cluster_handler;\n    char formatted_found_ip[FORMATTED_IP_SIZE];\n    char formatted_local_ip[FORMATTED_IP_SIZE];\n    int ports[4];\n    int count;\n    int i;\n\n    service_handler = SERVICE_SF_CTX.handlers[SF_IPV4_ADDRESS_FAMILY_INDEX].\n        handlers + SF_SOCKET_NETWORK_HANDLER_INDEX;\n    cluster_handler = CLUSTER_SF_CTX.handlers[SF_IPV4_ADDRESS_FAMILY_INDEX].\n        handlers + SF_SOCKET_NETWORK_HANDLER_INDEX;\n    count = 0;\n    ports[count++] = service_handler->inner.port;\n    if (service_handler->outer.port != service_handler->inner.port) {\n        ports[count++] = service_handler->outer.port;\n    }\n\n    ports[count++] = cluster_handler->inner.port;\n    if (cluster_handler->outer.port != cluster_handler->inner.port) {\n        ports[count++] = cluster_handler->outer.port;\n    }\n\n    found.ip_addr = NULL;\n    found.port = 0;\n    local_ip = get_first_local_ip();\n    while (local_ip != NULL) {\n        for (i=0; i<count; i++) {\n            server = fc_server_get_by_ip_port(&CLUSTER_SERVER_CONFIG,\n                    local_ip, ports[i]);\n            if (server != NULL) {\n                myself = CLUSTER_SERVER_ARRAY.servers +\n                    (server - FC_SID_SERVERS(CLUSTER_SERVER_CONFIG));\n                if (CLUSTER_MYSELF_PTR == NULL) {\n                    CLUSTER_MYSELF_PTR = myself;\n                } else if (myself != CLUSTER_MYSELF_PTR) {\n                    format_ip_address(found.ip_addr, formatted_found_ip);\n                    format_ip_address(local_ip, formatted_local_ip);\n                    logError(\"file: \"__FILE__\", line: %d, \"\n                            \"cluster config file: %s, my ip and port \"\n                            \"in more than one instances, %s:%u in \"\n                            \"server id %d, and %s:%u in server id %d\",\n                            __LINE__, filename, formatted_found_ip, found.port,\n                            CLUSTER_MY_SERVER_ID, formatted_local_ip,\n                            ports[i], myself->server->id);\n                    return EEXIST;\n                }\n\n                found.ip_addr = local_ip;\n                found.port = ports[i];\n            }\n        }\n\n        local_ip = get_next_local_ip(local_ip);\n    }\n\n    if (CLUSTER_MYSELF_PTR == NULL) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"cluster config file: %s, can't find myself \"\n                \"by my local ip and listen port\", __LINE__, filename);\n        return ENOENT;\n    }\n\n    return 0;\n}\n\nFCFSAuthClusterServerInfo *fcfs_auth_get_server_by_id(const int server_id)\n{\n    FCServerInfo *server;\n    server = fc_server_get_by_id(&CLUSTER_SERVER_CONFIG, server_id);\n    if (server == NULL) {\n        return NULL;\n    }\n\n    return CLUSTER_SERVER_ARRAY.servers + (server -\n            FC_SID_SERVERS(CLUSTER_SERVER_CONFIG));\n}\n\nint cluster_info_init(const char *cluster_config_filename)\n{\n    int result;\n\n    if ((result=init_cluster_server_array()) != 0) {\n        return result;\n    }\n\n    if ((result=find_myself_in_cluster_config(cluster_config_filename)) != 0) {\n        return result;\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/server/cluster_info.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//cluster_info.h\n\n#ifndef _CLUSTER_INFO_H_\n#define _CLUSTER_INFO_H_\n\n#include <time.h>\n#include <pthread.h>\n#include \"server_global.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint cluster_info_init(const char *cluster_config_filename);\nint cluster_info_destroy();\n\nFCFSAuthClusterServerInfo *fcfs_auth_get_server_by_id(const int server_id);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/cluster_relationship.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"sf/sf_configs.h\"\n#include \"sf/sf_service.h\"\n#include \"sf/sf_func.h\"\n#include \"fastcfs/vote/fcfs_vote_client.h\"\n#include \"common/auth_proto.h\"\n#include \"db/auth_db.h\"\n#include \"db/pool_usage_updater.h\"\n#include \"server_global.h\"\n#include \"session_subscribe.h\"\n#include \"cluster_relationship.h\"\n\n#define NEED_REQUEST_VOTE_NODE(active_count) \\\n    SF_ELECTION_QUORUM_NEED_REQUEST_VOTE_NODE(MASTER_ELECTION_QUORUM, \\\n            VOTE_NODE_ENABLED, CLUSTER_SERVER_ARRAY.count, active_count)\n\n#define NEED_CHECK_VOTE_NODE() \\\n    SF_ELECTION_QUORUM_NEED_CHECK_VOTE_NODE(MASTER_ELECTION_QUORUM, \\\n            VOTE_NODE_ENABLED, CLUSTER_SERVER_ARRAY.count)\n\ntypedef struct fcfs_auth_cluster_server_status {\n    FCFSAuthClusterServerInfo *cs;\n    bool is_master;\n    int server_id;\n} FCFSAuthClusterServerStatus;\n\ntypedef struct fcfs_auth_cluster_server_detect_entry {\n    FCFSAuthClusterServerInfo *cs;\n    int next_time;\n} FCFSAuthClusterServerDetectEntry;\n\ntypedef struct fcfs_auth_cluster_server_detect_array {\n    FCFSAuthClusterServerDetectEntry *entries;\n    int count;\n    int alloc;\n    pthread_mutex_t lock;\n} FCFSAuthClusterServerDetectArray;\n\ntypedef struct fcfs_auth_cluster_relationship_context {\n    FCFSAuthClusterServerDetectArray inactive_server_array;\n    ConnectionInfo vote_connection;\n} FCFSAuthClusterRelationshipContext;\n\n#define INACTIVE_SERVER_ARRAY relationship_ctx.inactive_server_array\n#define VOTE_CONNECTION relationship_ctx.vote_connection\n\nstatic FCFSAuthClusterRelationshipContext relationship_ctx = {\n    {NULL, 0, 0}\n};\n\n#define SET_SERVER_DETECT_ENTRY(entry, server) \\\n    do {  \\\n        entry->cs = server;    \\\n        entry->next_time = g_current_time + 1; \\\n    } while (0)\n\nstatic inline void proto_unpack_server_status(\n        FCFSAuthProtoGetServerStatusResp *resp,\n        FCFSAuthClusterServerStatus *server_status)\n{\n    server_status->is_master = resp->is_master;\n    server_status->server_id = buff2int(resp->server_id);\n}\n\nstatic int proto_get_server_status(ConnectionInfo *conn,\n        const int network_timeout,\n        FCFSAuthClusterServerStatus *server_status)\n{\n\tint result;\n\tFCFSAuthProtoHeader *header;\n    FCFSAuthProtoGetServerStatusReq *req;\n    FCFSAuthProtoGetServerStatusResp *resp;\n    SFResponseInfo response;\n\tchar out_buff[sizeof(FCFSAuthProtoHeader) +\n        sizeof(FCFSAuthProtoGetServerStatusReq)];\n\tchar in_body[sizeof(FCFSAuthProtoGetServerStatusResp)];\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_REQ,\n            sizeof(out_buff) - sizeof(FCFSAuthProtoHeader));\n\n    req = (FCFSAuthProtoGetServerStatusReq *)(out_buff +\n            sizeof(FCFSAuthProtoHeader));\n    int2buff(CLUSTER_MY_SERVER_ID, req->server_id);\n    memcpy(req->config_sign, CLUSTER_CONFIG_SIGN_BUF,\n            SF_CLUSTER_CONFIG_SIGN_LEN);\n\n    response.error.length = 0;\n\tif ((result=sf_send_and_check_response_header(conn, out_buff,\n\t\t\tsizeof(out_buff), &response, network_timeout,\n            FCFS_AUTH_CLUSTER_PROTO_GET_SERVER_STATUS_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n        return result;\n    }\n\n    if (response.header.body_len != sizeof(FCFSAuthProtoGetServerStatusResp)) {\n        format_ip_address(conn->ip_addr, formatted_ip);\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"server %s:%u, recv body length: %d != %d\",\n                __LINE__, formatted_ip, conn->port,\n                response.header.body_len, (int)\n                sizeof(FCFSAuthProtoGetServerStatusResp));\n        return EINVAL;\n    }\n\n    if ((result=tcprecvdata_nb(conn->sock, in_body, response.\n                    header.body_len, network_timeout)) != 0)\n    {\n        format_ip_address(conn->ip_addr, formatted_ip);\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"recv from server %s:%u fail, \"\n                \"errno: %d, error info: %s\",\n                __LINE__, formatted_ip, conn->port,\n                result, STRERROR(result));\n        return result;\n    }\n\n    resp = (FCFSAuthProtoGetServerStatusResp *)in_body;\n    proto_unpack_server_status(resp, server_status);\n    return 0;\n}\n\nstatic void init_inactive_server_array()\n{\n    FCFSAuthClusterServerInfo *cs;\n    FCFSAuthClusterServerInfo *end;\n    FCFSAuthClusterServerDetectEntry *entry;\n\n    PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock);\n    entry = INACTIVE_SERVER_ARRAY.entries;\n    end = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count;\n    for (cs=CLUSTER_SERVER_ARRAY.servers; cs<end; cs++) {\n        if (cs != CLUSTER_MYSELF_PTR) {\n            SET_SERVER_DETECT_ENTRY(entry, cs);\n            entry++;\n        }\n    }\n\n    INACTIVE_SERVER_ARRAY.count = entry - INACTIVE_SERVER_ARRAY.entries;\n    PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock);\n}\n\nstatic inline bool cluster_unset_master(FCFSAuthClusterServerInfo *master)\n{\n    return __sync_bool_compare_and_swap(&CLUSTER_MASTER_PTR, master, NULL);\n}\n\nstatic int proto_join_master(ConnectionInfo *conn, const int network_timeout)\n{\n\tint result;\n\tFCFSAuthProtoHeader *header;\n    FCFSAuthProtoJoinMasterReq *req;\n    SFResponseInfo response;\n\tchar out_buff[sizeof(FCFSAuthProtoHeader) + sizeof(FCFSAuthProtoJoinMasterReq)];\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_AUTH_CLUSTER_PROTO_JOIN_MASTER,\n            sizeof(out_buff) - sizeof(FCFSAuthProtoHeader));\n\n    req = (FCFSAuthProtoJoinMasterReq *)(out_buff + sizeof(FCFSAuthProtoHeader));\n    int2buff(CLUSTER_MY_SERVER_ID, req->server_id);\n    memcpy(req->config_sign, CLUSTER_CONFIG_SIGN_BUF,\n            SF_CLUSTER_CONFIG_SIGN_LEN);\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff,\n                    sizeof(out_buff), &response, network_timeout,\n                    SF_PROTO_ACK)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nstatic int proto_ping_master(ConnectionInfo *conn, const int network_timeout)\n{\n    FCFSAuthProtoHeader header;\n    SFResponseInfo response;\n    int result;\n\n    SF_PROTO_SET_HEADER(&header, FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_REQ, 0);\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, (char *)&header,\n                    sizeof(header), &response, network_timeout,\n                    FCFS_AUTH_CLUSTER_PROTO_PING_MASTER_RESP)) != 0)\n    {\n        auth_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nstatic int cluster_cmp_server_status(const void *p1, const void *p2)\n{\n    FCFSAuthClusterServerStatus *status1;\n    FCFSAuthClusterServerStatus *status2;\n    int sub;\n\n    status1 = (FCFSAuthClusterServerStatus *)p1;\n    status2 = (FCFSAuthClusterServerStatus *)p2;\n    sub = (int)status1->is_master - (int)status2->is_master;\n    if (sub != 0) {\n        return sub;\n    }\n\n    return (int)status1->server_id - (int)status2->server_id;\n}\n\n#define cluster_get_server_status(server_status) \\\n    cluster_get_server_status_ex(server_status, true)\n\nstatic int cluster_get_server_status_ex(FCFSAuthClusterServerStatus\n        *server_status, const bool log_connect_error)\n{\n    const int connect_timeout = 2;\n    const int network_timeout = 2;\n    ConnectionInfo conn;\n    int result;\n\n    if (server_status->cs == CLUSTER_MYSELF_PTR) {\n        server_status->is_master = MYSELF_IS_MASTER;\n        server_status->server_id = CLUSTER_MY_SERVER_ID;\n        return 0;\n    } else {\n        if ((result=fc_server_make_connection_ex(&CLUSTER_GROUP_ADDRESS_ARRAY(\n                            server_status->cs->server), &conn, \"fauth\",\n                        connect_timeout, NULL, log_connect_error)) != 0)\n        {\n            return result;\n        }\n\n        result = proto_get_server_status(&conn,\n                network_timeout, server_status);\n        conn_pool_disconnect_server(&conn);\n        return result;\n    }\n}\n\nstatic int do_check_brainsplit(FCFSAuthClusterServerInfo *cs)\n{\n    int result;\n    const bool log_connect_error = false;\n    FCFSAuthClusterServerStatus server_status;\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n    server_status.cs = cs;\n    server_status.is_master = false;\n    server_status.server_id = 0;\n    if ((result=cluster_get_server_status_ex(&server_status,\n                    log_connect_error)) != 0)\n    {\n        return result;\n    }\n\n    if (server_status.is_master) {\n        format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                    cs->server), formatted_ip);\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"two masters occurs, anonther master id: %d, %s:%u, \"\n                \"trigger re-select master ...\", __LINE__, cs->server->id,\n                formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(cs->server));\n        cluster_relationship_trigger_reselect_master();\n        return EEXIST;\n    }\n\n    return 0;\n}\n\nstatic int cluster_check_brainsplit(int *inactive_count)\n{\n    FCFSAuthClusterServerDetectEntry *entry;\n    FCFSAuthClusterServerDetectEntry *end;\n    int result;\n\n    end = INACTIVE_SERVER_ARRAY.entries + *inactive_count;\n    for (entry=INACTIVE_SERVER_ARRAY.entries; entry<end; entry++) {\n        if (entry >= INACTIVE_SERVER_ARRAY.entries +\n                INACTIVE_SERVER_ARRAY.count)\n        {\n            break;\n        }\n        if (entry->next_time > g_current_time) {\n            continue;\n        }\n\n        result = do_check_brainsplit(entry->cs);\n        if (result == 0) {  //success\n            --(*inactive_count);\n        } else if (result == EEXIST) {  //brain-split occurs\n            return result;\n        }\n\n        entry->next_time = g_current_time + 1;\n    }\n\n    return 0;\n}\n\nstatic inline void fill_join_request(FCFSVoteClientJoinRequest *join_request,\n        const bool persistent)\n{\n    join_request->server_id = CLUSTER_MY_SERVER_ID;\n    join_request->is_leader = (CLUSTER_MYSELF_PTR ==\n            CLUSTER_MASTER_ATOM_PTR ? 1 : 0);\n    join_request->group_id = 1;\n    join_request->response_size = sizeof(FCFSAuthProtoGetServerStatusResp);\n    join_request->service_id = FCFS_VOTE_SERVICE_ID_FAUTH;\n    join_request->persistent = persistent;\n}\n\nstatic inline int vote_node_active_check()\n{\n    int result;\n    FCFSVoteClientJoinRequest join_request;\n\n    if (VOTE_CONNECTION.sock < 0) {\n        fill_join_request(&join_request, true);\n        if ((result=fcfs_vote_client_join(&VOTE_CONNECTION,\n                        &join_request)) != 0)\n        {\n            return result;\n        }\n    }\n\n    if ((result=vote_client_proto_active_check(&VOTE_CONNECTION)) != 0) {\n        vote_client_proto_close_connection(&VOTE_CONNECTION);\n    }\n\n    return result;\n}\n\nstatic int master_check()\n{\n    int result;\n    int active_count;\n    int inactive_count;\n    int vote_node_active;\n\n    if (NEED_CHECK_VOTE_NODE()) {\n        if ((result=vote_node_active_check()) == 0) {\n            vote_node_active = 1;\n        } else {\n            if (result == SF_CLUSTER_ERROR_LEADER_INCONSISTENT) {\n                logWarning(\"file: \"__FILE__\", line: %d, \"\n                        \"trigger re-select master because master \"\n                        \"inconsistent with the vote node\", __LINE__);\n                cluster_relationship_trigger_reselect_master();\n                return EBUSY;\n            }\n            vote_node_active = 0;\n        }\n    } else {\n        vote_node_active = 0;\n    }\n\n\n    PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock);\n    inactive_count = INACTIVE_SERVER_ARRAY.count;\n    PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock);\n    if (inactive_count > 0) {\n        if ((result=cluster_check_brainsplit(&inactive_count)) != 0) {\n            return result;\n        }\n\n        active_count = (CLUSTER_SERVER_ARRAY.count -\n                inactive_count) + vote_node_active;\n        if (!sf_election_quorum_check(MASTER_ELECTION_QUORUM,\n                    VOTE_NODE_ENABLED, CLUSTER_SERVER_ARRAY.count,\n                    active_count))\n        {\n            logWarning(\"file: \"__FILE__\", line: %d, \"\n                    \"trigger re-select master because alive server \"\n                    \"count: %d < half of total server count: %d ...\",\n                    __LINE__, active_count, CLUSTER_SERVER_ARRAY.count);\n            cluster_relationship_trigger_reselect_master();\n            return EBUSY;\n        }\n    }\n\n    return 0;\n}\n\nstatic int get_vote_server_status(FCFSAuthClusterServerStatus *server_status)\n{\n    FCFSVoteClientJoinRequest join_request;\n    SFGetServerStatusRequest status_request;\n    FCFSAuthProtoGetServerStatusResp resp;\n    int result;\n\n    if (VOTE_CONNECTION.sock >= 0) {\n        status_request.servers_sign = CLUSTER_CONFIG_SIGN_BUF;\n        status_request.cluster_sign = NULL;\n        status_request.server_id = CLUSTER_MY_SERVER_ID;\n        status_request.is_leader = (CLUSTER_MYSELF_PTR ==\n                CLUSTER_MASTER_ATOM_PTR ? 1 : 0);\n        result = vote_client_proto_get_vote(&VOTE_CONNECTION,\n                &status_request, (char *)&resp, sizeof(resp));\n        if (result != 0) {\n            vote_client_proto_close_connection(&VOTE_CONNECTION);\n        }\n    } else {\n        fill_join_request(&join_request, false);\n        result = fcfs_vote_client_get_vote(&join_request,\n                CLUSTER_CONFIG_SIGN_BUF, NULL,\n                (char *)&resp, sizeof(resp));\n    }\n\n    if (result == 0) {\n        proto_unpack_server_status(&resp, server_status);\n    }\n    return result;\n}\n\nstatic int notify_vote_next_leader(FCFSAuthClusterServerStatus *server_status,\n        const unsigned char vote_req_cmd)\n{\n    FCFSVoteClientJoinRequest join_request;\n\n    fill_join_request(&join_request, false);\n    return fcfs_vote_client_notify_next_leader(&join_request, vote_req_cmd);\n}\n\nstatic int cluster_get_master(FCFSAuthClusterServerStatus *server_status,\n        const bool log_connect_error, int *active_count)\n{\n#define STATUS_ARRAY_FIXED_COUNT  8\n\tFCFSAuthClusterServerInfo *server;\n\tFCFSAuthClusterServerInfo *end;\n\tFCFSAuthClusterServerStatus *current_status;\n\tFCFSAuthClusterServerStatus *cs_status;\n\tFCFSAuthClusterServerStatus status_array[STATUS_ARRAY_FIXED_COUNT];\n    char formatted_ip[FORMATTED_IP_SIZE];\n\tint result;\n\tint r;\n\tint i;\n\n\tmemset(server_status, 0, sizeof(FCFSAuthClusterServerStatus));\n    if (CLUSTER_SERVER_ARRAY.count < STATUS_ARRAY_FIXED_COUNT) {\n        cs_status = status_array;\n    } else {\n        int bytes;\n        bytes = sizeof(FCFSAuthClusterServerStatus) * CLUSTER_SERVER_ARRAY.count;\n        cs_status = (FCFSAuthClusterServerStatus *)fc_malloc(bytes);\n        if (cs_status == NULL) {\n            return ENOMEM;\n        }\n    }\n\n\tcurrent_status = cs_status;\n\tresult = 0;\n\tend = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count;\n\tfor (server=CLUSTER_SERVER_ARRAY.servers; server<end; server++) {\n\t\tcurrent_status->cs = server;\n        r = cluster_get_server_status_ex(current_status, log_connect_error);\n\t\tif (r == 0) {\n\t\t\tcurrent_status++;\n\t\t} else if (r != ENOENT) {\n\t\t\tresult = r;\n\t\t}\n\t}\n\n\t*active_count = current_status - cs_status;\n    if (*active_count == 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"get server status fail, \"\n                \"server count: %d\", __LINE__,\n                CLUSTER_SERVER_ARRAY.count);\n        return result == 0 ? ENOENT : result;\n    }\n\n    if (NEED_REQUEST_VOTE_NODE(*active_count)) {\n        current_status->cs = NULL;\n        if (get_vote_server_status(current_status) == 0) {\n            ++(*active_count);\n        }\n    }\n\n    qsort(cs_status, *active_count,\n            sizeof(FCFSAuthClusterServerStatus),\n            cluster_cmp_server_status);\n\n    if (FC_LOG_BY_LEVEL(LOG_DEBUG)) {\n        for (i=0; i<*active_count; i++) {\n            if (cs_status[i].cs == NULL) {\n                logDebug(\"file: \"__FILE__\", line: %d, \"\n                        \"%d. status from vote server\", __LINE__, i + 1);\n            } else {\n                format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                           cs_status[i].cs->server), formatted_ip);\n                logDebug(\"file: \"__FILE__\", line: %d, \"\n                        \"server_id: %d, ip and port %s:%u, is_master: %d\",\n                        __LINE__, cs_status[i].server_id, formatted_ip,\n                        CLUSTER_GROUP_ADDRESS_FIRST_PORT(cs_status[i].cs->server),\n                        cs_status[i].is_master);\n            }\n        }\n    }\n\n\tmemcpy(server_status, cs_status + (*active_count - 1),\n\t\t\tsizeof(FCFSAuthClusterServerStatus));\n    if (cs_status != status_array) {\n        free(cs_status);\n    }\n\treturn 0;\n}\n\nstatic int do_notify_master_changed(FCFSAuthClusterServerInfo *cs,\n\t\tFCFSAuthClusterServerInfo *master, const unsigned char cmd,\n        bool *bConnectFail)\n{\n    int connect_timeout;\n    char out_buff[sizeof(FCFSAuthProtoHeader) + 4];\n    ConnectionInfo conn;\n    FCFSAuthProtoHeader *header;\n    SFResponseInfo response;\n    int result;\n\n    connect_timeout = FC_MIN(CLUSTER_CONNECT_TIMEOUT, 2);\n    if ((result=fc_server_make_connection(&CLUSTER_GROUP_ADDRESS_ARRAY(\n                        cs->server), &conn, \"fauth\", connect_timeout)) != 0)\n    {\n        *bConnectFail = true;\n        return result;\n    }\n    *bConnectFail = false;\n\n    header = (FCFSAuthProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, cmd, sizeof(out_buff) -\n            sizeof(FCFSAuthProtoHeader));\n    int2buff(master->server->id, out_buff + sizeof(FCFSAuthProtoHeader));\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(&conn, out_buff,\n                    sizeof(out_buff), &response, CLUSTER_NETWORK_TIMEOUT,\n                    SF_PROTO_ACK)) != 0)\n    {\n        auth_log_network_error(&response, &conn, result);\n    }\n\n    conn_pool_disconnect_server(&conn);\n    return result;\n}\n\nint cluster_relationship_pre_set_master(FCFSAuthClusterServerInfo *master)\n{\n    FCFSAuthClusterServerInfo *next_master;\n\n    next_master = CLUSTER_NEXT_MASTER;\n    if (next_master == NULL) {\n        CLUSTER_NEXT_MASTER = master;\n    } else if (next_master != master) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"try to set next master id: %d, \"\n                \"but next master: %d already exist\",\n                __LINE__, master->server->id, next_master->server->id);\n        CLUSTER_NEXT_MASTER = NULL;\n        return EEXIST;\n    }\n\n    return 0;\n}\n\nstatic int load_data()\n{\n    int result;\n\n    if ((result=adb_load_data((AuthServerContext *)\n                    sf_get_random_thread_data()->arg)) != 0)\n    {\n        return result;\n    }\n    if ((result=adb_check_generate_admin_user((AuthServerContext *)\n                    sf_get_random_thread_data()->arg)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nstatic int cluster_relationship_set_master(FCFSAuthClusterServerInfo\n        *new_master, const time_t start_time)\n{\n    int result;\n    FCFSAuthClusterServerInfo *old_master;\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n    old_master = CLUSTER_MASTER_ATOM_PTR;\n    if (new_master == old_master) {\n        format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                    new_master->server), formatted_ip);\n        logDebug(\"file: \"__FILE__\", line: %d, \"\n                \"the server id: %d, %s:%u already is master\",\n                __LINE__, new_master->server->id, formatted_ip,\n                CLUSTER_GROUP_ADDRESS_FIRST_PORT(new_master->server));\n        return 0;\n    }\n\n    if (CLUSTER_MYSELF_PTR == new_master) {\n        if ((result=load_data()) != 0) {\n            sf_terminate_myself();\n            return result;\n        }\n    } else {\n        char time_used[128];\n        if (start_time > 0) {\n            sprintf(time_used, \", election time used: %ds\",\n                    (int)(g_current_time - start_time));\n        } else {\n            *time_used = '\\0';\n        }\n\n        format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                    new_master->server), formatted_ip);\n        logInfo(\"file: \"__FILE__\", line: %d, \"\n                \"the master server id: %d, %s:%u%s\",\n                __LINE__, new_master->server->id, formatted_ip,\n                CLUSTER_GROUP_ADDRESS_FIRST_PORT(new_master->server),\n                time_used);\n    }\n\n    do {\n        if (__sync_bool_compare_and_swap(&CLUSTER_MASTER_PTR,\n                    old_master, new_master))\n        {\n            break;\n        }\n        old_master = CLUSTER_MASTER_ATOM_PTR;\n    } while (old_master != new_master);\n\n    if (CLUSTER_MYSELF_PTR == new_master) {\n        if ((result=pool_usage_updater_start()) != 0) {\n            sf_terminate_myself();\n            return result;\n        }\n    }\n\n    return 0;\n}\n\nint cluster_relationship_commit_master(FCFSAuthClusterServerInfo *master)\n{\n    const time_t start_time = 0;\n    FCFSAuthClusterServerInfo *next_master;\n    int result;\n\n    next_master = CLUSTER_NEXT_MASTER;\n    if (next_master == NULL) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"next master is NULL\", __LINE__);\n        return EBUSY;\n    }\n    if (next_master != master) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"next master server id: %d != expected server id: %d\",\n                __LINE__, next_master->server->id, master->server->id);\n        CLUSTER_NEXT_MASTER = NULL;\n        return EBUSY;\n    }\n\n    result = cluster_relationship_set_master(master, start_time);\n    CLUSTER_NEXT_MASTER = NULL;\n    return result;\n}\n\nvoid cluster_relationship_trigger_reselect_master()\n{\n    FCFSAuthClusterServerInfo *master;\n\n    master = CLUSTER_MASTER_ATOM_PTR;\n    if (CLUSTER_MYSELF_PTR != master) {\n        return;\n    }\n\n    if (!cluster_unset_master(master)) {\n        return;\n    }\n\n    if (NEED_CHECK_VOTE_NODE()) {\n        vote_client_proto_close_connection(&VOTE_CONNECTION);\n    }\n    session_subscribe_clear_session();\n    server_session_clear();\n    pool_usage_updater_terminate();\n    auth_db_destroy();\n}\n\nstatic int cluster_pre_set_next_master(FCFSAuthClusterServerInfo *cs,\n        FCFSAuthClusterServerStatus *server_status, bool *bConnectFail)\n{\n    FCFSAuthClusterServerInfo *master;\n    int result;\n\n    master = server_status->cs;\n    if (cs == CLUSTER_MYSELF_PTR) {\n        if ((result=cluster_relationship_pre_set_master(master)) == 0) {\n            init_inactive_server_array();\n        }\n        return result;\n    } else {\n        return do_notify_master_changed(cs, master,\n                FCFS_AUTH_CLUSTER_PROTO_PRE_SET_NEXT_MASTER, bConnectFail);\n    }\n}\n\nstatic int cluster_commit_next_master(FCFSAuthClusterServerInfo *cs,\n        FCFSAuthClusterServerStatus *server_status, bool *bConnectFail)\n{\n    FCFSAuthClusterServerInfo *master;\n\n    master = server_status->cs;\n    if (cs == CLUSTER_MYSELF_PTR) {\n        return cluster_relationship_commit_master(master);\n    } else {\n        return do_notify_master_changed(cs, master,\n                FCFS_AUTH_CLUSTER_PROTO_COMMIT_NEXT_MASTER, bConnectFail);\n    }\n}\n\ntypedef int (*cluster_notify_next_master_func)(FCFSAuthClusterServerInfo *cs,\n        FCFSAuthClusterServerStatus *server_status, bool *bConnectFail);\n\nstatic int notify_next_master(cluster_notify_next_master_func notify_func,\n        FCFSAuthClusterServerStatus *server_status, const unsigned\n        char vote_req_cmd, int *success_count)\n{\n\tFCFSAuthClusterServerInfo *server;\n\tFCFSAuthClusterServerInfo *send;\n\tint result;\n\tbool bConnectFail;\n\n\tresult = ENOENT;\n\t*success_count = 0;\n\tsend = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count;\n    for (server=CLUSTER_SERVER_ARRAY.servers; server<send; server++) {\n        if ((result=notify_func(server, server_status, &bConnectFail)) != 0) {\n            if (!bConnectFail) {\n                return result;\n            }\n        } else {\n            ++(*success_count);\n        }\n    }\n\n    if (NEED_CHECK_VOTE_NODE()) {\n        result = notify_vote_next_leader(server_status, vote_req_cmd);\n        if (result == 0) {\n            if (*success_count < CLUSTER_SERVER_ARRAY.count) {\n                ++(*success_count);\n            }\n        } else if (result == SF_CLUSTER_ERROR_LEADER_INCONSISTENT) {\n            return -1 * result;\n        }\n    }\n\n    if (!sf_election_quorum_check(MASTER_ELECTION_QUORUM,\n                    VOTE_NODE_ENABLED, CLUSTER_SERVER_ARRAY.count,\n                    *success_count))\n    {\n        return EAGAIN;\n    }\n\n    return 0;\n}\n\nstatic int cluster_notify_master_changed(\n        FCFSAuthClusterServerStatus *server_status)\n{\n    int result;\n    int success_count;\n    const char *caption;\n\n    if ((result=notify_next_master(cluster_pre_set_next_master, server_status,\n                    FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER,\n                    &success_count)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=notify_next_master(cluster_commit_next_master, server_status,\n                    FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER,\n                    &success_count)) != 0)\n    {\n        if (result == SF_CLUSTER_ERROR_MASTER_INCONSISTENT ||\n                result == -SF_CLUSTER_ERROR_LEADER_INCONSISTENT)\n        {\n            if (result == SF_CLUSTER_ERROR_MASTER_INCONSISTENT) {\n                caption = \"other server\";\n            } else {\n                caption = \"the vote node\";\n                result = SF_CLUSTER_ERROR_MASTER_INCONSISTENT;\n            }\n            logWarning(\"file: \"__FILE__\", line: %d, \"\n                    \"trigger re-select master because master \"\n                    \"inconsistent with %s\", __LINE__, caption);\n        } else {\n            logWarning(\"file: \"__FILE__\", line: %d, \"\n                    \"trigger re-select master because alive server \"\n                    \"count: %d < half of total server count: %d ...\",\n                    __LINE__, success_count, CLUSTER_SERVER_ARRAY.count);\n        }\n        cluster_relationship_trigger_reselect_master();\n    }\n\n    return result;\n}\n\nstatic int cluster_select_master()\n{\n\tint result;\n    int active_count;\n    int i;\n    bool need_log;\n    int max_sleep_secs;\n    int sleep_secs;\n    int remain_time;\n    time_t start_time;\n    time_t last_log_time;\n\tFCFSAuthClusterServerStatus server_status;\n    FCFSAuthClusterServerInfo *next_master;\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n\tlogInfo(\"file: \"__FILE__\", line: %d, \"\n\t\t\"selecting master...\", __LINE__);\n\n    start_time = g_current_time;\n    last_log_time = 0;\n    sleep_secs = 10;\n    max_sleep_secs = 1;\n    i = 0;\n    while (CLUSTER_MASTER_ATOM_PTR == NULL) {\n        if (sleep_secs > 1) {\n            need_log = true;\n            last_log_time = g_current_time;\n        } if (g_current_time - last_log_time > 8) {\n            need_log = ((i + 1) % 10 == 0);\n            if (need_log) {\n                last_log_time = g_current_time;\n            }\n        } else {\n            need_log = false;\n        }\n\n        if ((result=cluster_get_master(&server_status,\n                        need_log, &active_count)) != 0)\n        {\n            return result;\n        }\n\n        ++i;\n        if (!sf_election_quorum_check(MASTER_ELECTION_QUORUM,\n                    VOTE_NODE_ENABLED, CLUSTER_SERVER_ARRAY.count,\n                    active_count))\n        {\n            sleep_secs = 1;\n            if (need_log) {\n                logWarning(\"file: \"__FILE__\", line: %d, \"\n                        \"round %dth select master fail because alive server \"\n                        \"count: %d < half of total server count: %d, \"\n                        \"try again after %d seconds.\", __LINE__, i,\n                        active_count, CLUSTER_SERVER_ARRAY.count,\n                        sleep_secs);\n            }\n            sleep(sleep_secs);\n            continue;\n        }\n\n        if ((active_count == CLUSTER_SERVER_ARRAY.count) ||\n                (active_count >= 2 && server_status.is_master))\n        {\n            break;\n        }\n\n        remain_time = ELECTION_MAX_WAIT_TIME - (g_current_time - start_time);\n        if (remain_time <= 0) {\n            break;\n        }\n\n        sleep_secs = FC_MIN(remain_time, max_sleep_secs);\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"round %dth select master, alive server count: %d \"\n                \"< server count: %d, try again after %d seconds.\",\n                __LINE__, i, active_count, CLUSTER_SERVER_ARRAY.count,\n                sleep_secs);\n\n        sleep(sleep_secs);\n        if ((i % 2 == 0) && (max_sleep_secs < 8)) {\n            max_sleep_secs *= 2;\n        }\n    }\n\n    next_master = CLUSTER_MASTER_ATOM_PTR;\n    if (next_master != NULL) {\n        format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                    next_master->server), formatted_ip);\n        logInfo(\"file: \"__FILE__\", line: %d, \"\n                \"abort election because the master exists, \"\n                \"master id: %d, %s:%u, election time used: %ds\",\n                __LINE__, next_master->server->id, formatted_ip,\n                CLUSTER_GROUP_ADDRESS_FIRST_PORT(next_master->server),\n                (int)(g_current_time - start_time));\n        return 0;\n    }\n\n    next_master = server_status.cs;\n    if (CLUSTER_MYSELF_PTR == next_master) {\n\t\tif ((result=cluster_notify_master_changed(\n                        &server_status)) != 0)\n        {\n            return result;\n        }\n\n        format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                    next_master->server), formatted_ip);\n\t\tlogInfo(\"file: \"__FILE__\", line: %d, \"\n\t\t\t\"I am the new master, id: %d, %s:%u, election \"\n            \"time used: %ds\", __LINE__, next_master->server->id,\n            formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(next_master->\n                server), (int)(g_current_time - start_time));\n    } else {\n        if (server_status.is_master) {\n            cluster_relationship_set_master(next_master, start_time);\n        } else if (CLUSTER_MASTER_ATOM_PTR == NULL) {\n            format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                        next_master->server), formatted_ip);\n            logInfo(\"file: \"__FILE__\", line: %d, \"\n                    \"election time used: %ds, waiting for the candidate \"\n                    \"master server id: %d, %s:%u notify ...\", __LINE__,\n                    (int)(g_current_time - start_time), next_master->server->id,\n                    formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(\n                        next_master->server));\n            return ENOENT;\n        }\n    }\n\n\treturn 0;\n}\n\nstatic int cluster_ping_master(FCFSAuthClusterServerInfo *master,\n        ConnectionInfo *conn, const int timeout, bool *is_ping)\n{\n    int connect_timeout;\n    int network_timeout;\n    int result;\n\n    if (CLUSTER_MYSELF_PTR == master) {\n        *is_ping = false;\n        return master_check();\n    }\n\n    network_timeout = FC_MIN(CLUSTER_NETWORK_TIMEOUT, timeout);\n    *is_ping = true;\n    if (conn->sock < 0) {\n        connect_timeout = FC_MIN(CLUSTER_CONNECT_TIMEOUT, timeout);\n        if ((result=fc_server_make_connection(&CLUSTER_GROUP_ADDRESS_ARRAY(\n                            master->server), conn, \"fauth\",\n                        connect_timeout)) != 0)\n        {\n            return result;\n        }\n\n        if ((result=proto_join_master(conn, network_timeout)) != 0) {\n            conn_pool_disconnect_server(conn);\n            return result;\n        }\n    }\n\n    if ((result=proto_ping_master(conn, network_timeout)) != 0) {\n        conn_pool_disconnect_server(conn);\n    }\n\n    return result;\n}\n\nstatic void *cluster_thread_entrance(void* arg)\n{\n#define MAX_SLEEP_SECONDS  10\n\n    int result;\n    int fail_count;\n    int sleep_seconds;\n    int ping_remain_time;\n    time_t ping_start_time;\n    bool is_ping;\n    FCFSAuthClusterServerInfo *master;\n    ConnectionInfo mconn;  //master connection\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n#ifdef OS_LINUX\n    prctl(PR_SET_NAME, \"relationship\");\n#endif\n\n    memset(&mconn, 0, sizeof(mconn));\n    mconn.sock = -1;\n\n    fail_count = 0;\n    sleep_seconds = 1;\n    ping_start_time = g_current_time;\n    while (SF_G_CONTINUE_FLAG) {\n        master = CLUSTER_MASTER_ATOM_PTR;\n        if (master == NULL) {\n            if (cluster_select_master() != 0) {\n                sleep_seconds = 1 + (int)((double)rand()\n                        * (double)MAX_SLEEP_SECONDS / RAND_MAX);\n            } else {\n                if (mconn.sock >= 0) {\n                    conn_pool_disconnect_server(&mconn);\n                }\n                ping_start_time = g_current_time;\n                sleep_seconds = 1;\n            }\n        } else {\n            ping_remain_time = ELECTION_MASTER_LOST_TIMEOUT -\n                (g_current_time - ping_start_time);\n            if (ping_remain_time < 2) {\n                ping_remain_time = 2;\n            }\n            if ((result=cluster_ping_master(master, &mconn,\n                            ping_remain_time, &is_ping)) == 0)\n            {\n                fail_count = 0;\n                ping_start_time = g_current_time;\n                sleep_seconds = 1;\n            } else if (is_ping) {\n                ++fail_count;\n                format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                        master->server), formatted_ip);\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"%dth ping master id: %d, %s:%u fail\", __LINE__,\n                        fail_count, master->server->id, formatted_ip,\n                        CLUSTER_GROUP_ADDRESS_FIRST_PORT(master->server));\n                if (result == SF_RETRIABLE_ERROR_NOT_MASTER) {\n                    cluster_unset_master(master);\n                    fail_count = 0;\n                    sleep_seconds = 0;\n                } else if (g_current_time - ping_start_time >\n                        ELECTION_MASTER_LOST_TIMEOUT)\n                {\n                    if (fail_count > 1) {\n                        cluster_unset_master(master);\n                        fail_count = 0;\n                    }\n                    sleep_seconds = 0;\n                } else {\n                    sleep_seconds = 1;\n                }\n            } else {\n                sleep_seconds = 0;  //master check fail\n            }\n        }\n\n        if (sleep_seconds > 0) {\n            sleep(sleep_seconds);\n        }\n    }\n\n    return NULL;\n}\n\nint cluster_relationship_init()\n{\n    int result;\n    int bytes;\n    pthread_t tid;\n\n    VOTE_CONNECTION.sock = -1;\n    bytes = sizeof(FCFSAuthClusterServerDetectEntry) * CLUSTER_SERVER_ARRAY.count;\n    INACTIVE_SERVER_ARRAY.entries = (FCFSAuthClusterServerDetectEntry *)\n        fc_malloc(bytes);\n    if (INACTIVE_SERVER_ARRAY.entries == NULL) {\n        return ENOMEM;\n    }\n    if ((result=init_pthread_lock(&INACTIVE_SERVER_ARRAY.lock)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"init_pthread_lock fail, errno: %d, error info: %s\",\n                __LINE__, result, STRERROR(result));\n        return result;\n    }\n    INACTIVE_SERVER_ARRAY.alloc = CLUSTER_SERVER_ARRAY.count;\n\n    return fc_create_thread(&tid, cluster_thread_entrance, NULL,\n            SF_G_THREAD_STACK_SIZE);\n}\n\nint cluster_relationship_destroy()\n{\n\treturn 0;\n}\n\nvoid cluster_relationship_add_to_inactive_sarray(FCFSAuthClusterServerInfo *cs)\n{\n    FCFSAuthClusterServerDetectEntry *entry;\n    FCFSAuthClusterServerDetectEntry *end;\n    bool found;\n\n    found = false;\n    PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock);\n    if (INACTIVE_SERVER_ARRAY.count > 0) {\n        end = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count;\n        for (entry=INACTIVE_SERVER_ARRAY.entries; entry<end; entry++) {\n            if (entry->cs == cs) {\n                found = true;\n                break;\n            }\n        }\n    }\n    if (!found) {\n        if (INACTIVE_SERVER_ARRAY.count < INACTIVE_SERVER_ARRAY.alloc) {\n            entry = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count;\n            SET_SERVER_DETECT_ENTRY(entry, cs);\n            INACTIVE_SERVER_ARRAY.count++;\n        } else {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"server id: %d, add to inactive array fail \"\n                    \"because array is full\", __LINE__, cs->server->id);\n        }\n    } else {\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"server id: %d, already in inactive array!\",\n                __LINE__, cs->server->id);\n    }\n    PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock);\n}\n\nvoid cluster_relationship_remove_from_inactive_sarray(FCFSAuthClusterServerInfo *cs)\n{\n    FCFSAuthClusterServerDetectEntry *entry;\n    FCFSAuthClusterServerDetectEntry *p;\n    FCFSAuthClusterServerDetectEntry *end;\n\n    PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock);\n    end = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count;\n    for (entry=INACTIVE_SERVER_ARRAY.entries; entry<end; entry++) {\n        if (entry->cs == cs) {\n            break;\n        }\n    }\n\n    if (entry < end) {\n        for (p=entry+1; p<end; p++) {\n            *(p - 1) = *p;\n        }\n        INACTIVE_SERVER_ARRAY.count--;\n    }\n    PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock);\n}\n"
  },
  {
    "path": "src/auth/server/cluster_relationship.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//cluster_relationship.h\n\n#ifndef _CLUSTER_RELATIONSHIP_H_\n#define _CLUSTER_RELATIONSHIP_H_\n\n#include <time.h>\n#include <pthread.h>\n#include \"server_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint cluster_relationship_init();\nint cluster_relationship_destroy();\n\nint cluster_relationship_pre_set_master(FCFSAuthClusterServerInfo *master);\n\nint cluster_relationship_commit_master(FCFSAuthClusterServerInfo *master);\n\nvoid cluster_relationship_trigger_reselect_master();\n\nvoid cluster_relationship_add_to_inactive_sarray(FCFSAuthClusterServerInfo *cs);\n\nvoid cluster_relationship_remove_from_inactive_sarray(FCFSAuthClusterServerInfo *cs);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/common_handler.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//common_handler.c\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <sys/stat.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <limits.h>\n#include <fcntl.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"common/auth_proto.h\"\n#include \"server_global.h\"\n#include \"common_handler.h\"\n\n#define LOG_LEVEL_FOR_DEBUG  LOG_DEBUG\n\nstatic int fcfs_auth_get_cmd_log_level(const int cmd)\n{\n    switch (cmd) {\n        case SF_PROTO_ACTIVE_TEST_REQ:\n        case SF_PROTO_ACTIVE_TEST_RESP:\n            return LOG_NOTHING;\n        case SF_SERVICE_PROTO_REPORT_REQ_RECEIPT_REQ:\n            return LOG_DEBUG;\n        default:\n            return LOG_LEVEL_FOR_DEBUG;\n    }\n}\n\nvoid common_handler_init()\n{\n    SFHandlerContext handler_ctx;\n\n    fcfs_auth_proto_init();\n\n    handler_ctx.slow_log = &SLOW_LOG;\n    handler_ctx.callbacks.get_cmd_caption = fcfs_auth_get_cmd_caption;\n    if (FC_LOG_BY_LEVEL(LOG_LEVEL_FOR_DEBUG)) {\n        handler_ctx.callbacks.get_cmd_log_level = fcfs_auth_get_cmd_log_level;\n    } else {\n        handler_ctx.callbacks.get_cmd_log_level = NULL;\n    }\n    sf_proto_set_handler_context(&handler_ctx);\n}\n\nint fcfs_auth_deal_get_master(struct fast_task_info *task,\n        const int group_index)\n{\n    int result;\n    FCFSAuthProtoGetServerResp *resp;\n    FCFSAuthClusterServerInfo *master;\n    const FCAddressInfo *addr;\n\n    if ((result=server_expect_body_length(0)) != 0) {\n        return result;\n    }\n\n    master = (CLUSTER_MASTER_ATOM_PTR != NULL) ?\n        CLUSTER_MASTER_ATOM_PTR : CLUSTER_NEXT_MASTER;\n    if (master == NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"the master NOT exist\");\n        return SF_RETRIABLE_ERROR_NO_SERVER;\n    }\n\n    resp = (FCFSAuthProtoGetServerResp *)SF_PROTO_SEND_BODY(task);\n    if (group_index == SERVICE_GROUP_INDEX) {\n        addr = fc_server_get_address_by_peer(&SERVICE_GROUP_ADDRESS_ARRAY(\n                    master->server), task->client_ip);\n    } else {\n        addr = fc_server_get_address_by_peer(&CLUSTER_GROUP_ADDRESS_ARRAY(\n                    master->server), task->client_ip);\n    }\n\n    int2buff(master->server->id, resp->server_id);\n    fc_safe_strcpy(resp->ip_addr, addr->conn.ip_addr);\n    short2buff(addr->conn.port, resp->port);\n\n    RESPONSE.header.body_len = sizeof(FCFSAuthProtoGetServerResp);\n    TASK_CTX.common.response_done = true;\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/server/common_handler.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//common_handler.h\n\n#ifndef FCFS_AUTH_COMMON_HANDLER_H\n#define FCFS_AUTH_COMMON_HANDLER_H\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"server_types.h\"\n\n#ifdef __cplusplus\n\nextern \"C\" {\n#endif\n\nvoid common_handler_init();\n\nint fcfs_auth_deal_get_master(struct fast_task_info *task,\n        const int group_index);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/db/auth_db.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/uniq_skiplist.h\"\n#include \"fastcommon/fast_allocator.h\"\n#include \"fastcommon/fc_atomic.h\"\n#include \"sf/sf_global.h\"\n#include \"../../common/auth_func.h\"\n#include \"../server_global.h\"\n#include \"dao/user.h\"\n#include \"dao/storage_pool.h\"\n#include \"dao/granted_pool.h\"\n#include \"auth_db.h\"\n\n#define SKIPLIST_INIT_LEVEL_COUNT   2\n\nstatic int auth_db_init();\n\ntypedef struct auth_db_context {\n    pthread_mutex_t lock;\n    struct fast_allocator_context name_acontext;\n    bool inited;\n\n    struct {\n        int count;\n        UniqSkiplistPair sl_pair;   //element: DBUserInfo *\n        struct fast_mblock_man allocator; //element: DBUserInfo\n    } user;\n\n    struct {\n        volatile int64_t auto_id;\n        struct {\n            UniqSkiplistFactory created;\n            UniqSkiplistFactory granted;\n        } factories;\n\n        struct {\n            struct fast_mblock_man created; //element: DBStoragePoolInfo\n            struct fast_mblock_man granted; //element: DBGrantedPoolInfo\n        } allocators;\n\n        struct {\n            UniqSkiplistPair by_id;   //element: DBStoragePoolInfo *\n            UniqSkiplistPair by_name; //element: DBStoragePoolInfo *\n        } sl_pairs;\n    } pool;\n} AuthDBContext;\n\nstatic AuthDBContext adb_ctx;\n\nstatic int user_compare(const DBUserInfo *user1, const DBUserInfo *user2)\n{\n    return fc_string_compare(&user1->user.name, &user2->user.name);\n}\n\nstatic int pool_name_cmp(const DBStoragePoolInfo *sp1,\n        const DBStoragePoolInfo *sp2)\n{\n    return fc_string_compare(&sp1->pool.name, &sp2->pool.name);\n}\n\nstatic int pool_id_cmp(const DBStoragePoolInfo *sp1,\n        const DBStoragePoolInfo *sp2)\n{\n    return fc_compare_int64(sp1->pool.id, sp2->pool.id);\n}\n\nstatic int granted_pool_cmp(const DBGrantedPoolInfo *pg1,\n        const DBGrantedPoolInfo *pg2)\n{\n    return fc_compare_int64(pg1->granted.pool_id, pg2->granted.pool_id);\n}\n\nint user_alloc_init(void *element, void *args)\n{\n    DBUserInfo *user;\n\n    user = (DBUserInfo *)element;\n    if ((user->storage_pools.created=uniq_skiplist_new(&adb_ctx.pool.\n                    factories.created, SKIPLIST_INIT_LEVEL_COUNT)) == NULL)\n    {\n        return ENOMEM;\n    }\n    if ((user->storage_pools.granted=uniq_skiplist_new(&adb_ctx.pool.\n                    factories.granted, SKIPLIST_INIT_LEVEL_COUNT)) == NULL)\n    {\n        return ENOMEM;\n    }\n\n    return 0;\n}\n\nstatic inline int init_name_allocators(\n        struct fast_allocator_context *name_acontext)\n{\n#define NAME_REGION_COUNT 1\n    const int obj_size = 0;\n    struct fast_region_info regions[NAME_REGION_COUNT];\n\n    FAST_ALLOCATOR_INIT_REGION(regions[0], 0, 256, 8, 1024);\n    return fast_allocator_init_ex(name_acontext, \"name\", obj_size,\n            NULL, regions, NAME_REGION_COUNT, 0, 0.00, 0, false);\n}\n\nstatic int init_allocators()\n{\n    int result;\n    if ((result=fast_mblock_init_ex1(&adb_ctx.user.allocator,\n                \"user_allocator\", sizeof(DBUserInfo), 256, 0,\n                user_alloc_init, NULL, true)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fast_mblock_init_ex1(&adb_ctx.pool.allocators.created,\n                \"spool_created\", sizeof(DBStoragePoolInfo), 1024, 0,\n                NULL, NULL, true)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fast_mblock_init_ex1(&adb_ctx.pool.allocators.granted,\n                \"spool_granted\", sizeof(DBGrantedPoolInfo), 1024, 0,\n                NULL, NULL, true)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nstatic void destroy_allocators()\n{\n    fast_mblock_destroy(&adb_ctx.user.allocator);\n    fast_mblock_destroy(&adb_ctx.pool.allocators.created);\n    fast_mblock_destroy(&adb_ctx.pool.allocators.granted);\n}\n\nstatic void free_storage_pool_func(UniqSkiplist *sl,\n        void *ptr, const int delay_seconds)\n{\n    DBStoragePoolInfo *dbspool;\n\n    dbspool = (DBStoragePoolInfo *)ptr;\n    uniq_skiplist_delete(adb_ctx.pool.sl_pairs.by_id.skiplist, dbspool);\n    uniq_skiplist_delete(adb_ctx.pool.sl_pairs.by_name.skiplist, dbspool);\n\n    fast_allocator_free(&adb_ctx.name_acontext, dbspool->pool.name.str);\n    fast_mblock_free_object(&adb_ctx.pool.allocators.created, dbspool);\n}\n\nstatic void free_granted_pool_func(UniqSkiplist *sl,\n        void *ptr, const int delay_seconds)\n{\n    DBGrantedPoolInfo *dbgranted;\n\n    dbgranted = (DBGrantedPoolInfo *)ptr;\n    fast_mblock_free_object(&adb_ctx.pool.allocators.granted, dbgranted);\n}\n\nstatic int init_skiplists()\n{\n    const int max_level_count = 12;\n    const int alloc_skiplist_once = 1024;\n    const int min_alloc_elements_once = 8;\n    const int delay_free_seconds = 0;\n    int result;\n\n    if ((result=uniq_skiplist_init_pair(&adb_ctx.user.sl_pair,\n                    SKIPLIST_INIT_LEVEL_COUNT, max_level_count,\n                    (skiplist_compare_func)user_compare, NULL,\n                    min_alloc_elements_once, delay_free_seconds)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=uniq_skiplist_init_ex(&adb_ctx.pool.factories.created,\n                    max_level_count, (skiplist_compare_func)pool_name_cmp,\n                    free_storage_pool_func, alloc_skiplist_once,\n                    min_alloc_elements_once, delay_free_seconds, NULL)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=uniq_skiplist_init_ex(&adb_ctx.pool.factories.granted,\n                    max_level_count, (skiplist_compare_func)granted_pool_cmp,\n                    free_granted_pool_func, alloc_skiplist_once,\n                    min_alloc_elements_once, delay_free_seconds, NULL)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=uniq_skiplist_init_pair(&adb_ctx.pool.sl_pairs.by_id,\n                    SKIPLIST_INIT_LEVEL_COUNT, max_level_count,\n                    (skiplist_compare_func)pool_id_cmp, NULL,\n                    min_alloc_elements_once, delay_free_seconds)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=uniq_skiplist_init_pair(&adb_ctx.pool.sl_pairs.by_name,\n                    SKIPLIST_INIT_LEVEL_COUNT, max_level_count,\n                    (skiplist_compare_func)pool_name_cmp, NULL,\n                    min_alloc_elements_once, delay_free_seconds)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nstatic void destroy_skiplists()\n{\n    uniq_skiplist_destroy(&adb_ctx.user.sl_pair.factory);\n    uniq_skiplist_destroy(&adb_ctx.pool.factories.created);\n    uniq_skiplist_destroy(&adb_ctx.pool.factories.granted);\n    uniq_skiplist_destroy(&adb_ctx.pool.sl_pairs.by_id.factory);\n    uniq_skiplist_destroy(&adb_ctx.pool.sl_pairs.by_name.factory);\n}\n\nstatic int auth_db_init()\n{\n    int result;\n\n    if ((result=init_pthread_lock(&adb_ctx.lock)) != 0) {\n        return result;\n    }\n\n    if ((result=init_name_allocators(&adb_ctx.name_acontext)) != 0) {\n        return result;\n    }\n\n    if ((result=init_allocators()) != 0) {\n        return result;\n    }\n\n    if ((result=init_skiplists()) != 0) {\n        return result;\n    }\n\n    adb_ctx.inited = true;\n    return 0;\n}\n\nvoid auth_db_destroy()\n{\n    if (adb_ctx.inited) {\n        destroy_skiplists();\n        destroy_allocators();\n        fast_allocator_destroy(&adb_ctx.name_acontext);\n        pthread_mutex_destroy(&adb_ctx.lock);\n        adb_ctx.inited = false;\n    }\n}\n\nstatic inline DBUserInfo *user_get(AuthServerContext *server_ctx,\n        const string_t *username)\n{\n    DBUserInfo target;\n\n    target.user.name = *username;\n    return (DBUserInfo *)uniq_skiplist_find(\n            adb_ctx.user.sl_pair.skiplist, &target);\n}\n\nstatic inline int user_set_passwd(DBUserInfo *user, const string_t *passwd)\n{\n    int result;\n    char *old_passwd;\n\n    old_passwd = user->user.passwd.str;\n    result = fast_allocator_alloc_string(&adb_ctx.name_acontext,\n            &user->user.passwd, passwd);\n    if (old_passwd != NULL) {\n        fast_allocator_free(&adb_ctx.name_acontext, old_passwd);\n    }\n    return result;\n}\n\nstatic int user_create(AuthServerContext *server_ctx, DBUserInfo **dbuser,\n        const FCFSAuthUserInfo *user, const bool addto_backend)\n{\n    int result;\n    bool need_insert;\n\n    if (*dbuser == NULL) {\n        *dbuser = (DBUserInfo *)fast_mblock_alloc_object(\n                &adb_ctx.user.allocator);\n        if (*dbuser == NULL) {\n            return ENOMEM;\n        }\n\n        if ((result=fast_allocator_alloc_string(&adb_ctx.name_acontext,\n                        &(*dbuser)->user.name, &user->name)) != 0)\n        {\n            return result;\n        }\n        need_insert = true;\n    } else {\n        need_insert = false;\n    }\n\n    if ((result=user_set_passwd(*dbuser, &user->passwd)) != 0) {\n        return result;\n    }\n    (*dbuser)->user.priv = user->priv;\n    if (addto_backend) {\n        if ((result=dao_user_create(server_ctx->dao_ctx,\n                        &(*dbuser)->user)) != 0)\n        {\n            return result;\n        }\n    } else {\n        (*dbuser)->user.id = user->id;\n    }\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    (*dbuser)->user.status = user->status;\n    if (need_insert) {\n        if ((result=uniq_skiplist_insert(adb_ctx.user.sl_pair.\n                        skiplist, *dbuser)) == 0)\n        {\n            adb_ctx.user.count++;\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return 0;\n}\n\nint adb_user_create(AuthServerContext *server_ctx,\n        const FCFSAuthUserInfo *user)\n{\n    const bool addto_backend = true;\n    int result;\n    DBUserInfo *dbuser;\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    dbuser = user_get(server_ctx, &user->name);\n    if (dbuser != NULL && dbuser->user.status == FCFS_AUTH_USER_STATUS_NORMAL) {\n        result = EEXIST;\n    } else {\n        result = ENOENT;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    if (result == ENOENT) {\n        result = user_create(server_ctx, &dbuser, user, addto_backend);\n    }\n    return result;\n}\n\nconst DBUserInfo *adb_user_get(AuthServerContext *server_ctx,\n        const string_t *username)\n{\n    DBUserInfo *user;\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    user = user_get(server_ctx, username);\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    if (user != NULL && user->user.status == FCFS_AUTH_USER_STATUS_NORMAL) {\n        return user;\n    } else {\n        return NULL;\n    }\n}\n\nint adb_user_remove(AuthServerContext *server_ctx, const string_t *username)\n{\n    DBUserInfo *user;\n    int result;\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    user = user_get(server_ctx, username);\n    if (user != NULL && user->user.status == FCFS_AUTH_USER_STATUS_NORMAL) {\n        if ((result=dao_user_remove(server_ctx->dao_ctx,\n                        user->user.id)) == 0)\n        {\n            user->user.status = FCFS_AUTH_USER_STATUS_DELETED;\n            adb_ctx.user.count--;\n        }\n    } else {\n        result = ENOENT;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return result;\n}\n\nint adb_user_update_priv(AuthServerContext *server_ctx,\n        const string_t *username, const int64_t priv)\n{\n    DBUserInfo *user;\n    int result;\n    bool changed;\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    user = user_get(server_ctx, username);\n    if (user != NULL && user->user.status == FCFS_AUTH_USER_STATUS_NORMAL) {\n        if (user->user.priv == priv) {\n            result = 0;\n            changed = false;\n        } else if ((result=dao_user_update_priv(server_ctx->dao_ctx,\n                        user->user.id, priv)) == 0)\n        {\n            user->user.priv = priv;\n            changed = true;\n        } else {\n            changed = false;\n        }\n    } else {\n        result = ENOENT;\n        changed = false;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    if (changed) {\n        g_db_priv_change_callbacks.user_priv_changed(user->user.id, priv);\n    }\n    return result;\n}\n\nint adb_user_update_passwd(AuthServerContext *server_ctx,\n        const string_t *username, const string_t *passwd)\n{\n    DBUserInfo *user;\n    int result;\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    user = user_get(server_ctx, username);\n    if (user != NULL && user->user.status == FCFS_AUTH_USER_STATUS_NORMAL) {\n        if ((result=dao_user_update_passwd(server_ctx->dao_ctx,\n                        user->user.id, passwd)) == 0)\n        {\n            user_set_passwd(user, passwd);\n        }\n    } else {\n        result = ENOENT;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return result;\n}\n\nint adb_user_list(AuthServerContext *server_ctx,\n        const SFListLimitInfo *limit,\n        FCFSAuthUserArray *array)\n{\n    UniqSkiplistIterator it;\n    DBUserInfo *dbuser;\n    int result;\n\n    array->count = 0;\n    if ((result=fcfs_auth_user_check_realloc_array(array,\n                    limit->count)) != 0)\n    {\n        return result;\n    }\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    uniq_skiplist_iterator_at(adb_ctx.user.sl_pair.skiplist,\n            limit->offset, &it);\n    while ((array->count < limit->count) && (dbuser=(DBUserInfo *)\n                uniq_skiplist_next(&it)) != NULL)\n    {\n        if (dbuser->user.status == FCFS_AUTH_USER_STATUS_NORMAL) {\n            array->users[array->count++] = dbuser->user;\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return 0;\n}\n\nstatic inline DBStoragePoolInfo *get_spool_by_id(const int64_t pool_id)\n{\n    DBStoragePoolInfo target;\n\n    target.pool.id = pool_id;\n    return (DBStoragePoolInfo *)uniq_skiplist_find(\n            adb_ctx.pool.sl_pairs.by_id.skiplist, &target);\n}\n\nstatic inline DBStoragePoolInfo *get_spool_by_name(const string_t *name)\n{\n    DBStoragePoolInfo target;\n\n    target.pool.name = *name;\n    return (DBStoragePoolInfo *)uniq_skiplist_find(\n            adb_ctx.pool.sl_pairs.by_name.skiplist, &target);\n}\n\n\nstatic inline DBStoragePoolInfo *user_spool_get(AuthServerContext\n        *server_ctx, DBUserInfo *user, const string_t *poolname)\n{\n    DBStoragePoolInfo target;\n\n    target.pool.name = *poolname;\n    return (DBStoragePoolInfo *)uniq_skiplist_find(\n            user->storage_pools.created, &target);\n}\n\nstatic inline int spool_global_skiplists_insert(DBStoragePoolInfo *dbspool)\n{\n    int result;\n    if ((result=uniq_skiplist_insert(adb_ctx.pool.sl_pairs.\n                    by_id.skiplist, dbspool)) == 0)\n    {\n        result = uniq_skiplist_insert(adb_ctx.pool.\n                sl_pairs.by_name.skiplist, dbspool);\n    }\n\n    return result;\n}\n\nstatic int storage_pool_create(AuthServerContext *server_ctx,\n        DBUserInfo *dbuser, DBStoragePoolInfo **dbspool,\n        const FCFSAuthStoragePoolInfo *pool, const bool addto_backend)\n{\n    int result;\n    bool need_insert;\n\n    if (*dbspool == NULL) {\n        *dbspool = (DBStoragePoolInfo *)fast_mblock_alloc_object(\n                &adb_ctx.pool.allocators.created);\n        if (*dbspool == NULL) {\n            return ENOMEM;\n        }\n\n        if ((result=fast_allocator_alloc_string(&adb_ctx.name_acontext,\n                        &(*dbspool)->pool.name, &pool->name)) != 0)\n        {\n            return result;\n        }\n        (*dbspool)->user = dbuser;\n        need_insert = true;\n    } else {\n        result = 0;\n        need_insert = false;\n    }\n\n    (*dbspool)->pool.quota = pool->quota;\n    if (addto_backend) {\n        if ((result=dao_spool_create(server_ctx->dao_ctx, &dbuser->\n                        user.name, &(*dbspool)->pool)) != 0)\n        {\n            fast_allocator_free(&adb_ctx.name_acontext,\n                    (*dbspool)->pool.name.str);\n            fast_mblock_free_object(&adb_ctx.pool.\n                    allocators.created, *dbspool);\n            return result;\n        }\n    } else {\n        (*dbspool)->pool.id = pool->id;\n    }\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    (*dbspool)->pool.status = pool->status;\n    if (need_insert) {\n        if ((result=spool_global_skiplists_insert(*dbspool)) == 0) {\n            result = uniq_skiplist_insert(dbuser->storage_pools.\n                    created, *dbspool);\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    if (result != 0) {\n        dao_spool_remove(server_ctx->dao_ctx, (*dbspool)->pool.id);  //rollback\n\n        fast_allocator_free(&adb_ctx.name_acontext,\n                (*dbspool)->pool.name.str);\n        fast_mblock_free_object(&adb_ctx.pool.\n                allocators.created, *dbspool);\n    }\n\n    return result;\n}\n\nint adb_spool_create(AuthServerContext *server_ctx, const string_t\n        *username, const FCFSAuthStoragePoolInfo *pool)\n{\n    const bool addto_backend = true;\n    int result;\n    DBUserInfo *user;\n    DBStoragePoolInfo *dbspool;\n\n    result = ENOENT;\n    dbspool = NULL;\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    user = user_get(server_ctx, username);\n    if (user != NULL) {\n        if (user->user.status == FCFS_AUTH_USER_STATUS_NORMAL) {\n            dbspool = user_spool_get(server_ctx, user, &pool->name);\n            if (dbspool != NULL) {\n                if (dbspool->pool.status == FCFS_AUTH_POOL_STATUS_NORMAL) {\n                    result = EEXIST;\n                }\n            } else {\n                if (get_spool_by_name(&pool->name) != NULL) {\n                    result = EEXIST; //occupied by other user\n                }\n            }\n        } else {\n            user = NULL;\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    if (user != NULL && result == ENOENT) {\n        return storage_pool_create(server_ctx, user,\n                &dbspool, pool, addto_backend);\n    } else {\n        return result;\n    }\n}\n\nconst FCFSAuthStoragePoolInfo *adb_spool_get(AuthServerContext *server_ctx,\n        const string_t *username, const string_t *poolname)\n{\n    DBUserInfo *user;\n    DBStoragePoolInfo *spool;\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    user = user_get(server_ctx, username);\n    if (user != NULL) {\n        spool = user_spool_get(server_ctx, user, poolname);\n    } else {\n        spool = NULL;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    if (spool != NULL && spool->pool.status == FCFS_AUTH_POOL_STATUS_NORMAL) {\n        return &spool->pool;\n    } else {\n        return NULL;\n    }\n}\n\nint adb_spool_remove(AuthServerContext *server_ctx,\n        const string_t *username, const string_t *poolname)\n{\n    DBUserInfo *user;\n    DBStoragePoolInfo *spool;\n    int result;\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    user = user_get(server_ctx, username);\n    if (user != NULL) {\n        spool = user_spool_get(server_ctx, user, poolname);\n    } else {\n        spool = NULL;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    if (!(spool != NULL && spool->pool.status ==\n            FCFS_AUTH_POOL_STATUS_NORMAL))\n    {\n        return ENOENT;\n    }\n\n    if ((result=dao_spool_remove(server_ctx->dao_ctx,\n                    spool->pool.id)) != 0)\n    {\n        return result;\n    }\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    spool->pool.status = FCFS_AUTH_POOL_STATUS_DELETED;\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return 0;\n}\n\nstatic void call_pool_quota_avail_func(const DBStoragePoolInfo *dbpool,\n        const bool old_avail)\n{\n    bool new_avail;\n\n    new_avail = FCFS_AUTH_POOL_AVAILABLE(dbpool->pool);\n    if (new_avail != old_avail) {\n        g_db_priv_change_callbacks.pool_quota_avail_changed(\n                dbpool->pool.id, new_avail);\n    }\n}\n\nint adb_spool_set_quota(AuthServerContext *server_ctx,\n        const string_t *username, const string_t *poolname,\n        const int64_t quota)\n{\n    DBUserInfo *user;\n    DBStoragePoolInfo *spool;\n    int64_t old_quota;\n    int result;\n    bool changed;\n    bool old_avail;\n\n    old_quota = 0;\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    user = user_get(server_ctx, username);\n    if (user != NULL) {\n        if ((spool=user_spool_get(server_ctx, user, poolname)) != NULL) {\n            old_quota = spool->pool.quota;\n        }\n    } else {\n        spool = NULL;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    if (!(spool != NULL && spool->pool.status ==\n                FCFS_AUTH_POOL_STATUS_NORMAL))\n    {\n        return ENOENT;\n    }\n\n    if (old_quota != quota) {\n        if ((result=dao_spool_set_quota(server_ctx->dao_ctx,\n                        spool->pool.id, quota)) != 0)\n        {\n            return result;\n        }\n        old_avail = FCFS_AUTH_POOL_AVAILABLE(spool->pool);\n        changed = true;\n    } else {\n        old_avail = false;\n        changed = false;\n    }\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    if (changed) {\n        spool->pool.quota = quota;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    if (changed) {\n        call_pool_quota_avail_func(spool, old_avail);\n    }\n\n    return 0;\n}\n\nint adb_spool_get_quota(AuthServerContext *server_ctx,\n        const string_t *poolname, int64_t *quota)\n{\n    DBStoragePoolInfo *spool;\n    int result;\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    if ((spool=get_spool_by_name(poolname)) != NULL) {\n        if (spool->pool.status == FCFS_AUTH_POOL_STATUS_NORMAL) {\n            *quota = spool->pool.quota;\n            result = 0;\n        } else {\n            *quota = 0;\n            result = ENOENT;\n        }\n    } else {\n        *quota = 0;\n        result = ENOENT;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return result;\n}\n\nint adb_spool_set_used_bytes(const string_t *poolname,\n        const int64_t used_bytes)\n{\n    DBStoragePoolInfo *dbpool;\n    bool old_avail;\n\n    if ((dbpool=adb_spool_global_get(poolname)) == NULL) {\n        return ENOENT;\n    }\n\n    if (dbpool->pool.used == used_bytes) {\n        return 0;\n    }\n\n    old_avail = FCFS_AUTH_POOL_AVAILABLE(dbpool->pool);\n    dbpool->pool.used = used_bytes;\n    call_pool_quota_avail_func(dbpool, old_avail);\n    return 0;\n}\n\nint adb_spool_list(AuthServerContext *server_ctx, const string_t *username,\n        const SFListLimitInfo *limit, FCFSAuthStoragePoolArray *array)\n{\n    UniqSkiplistIterator it;\n    DBUserInfo *user;\n    DBStoragePoolInfo *spool;\n    int result;\n\n    array->count = 0;\n    if ((result=fcfs_auth_spool_check_realloc_array(array,\n                    limit->count)) != 0)\n    {\n        return result;\n    }\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    user = user_get(server_ctx, username);\n    if (user != NULL) {\n        uniq_skiplist_iterator_at(user->storage_pools.created,\n                limit->offset, &it);\n        while ((array->count < limit->count) && (spool=(DBStoragePoolInfo *)\n                    uniq_skiplist_next(&it)) != NULL)\n        {\n            if (spool->pool.status == FCFS_AUTH_POOL_STATUS_NORMAL) {\n                array->spools[array->count++] = spool->pool;\n            }\n        }\n    } else {\n        result = ENOENT;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return result;\n}\n\nstatic int convert_spool_array(AuthServerContext *server_ctx,\n        DBUserInfo *dbuser, const FCFSAuthStoragePoolArray *spool_array)\n{\n    const bool addto_backend = false;\n    int result;\n    DBStoragePoolInfo *dbspool;\n    const FCFSAuthStoragePoolInfo *spool;\n    const FCFSAuthStoragePoolInfo *end;\n\n    end = spool_array->spools + spool_array->count;\n    for (spool=spool_array->spools; spool<end; spool++) {\n        dbspool = NULL;\n\n        if ((result=storage_pool_create(server_ctx, dbuser,\n                        &dbspool, spool, addto_backend)) != 0)\n        {\n            return result;\n        }\n    }\n\n    return 0;\n}\n\nstatic int load_user_spool(AuthServerContext *server_ctx,\n        struct fast_mpool_man *mpool, DBUserInfo *dbuser)\n{\n    FCFSAuthStoragePoolArray spool_array;\n    int result;\n\n    fcfs_auth_spool_init_array(&spool_array);\n    if ((result=dao_spool_list(server_ctx->dao_ctx, &dbuser->\n                    user.name, mpool, &spool_array)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=convert_spool_array(server_ctx,\n                    dbuser, &spool_array)) != 0)\n    {\n        return result;\n    }\n\n    fcfs_auth_spool_free_array(&spool_array);\n    return 0;\n}\n\nstatic int granted_pool_create(AuthServerContext *server_ctx,\n        DBUserInfo *dbuser, DBGrantedPoolInfo **dbgranted,\n        const FCFSAuthGrantedPoolInfo *granted, const bool addto_backend)\n{\n    int result;\n    bool need_insert;\n\n    if (*dbgranted == NULL) {\n        *dbgranted = (DBGrantedPoolInfo *)fast_mblock_alloc_object(\n                &adb_ctx.pool.allocators.granted);\n        if (*dbgranted == NULL) {\n            return ENOMEM;\n        }\n        need_insert = true;\n    } else {\n        need_insert = false;\n    }\n\n    (*dbgranted)->granted.pool_id = granted->pool_id;\n    (*dbgranted)->granted.privs = granted->privs;\n    if (addto_backend) {\n        if ((result=dao_granted_create(server_ctx->dao_ctx, &dbuser->\n                        user.name, &(*dbgranted)->granted)) != 0)\n        {\n            fast_mblock_free_object(&adb_ctx.pool.\n                    allocators.granted, *dbgranted);\n            return result;\n        }\n    } else {\n        (*dbgranted)->granted.id = granted->id;\n        result = 0;\n    }\n\n    if (need_insert) {\n        PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n        if (((*dbgranted)->sp=get_spool_by_id(granted->pool_id)) != NULL) {\n            result = uniq_skiplist_insert(dbuser->storage_pools.\n                    granted, *dbgranted);\n        } else {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"storage pool not exist, pool id: %\"PRId64,\n                    __LINE__, granted->pool_id);\n            result = ENOENT;\n        }\n        PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n        if (result != 0) {\n            fast_mblock_free_object(&adb_ctx.pool.\n                    allocators.granted, *dbgranted);\n        }\n    }\n\n    return result;\n}\n\nstatic int convert_granted_array(AuthServerContext *server_ctx,\n        DBUserInfo *dbuser, const FCFSAuthGrantedPoolArray *granted_array)\n{\n    const bool addto_backend = false;\n    int result;\n    DBGrantedPoolInfo *dbgranted;\n    const FCFSAuthGrantedPoolFullInfo *gf;\n    const FCFSAuthGrantedPoolFullInfo *end;\n\n    end = granted_array->gpools + granted_array->count;\n    for (gf=granted_array->gpools; gf<end; gf++) {\n        dbgranted = NULL;\n        if ((result=granted_pool_create(server_ctx, dbuser, &dbgranted,\n                        &gf->granted, addto_backend)) != 0)\n        {\n            return result;\n        }\n    }\n\n    return 0;\n}\n\nstatic int load_granted_pool(AuthServerContext *server_ctx, DBUserInfo *dbuser)\n{\n    FCFSAuthGrantedPoolArray granted_array;\n    int result;\n\n    fcfs_auth_granted_init_array(&granted_array);\n    if ((result=dao_granted_list(server_ctx->dao_ctx, &dbuser->\n                    user.name, &granted_array)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=convert_granted_array(server_ctx,\n                    dbuser, &granted_array)) != 0)\n    {\n        return result;\n    }\n\n    fcfs_auth_granted_free_array(&granted_array);\n    return 0;\n}\n\nstatic int convert_user_array(AuthServerContext *server_ctx,\n        struct fast_mpool_man *mpool,\n        const FCFSAuthUserArray *user_array)\n{\n    const bool addto_backend = false;\n    int result;\n    DBUserInfo *dbuser;\n    const FCFSAuthUserInfo *user;\n    const FCFSAuthUserInfo *end;\n    UniqSkiplistIterator it;\n\n    end = user_array->users + user_array->count;\n    for (user=user_array->users; user<end; user++) {\n        dbuser = NULL;\n        if ((result=user_create(server_ctx, &dbuser,\n                        user, addto_backend)) != 0)\n        {\n            return result;\n        }\n\n        if ((result=load_user_spool(server_ctx, mpool, dbuser)) != 0) {\n            return result;\n        }\n    }\n\n    uniq_skiplist_iterator(adb_ctx.user.sl_pair.skiplist, &it);\n    while ((dbuser=(DBUserInfo *)uniq_skiplist_next(&it)) != NULL) {\n        if ((result=load_granted_pool(server_ctx, dbuser)) != 0) {\n            return result;\n        }\n    }\n\n    return 0;\n}\n\nstatic int load_pool_auto_id(AuthServerContext *server_ctx)\n{\n    int result;\n    int64_t auto_id;\n\n    if ((result=dao_spool_set_base_path_inode(server_ctx->dao_ctx)) != 0) {\n        return result;\n    }\n\n    if ((result=dao_spool_get_auto_id(server_ctx->dao_ctx, &auto_id)) != 0) {\n        return result;\n    }\n\n    if (AUTO_ID_INITIAL > auto_id) {\n        auto_id = AUTO_ID_INITIAL;\n    }\n    FC_ATOMIC_SET(adb_ctx.pool.auto_id, auto_id);\n    return 0;\n}\n\nint adb_load_data(AuthServerContext *server_ctx)\n{\n    const int alloc_size_once = 64 * 1024;\n    const int discard_size = 8;\n    int result;\n    struct fast_mpool_man mpool;\n    FCFSAuthUserArray user_array;\n\n    if ((result=auth_db_init()) != 0) {\n        return result;\n    }\n\n    if ((result=fast_mpool_init(&mpool, alloc_size_once,\n                    discard_size)) != 0)\n    {\n        return result;\n    }\n\n    fcfs_auth_user_init_array(&user_array);\n    if ((result=dao_user_list(server_ctx->dao_ctx,\n                    &mpool, &user_array)) != 0)\n    {\n        return result;\n    }\n\n    result = convert_user_array(server_ctx, &mpool, &user_array);\n    fcfs_auth_user_free_array(&user_array);\n    fast_mpool_destroy(&mpool);\n\n    if (result != 0) {\n        return result;\n    }\n    return load_pool_auto_id(server_ctx);\n}\n\nint adb_check_generate_admin_user(AuthServerContext *server_ctx)\n{\n    int result;\n    bool need_create;\n    unsigned char passwd[FCFS_AUTH_PASSWD_LEN];\n    FCFSAuthUserInfo user;\n\n    if (ADMIN_GENERATE_MODE == AUTH_ADMIN_GENERATE_MODE_FIRST) {\n        need_create = (adb_ctx.user.count == 0);\n    } else {\n        need_create = adb_user_get(server_ctx,\n                &ADMIN_GENERATE_USERNAME) == NULL;\n    }\n\n    if (need_create) {\n        user.name = ADMIN_GENERATE_USERNAME;\n        fcfs_auth_generate_passwd(passwd);\n        FC_SET_STRING_EX(user.passwd, (char *)passwd, FCFS_AUTH_PASSWD_LEN);\n        user.priv = FCFS_AUTH_USER_PRIV_ALL;\n        if ((result=adb_user_create(server_ctx, &user)) == 0) {\n            if ((result=fcfs_auth_save_passwd(ADMIN_GENERATE_KEY_FILENAME.\n                            str, passwd)) == 0)\n            {\n                logInfo(\"file: \"__FILE__\", line: %d, \"\n                        \"scret key for user \\\"%s\\\" store to file: %s\",\n                        __LINE__, ADMIN_GENERATE_USERNAME.str,\n                        ADMIN_GENERATE_KEY_FILENAME.str);\n            }\n        }\n        return result;\n    } else {\n        return 0;\n    }\n}\n\nstatic inline DBGrantedPoolInfo *granted_pool_get(\n        const DBUserInfo *dbuser, const int64_t pool_id)\n{\n    DBGrantedPoolInfo target;\n\n    target.granted.pool_id = pool_id;\n    return (DBGrantedPoolInfo *)uniq_skiplist_find(\n            dbuser->storage_pools.granted, &target);\n}\n\nint adb_granted_create(AuthServerContext *server_ctx, const string_t *username,\n        FCFSAuthGrantedPoolInfo *granted)\n{\n    const bool addto_backend = true;\n    bool changed;\n    int result;\n    DBUserInfo *dbuser;\n    DBGrantedPoolInfo *dbgranted;\n\n    dbgranted = NULL;\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    dbuser = user_get(server_ctx, username);\n    if (dbuser != NULL) {\n        if (dbuser->user.status == FCFS_AUTH_USER_STATUS_NORMAL) {\n            dbgranted = granted_pool_get(dbuser, granted->pool_id);\n        } else {\n            dbuser = NULL;\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    /*\n    logInfo(\"username: %.*s, ptr: %p, dbgranted: %p\", username->len,\n            username->str, user, dbgranted);\n            */\n\n    if (dbuser == NULL) {\n        return ENOENT;\n    }\n\n    if (dbgranted != NULL) {\n        changed = !(dbgranted->granted.privs.fdir == granted->privs.fdir &&\n                dbgranted->granted.privs.fstore == granted->privs.fstore);\n        if (!changed) {\n            return 0;\n        }\n    } else {\n        changed = false;\n    }\n\n    if ((result=granted_pool_create(server_ctx, dbuser,\n                    &dbgranted, granted, addto_backend)) != 0)\n    {\n        return result;\n    }\n\n    if (changed) {\n        g_db_priv_change_callbacks.pool_priv_changed(dbuser->user.id,\n                dbgranted->granted.pool_id, &dbgranted->granted.privs);\n    }\n    return 0;\n}\n\nint adb_granted_remove(AuthServerContext *server_ctx,\n        const string_t *username, const int64_t pool_id)\n{\n    int result;\n    DBUserInfo *dbuser;\n    DBGrantedPoolInfo *dbgranted;\n\n    dbgranted = NULL;\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    dbuser = user_get(server_ctx, username);\n    if (dbuser != NULL) {\n        if (dbuser->user.status == FCFS_AUTH_USER_STATUS_NORMAL) {\n            dbgranted = granted_pool_get(dbuser, pool_id);\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    if (dbgranted != NULL) {\n        if ((result=dao_granted_remove(server_ctx->dao_ctx,\n                        username, pool_id)) == 0)\n        {\n            FCFSAuthSPoolPriviledges privs;\n            privs.fdir = privs.fstore = FCFS_AUTH_POOL_ACCESS_NONE;\n            g_db_priv_change_callbacks.pool_priv_changed(dbuser->user.id,\n                    dbgranted->granted.pool_id, &privs);\n\n            PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n            uniq_skiplist_delete(dbuser->storage_pools.granted, dbgranted);\n            PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n        }\n        return result;\n    } else {\n        return ENOENT;\n    }\n}\n\nstatic inline void set_gpool_full_info(FCFSAuthGrantedPoolFullInfo *gf,\n        const DBGrantedPoolInfo *dbgpool)\n{\n    gf->granted = dbgpool->granted;\n    gf->username = dbgpool->sp->user->user.name;\n    gf->pool_name = dbgpool->sp->pool.name;\n}\n\nint adb_granted_full_get(AuthServerContext *server_ctx, const string_t\n        *username, const int64_t pool_id, FCFSAuthGrantedPoolFullInfo *gf)\n{\n    DBUserInfo *dbuser;\n    DBGrantedPoolInfo *dbgranted;\n\n    dbgranted = NULL;\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    dbuser = user_get(server_ctx, username);\n    if (dbuser != NULL) {\n        if (dbuser->user.status == FCFS_AUTH_USER_STATUS_NORMAL) {\n            if ((dbgranted=granted_pool_get(dbuser, pool_id)) != NULL) {\n                set_gpool_full_info(gf, dbgranted);\n            }\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return (dbgranted != NULL) ? 0 : ENOENT;\n}\n\nint adb_granted_privs_get(AuthServerContext *server_ctx,\n        const DBUserInfo *dbuser, const DBStoragePoolInfo *dbpool,\n        FCFSAuthSPoolPriviledges *privs)\n{\n    DBGrantedPoolInfo *dbgranted;\n    int result;\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    if (dbpool->user == dbuser) {\n        privs->fdir = privs->fstore = FCFS_AUTH_POOL_ACCESS_ALL;\n        result = 0;\n    } else if ((dbgranted=granted_pool_get(dbuser,\n                    dbpool->pool.id)) != NULL)\n    {\n        *privs = dbgranted->granted.privs;\n        result = 0;\n    } else {\n        result = ENOENT;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return result;\n}\n\nint adb_granted_list(AuthServerContext *server_ctx, const string_t *username,\n        const SFListLimitInfo *limit, FCFSAuthGrantedPoolArray *array)\n{\n    UniqSkiplistIterator it;\n    DBUserInfo *user;\n    DBGrantedPoolInfo *dbgpool;\n    int result;\n\n    array->count = 0;\n    if ((result=fcfs_auth_gpool_check_realloc_array(\n                    array, limit->count)) != 0)\n    {\n        return result;\n    }\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    user = user_get(server_ctx, username);\n    if (user != NULL) {\n        uniq_skiplist_iterator_at(user->storage_pools.granted,\n                limit->offset, &it);\n        while ((array->count < limit->count) && (dbgpool=(DBGrantedPoolInfo *)\n                    uniq_skiplist_next(&it)) != NULL)\n        {\n            set_gpool_full_info(array->gpools + array->count, dbgpool);\n            array->count++;\n        }\n    } else {\n        result = ENOENT;\n    }\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return result;\n}\n\nint64_t adb_spool_get_auto_id(AuthServerContext *server_ctx)\n{\n    return __sync_add_and_fetch(&adb_ctx.pool.auto_id, 0);\n}\n\nint adb_spool_inc_auto_id(AuthServerContext *server_ctx)\n{\n    int64_t auto_id;\n    auto_id = __sync_add_and_fetch(&adb_ctx.pool.auto_id, 1);\n    return dao_spool_set_auto_id(server_ctx->dao_ctx, auto_id);\n}\n\nint adb_spool_next_auto_id(AuthServerContext *server_ctx, int64_t *auto_id)\n{\n    *auto_id = __sync_add_and_fetch(&adb_ctx.pool.auto_id, 1);\n    return dao_spool_set_auto_id(server_ctx->dao_ctx, *auto_id);\n}\n\nDBStoragePoolInfo *adb_spool_global_get(const string_t *poolname)\n{\n    DBStoragePoolInfo *dbpool;\n\n    PTHREAD_MUTEX_LOCK(&adb_ctx.lock);\n    dbpool = get_spool_by_name(poolname);\n    PTHREAD_MUTEX_UNLOCK(&adb_ctx.lock);\n\n    return dbpool;\n}\n"
  },
  {
    "path": "src/auth/server/db/auth_db.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_DB_H\n#define _FCFS_AUTH_DB_H\n\n#include \"../server_types.h\"\n\ntypedef struct db_user_info {\n    FCFSAuthUserInfo user;\n    struct {\n        struct uniq_skiplist *created;  //element: DBStoragePoolInfo\n        struct uniq_skiplist *granted;  //element: DBGrantedPoolInfo\n    } storage_pools;\n} DBUserInfo;\n\ntypedef struct db_storage_pool_info {\n    FCFSAuthStoragePoolInfo pool;\n    DBUserInfo *user;\n} DBStoragePoolInfo;\n\ntypedef struct db_granted_pool_info {\n    FCFSAuthGrantedPoolInfo granted;\n    DBStoragePoolInfo *sp;\n} DBGrantedPoolInfo;\n\ntypedef void (*adb_user_priv_change_callback)(\n        const int64_t user_id, const int64_t new_priv);\ntypedef void (*adb_pool_priv_change_callback)(const int64_t user_id,\n        const int64_t pool_id, const FCFSAuthSPoolPriviledges *privs);\ntypedef void (*adb_pool_quota_avail_change_callback)(\n        const int64_t pool_id, const bool available);\n\ntypedef struct db_priv_change_callbacks {\n    adb_user_priv_change_callback user_priv_changed;\n    adb_pool_priv_change_callback pool_priv_changed;\n    adb_pool_quota_avail_change_callback pool_quota_avail_changed;\n} DBPrivChangeCallbacks;\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern DBPrivChangeCallbacks g_db_priv_change_callbacks;\n\nint adb_load_data(AuthServerContext *server_ctx);\n\nvoid auth_db_destroy();\n\nint adb_check_generate_admin_user(AuthServerContext *server_ctx);\n\nint adb_user_create(AuthServerContext *server_ctx,\n        const FCFSAuthUserInfo *user);\n\nconst DBUserInfo *adb_user_get(AuthServerContext *server_ctx,\n        const string_t *username);\n\nint adb_user_remove(AuthServerContext *server_ctx, const string_t *username);\n\nint adb_user_list(AuthServerContext *server_ctx,\n        const SFListLimitInfo *limit,\n        FCFSAuthUserArray *array);\n\nint adb_user_update_priv(AuthServerContext *server_ctx,\n        const string_t *username, const int64_t priv);\n\nint adb_user_update_passwd(AuthServerContext *server_ctx,\n        const string_t *username, const string_t *passwd);\n\n/* storage pool */\nint64_t adb_spool_get_auto_id(AuthServerContext *server_ctx);\nint adb_spool_inc_auto_id(AuthServerContext *server_ctx);\nint adb_spool_next_auto_id(AuthServerContext *server_ctx, int64_t *next_id);\n\nDBStoragePoolInfo *adb_spool_global_get(const string_t *poolname);\n\nstatic inline int adb_spool_access(AuthServerContext\n        *server_ctx, const string_t *poolname)\n{\n    return adb_spool_global_get(poolname) != NULL ? 0 : ENOENT;\n}\n\nint adb_spool_create(AuthServerContext *server_ctx, const string_t\n        *username, const FCFSAuthStoragePoolInfo *pool);\n\nconst FCFSAuthStoragePoolInfo *adb_spool_get(AuthServerContext *server_ctx,\n        const string_t *username, const string_t *poolname);\n\nint adb_spool_remove(AuthServerContext *server_ctx,\n        const string_t *username, const string_t *poolname);\n\nint adb_spool_set_quota(AuthServerContext *server_ctx,\n        const string_t *username, const string_t *poolname,\n        const int64_t quota);\n\nint adb_spool_get_quota(AuthServerContext *server_ctx,\n        const string_t *poolname, int64_t *quota);\n\nint adb_spool_list(AuthServerContext *server_ctx, const string_t *username,\n        const SFListLimitInfo *limit, FCFSAuthStoragePoolArray *array);\n\nint adb_spool_set_used_bytes(const string_t *poolname,\n        const int64_t used_bytes);\n\n/* granted pool */\nint adb_granted_create(AuthServerContext *server_ctx, const string_t *username,\n        FCFSAuthGrantedPoolInfo *granted);\n\nint adb_granted_remove(AuthServerContext *server_ctx,\n        const string_t *username, const int64_t pool_id);\n\nint adb_granted_full_get(AuthServerContext *server_ctx, const string_t\n        *username, const int64_t pool_id, FCFSAuthGrantedPoolFullInfo *gf);\n\nint adb_granted_privs_get(AuthServerContext *server_ctx,\n        const DBUserInfo *dbuser, const DBStoragePoolInfo *dbpool,\n        FCFSAuthSPoolPriviledges *privs);\n\nint adb_granted_list(AuthServerContext *server_ctx, const string_t *username,\n        const SFListLimitInfo *limit, FCFSAuthGrantedPoolArray *array);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/db/dao/dao.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"dao.h\"\n"
  },
  {
    "path": "src/auth/server/db/dao/dao.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _AUTH_DAO_H\n#define _AUTH_DAO_H\n\n#include \"../../server_global.h\"\n#include \"types.h\"\n#include \"user.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstatic inline int dao_get_context_size()\n{\n    return sizeof(FDIRClientContext);\n}\n\nstatic inline int dao_init_context(const int thread_index,\n        void *ctx, char *session_id)\n{\n    int result;\n    FDIRClientContext *client_ctx;\n\n    client_ctx = (FDIRClientContext *)ctx;\n    if ((result=fdir_client_simple_init_ex(client_ctx,\n                    &g_fcfs_auth_client_vars.client_ctx,\n                    g_server_global_vars.fdir_client_cfg_filename,\n                    NULL)) != 0)\n    {\n        return result;\n    }\n\n    if (thread_index == 0) {\n        fdir_client_auth_session_set_ex(client_ctx, session_id);\n    }\n    return 0;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/db/dao/func.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"func.h\"\n\nFCFSAuthDAOVariables g_auth_dao_vars = {\n    {AUTH_NAMESPACE_STR, AUTH_NAMESPACE_LEN},\n    {\n        {AUTH_XTTR_NAME_PASSWD_STR, AUTH_XTTR_NAME_PASSWD_LEN},\n        {AUTH_XTTR_NAME_PRIV_STR, AUTH_XTTR_NAME_PRIV_LEN},\n        {AUTH_XTTR_NAME_STATUS_STR, AUTH_XTTR_NAME_STATUS_LEN},\n        {AUTH_XTTR_NAME_QUOTA_STR, AUTH_XTTR_NAME_QUOTA_LEN},\n        {AUTH_XTTR_NAME_FDIR_STR, AUTH_XTTR_NAME_FDIR_LEN},\n        {AUTH_XTTR_NAME_FSTORE_STR, AUTH_XTTR_NAME_FSTORE_LEN},\n        {AUTH_XTTR_NAME_POOL_AUTO_ID_STR, AUTH_XTTR_NAME_POOL_AUTO_ID_LEN}\n    }\n};\n"
  },
  {
    "path": "src/auth/server/db/dao/func.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _AUTH_DAO_FUNC_H\n#define _AUTH_DAO_FUNC_H\n\n#include \"fastcommon/fast_mpool.h\"\n#include \"types.h\"\n\n#define AUTH_DIR_NAME_CREATED_STR  \"created\"\n#define AUTH_DIR_NAME_CREATED_LEN  (sizeof(AUTH_DIR_NAME_CREATED_STR) - 1)\n\n#define AUTH_DIR_NAME_GRANTED_STR  \"granted\"\n#define AUTH_DIR_NAME_GRANTED_LEN  (sizeof(AUTH_DIR_NAME_GRANTED_STR) - 1)\n\n#define AUTH_XTTR_NAME_PASSWD_STR  \"passwd\"\n#define AUTH_XTTR_NAME_PASSWD_LEN  (sizeof(AUTH_XTTR_NAME_PASSWD_STR) - 1)\n\n#define AUTH_XTTR_NAME_PRIV_STR    \"priv\"\n#define AUTH_XTTR_NAME_PRIV_LEN    (sizeof(AUTH_XTTR_NAME_PRIV_STR) - 1)\n\n#define AUTH_XTTR_NAME_STATUS_STR  \"status\"\n#define AUTH_XTTR_NAME_STATUS_LEN  (sizeof(AUTH_XTTR_NAME_STATUS_STR) - 1)\n\n#define AUTH_XTTR_NAME_QUOTA_STR   \"quota\"\n#define AUTH_XTTR_NAME_QUOTA_LEN   (sizeof(AUTH_XTTR_NAME_QUOTA_STR) - 1)\n\n#define AUTH_XTTR_NAME_FDIR_STR    \"fdir\"\n#define AUTH_XTTR_NAME_FDIR_LEN    (sizeof(AUTH_XTTR_NAME_FDIR_STR) - 1)\n\n#define AUTH_XTTR_NAME_FSTORE_STR  \"fstore\"\n#define AUTH_XTTR_NAME_FSTORE_LEN  (sizeof(AUTH_XTTR_NAME_FSTORE_STR) - 1)\n\n#define AUTH_XTTR_NAME_POOL_AUTO_ID_STR  \"auto_pid\"\n#define AUTH_XTTR_NAME_POOL_AUTO_ID_LEN  \\\n    (sizeof(AUTH_XTTR_NAME_POOL_AUTO_ID_STR) - 1)\n\ntypedef struct {\n    string_t ns;\n\n    struct {\n        string_t passwd;\n        string_t priv;\n        string_t status;\n        string_t quota;\n        string_t fdir;\n        string_t fstore;\n        string_t pool_auto_id;\n    } xttr_names;\n\n    int64_t base_path_inode;\n} FCFSAuthDAOVariables;\n\n#define DAO_NAMESPACE g_auth_dao_vars.ns\n#define DAO_BASE_PATH_INODE  g_auth_dao_vars.base_path_inode\n\n#define DAO_MODE_DIR  (0700 | S_IFDIR)\n#define DAO_MODE_FILE (0700 | S_IFREG)\n\n#define AUTH_XTTR_NAME_PASSWD g_auth_dao_vars.xttr_names.passwd\n#define AUTH_XTTR_NAME_PRIV   g_auth_dao_vars.xttr_names.priv\n#define AUTH_XTTR_NAME_STATUS g_auth_dao_vars.xttr_names.status\n#define AUTH_XTTR_NAME_QUOTA  g_auth_dao_vars.xttr_names.quota\n#define AUTH_XTTR_NAME_FDIR   g_auth_dao_vars.xttr_names.fdir\n#define AUTH_XTTR_NAME_FSTORE g_auth_dao_vars.xttr_names.fstore\n#define AUTH_XTTR_NAME_POOL_AUTO_ID g_auth_dao_vars.xttr_names.pool_auto_id\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nextern FCFSAuthDAOVariables g_auth_dao_vars;\n\nstatic inline int dao_set_xattr_string(FDIRClientContext *client_ctx,\n        const int64_t inode, const string_t *name, const string_t *value)\n{\n    FDIRClientOperInodePair oino;\n    key_value_pair_t xattr;\n\n    AUTH_SET_OPER_INODE_PAIR(oino, inode);\n    xattr.key = *name;\n    xattr.value = *value;\n    return fdir_client_set_xattr_by_inode(client_ctx,\n            &DAO_NAMESPACE, &oino, &xattr, 0);\n}\n\nstatic inline int dao_set_xattr_integer(FDIRClientContext *client_ctx,\n        const int64_t inode, const string_t *name, const int64_t nv)\n{\n    char buff[32];\n    string_t value;\n\n    value.str = buff;\n    value.len = fc_itoa(nv, buff);\n    return dao_set_xattr_string(client_ctx, inode, name, &value);\n}\n\nstatic inline int dao_get_xattr_string(FDIRClientContext *client_ctx,\n        const int64_t inode, const string_t *name, string_t *value,\n        const int size)\n{\n    const int flags = 0;\n    int result;\n    FDIRClientOperInodePair oino;\n\n    AUTH_SET_OPER_INODE_PAIR(oino, inode);\n    result = fdir_client_get_xattr_by_inode_ex(client_ctx,\n            &DAO_NAMESPACE, &oino, name, LOG_WARNING,\n            value, size, flags);\n    if (result == ENODATA) {\n        value->len = 0;\n        return 0;\n    } else {\n        return result;\n    }\n}\n\nstatic inline int dao_get_xattr_int64(FDIRClientContext *client_ctx,\n        const int64_t inode, const string_t *name, int64_t *nv)\n{\n    char buff[32];\n    string_t value;\n    char *endptr;\n    int result;\n\n    value.str = buff;\n    if ((result=dao_get_xattr_string(client_ctx, inode,\n                    name, &value, sizeof(buff) - 1)) != 0)\n    {\n        return result;\n    }\n\n    if (value.len == 0) {\n        *nv = 0;\n        return 0;\n    }\n\n    *(value.str + value.len) = '\\0';\n    *nv = strtoll(value.str, &endptr, 10);\n    if (endptr != NULL && *endptr != '\\0') {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"field: %.*s, invalid digital string: %s\",\n                __LINE__, name->len, name->str, value.str);\n        return EINVAL;\n    }\n\n    return 0;\n}\n\nstatic inline int dao_get_xattr_int32(FDIRClientContext *client_ctx,\n        const int64_t inode, const string_t *name, int32_t *nv)\n{\n    int64_t n;\n    int result;\n\n    if ((result=dao_get_xattr_int64(client_ctx, inode, name, &n)) == 0) {\n        *nv = n;\n    }\n\n    return result;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/db/dao/granted_pool.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"func.h\"\n#include \"granted_pool.h\"\n\nint dao_granted_create(FDIRClientContext *client_ctx, const string_t *username,\n        FCFSAuthGrantedPoolInfo *granted)\n{\n    int result;\n    int64_t inode;\n    AuthFullPath fp;\n    FDIRClientOperFnamePair path;\n    FDIRDEntryInfo dentry;\n\n    AUTH_SET_GRANTED_POOL_PATH(fp, username, granted->pool_id);\n    AUTH_SET_PATH_OPER_FNAME(path, fp);\n    if ((result=fdir_client_lookup_inode_by_path_ex(client_ctx,\n                    &path, LOG_DEBUG, &inode)) != 0)\n    {\n        if (result != ENOENT) {\n            return result;\n        }\n\n        if ((result=fdir_client_create_dentry(client_ctx, &path,\n                        DAO_MODE_FILE, &dentry)) != 0)\n        {\n            return result;\n        }\n        inode = dentry.inode;\n    }\n\n    if ((result=dao_set_xattr_integer(client_ctx, inode,\n                    &AUTH_XTTR_NAME_FDIR, granted->privs.fdir)) != 0)\n    {\n        return result;\n    }\n    if ((result=dao_set_xattr_integer(client_ctx, inode,\n                    &AUTH_XTTR_NAME_FSTORE, granted->privs.fstore)) != 0)\n    {\n        return result;\n    }\n\n    granted->id = inode;\n    return 0;\n}\n\nint dao_granted_remove(FDIRClientContext *client_ctx,\n        const string_t *username, const int64_t pool_id)\n{\n    const int flags = 0;\n    AuthFullPath fp;\n    FDIRClientOperFnamePair path;\n\n    AUTH_SET_GRANTED_POOL_PATH(fp, username, pool_id);\n    AUTH_SET_PATH_OPER_FNAME(path, fp);\n    return fdir_client_remove_dentry(client_ctx, &path, flags);\n}\n\nstatic int dump_to_granted_array(FDIRClientContext *client_ctx,\n        const FDIRClientDentryArray *darray,\n        FCFSAuthGrantedPoolArray *parray)\n{\n    const FDIRClientDentry *entry;\n    const FDIRClientDentry *end;\n    char pool_id_buff[32];\n    char *endptr;\n    FCFSAuthGrantedPoolFullInfo *new_gpools;\n    FCFSAuthGrantedPoolFullInfo *gpool;\n    FCFSAuthGrantedPoolInfo *granted;\n    int len;\n    int result;\n\n    if (darray->count > parray->alloc) {\n        new_gpools = (FCFSAuthGrantedPoolFullInfo *)fc_malloc(\n                sizeof(FCFSAuthGrantedPoolFullInfo) * darray->count);\n        if (new_gpools == NULL) {\n            return ENOMEM;\n        }\n\n        if (parray->gpools != parray->fixed) {\n            free(parray->gpools);\n        }\n        parray->gpools = new_gpools;\n        parray->alloc = darray->count;\n    }\n\n    end = darray->entries + darray->count;\n    for (entry=darray->entries, gpool=parray->gpools;\n            entry<end; entry++, gpool++)\n    {\n        if (entry->name.len < sizeof(pool_id_buff)) {\n            len = entry->name.len;\n        } else {\n            len = sizeof(pool_id_buff) - 1;\n        }\n        memcpy(pool_id_buff, entry->name.str, len);\n        *(pool_id_buff + len) = '\\0';\n\n        granted = &gpool->granted;\n        granted->id = entry->dentry.inode;\n        granted->pool_id = strtoll(pool_id_buff, &endptr, 10);\n        if ((result=dao_get_xattr_int32(client_ctx, granted->id,\n                        &AUTH_XTTR_NAME_FDIR, &granted->privs.fdir)) != 0)\n        {\n            return result;\n        }\n        if ((result=dao_get_xattr_int32(client_ctx, granted->id,\n                        &AUTH_XTTR_NAME_FSTORE, &granted->privs.fstore)) != 0)\n        {\n            return result;\n        }\n    }\n\n    parray->count = darray->count;\n    return 0;\n}\n\nint dao_granted_list(FDIRClientContext *client_ctx, const string_t *username,\n        FCFSAuthGrantedPoolArray *granted_array)\n{\n    const int flags = 0;\n    int result;\n    AuthFullPath fp;\n    FDIRClientOperFnamePair path;\n    FDIRClientDentryArray dentry_array;\n\n    if ((result=fdir_client_dentry_array_init(&dentry_array)) != 0) {\n        return result;\n    }\n\n    AUTH_SET_USER_PATH1(fp, username,\n            AUTH_DIR_NAME_GRANTED_STR,\n            AUTH_DIR_NAME_GRANTED_LEN);\n    AUTH_SET_PATH_OPER_FNAME(path, fp);\n    if ((result=fdir_client_list_dentry_by_path(client_ctx,\n                    &path, &dentry_array, flags)) != 0)\n    {\n        fdir_client_dentry_array_free(&dentry_array);\n        return result;\n    }\n\n    result = dump_to_granted_array(client_ctx,\n            &dentry_array, granted_array);\n    fdir_client_dentry_array_free(&dentry_array);\n    return result;\n}\n"
  },
  {
    "path": "src/auth/server/db/dao/granted_pool.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _AUTH_DAO_GRANTED_POOL_H\n#define _AUTH_DAO_GRANTED_POOL_H\n\n#include \"fastcommon/fast_mpool.h\"\n#include \"types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint dao_granted_create(FDIRClientContext *client_ctx, const string_t *username,\n        FCFSAuthGrantedPoolInfo *granted);\n\nint dao_granted_remove(FDIRClientContext *client_ctx,\n        const string_t *username, const int64_t pool_id);\n\nint dao_granted_list(FDIRClientContext *client_ctx, const string_t *username,\n        FCFSAuthGrantedPoolArray *granted_array);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/db/dao/storage_pool.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"func.h\"\n#include \"storage_pool.h\"\n\nint dao_spool_create(FDIRClientContext *client_ctx,\n        const string_t *username, FCFSAuthStoragePoolInfo *spool)\n{\n    int result;\n    int64_t inode;\n    AuthFullPath fp;\n    FDIRClientOperFnamePair path;\n    FDIRDEntryInfo dentry;\n\n    AUTH_SET_USER_PATH2(fp, username, AUTH_DIR_NAME_CREATED_STR,\n            AUTH_DIR_NAME_CREATED_LEN, spool->name);\n    AUTH_SET_PATH_OPER_FNAME(path, fp);\n    if ((result=fdir_client_create_dentry(client_ctx, &path,\n                    DAO_MODE_FILE, &dentry)) == 0)\n    {\n        inode = dentry.inode;\n    } else if (result == EEXIST) {\n        if ((result=fdir_client_lookup_inode_by_path_ex(client_ctx,\n                        &path, LOG_ERR, &inode)) != 0)\n        {\n            return result;\n        }\n    } else {\n        return result;\n    }\n\n    if ((result=dao_set_xattr_integer(client_ctx, inode,\n                    &AUTH_XTTR_NAME_QUOTA, spool->quota)) != 0)\n    {\n        return result;\n    }\n    if ((result=dao_set_xattr_integer(client_ctx, inode,\n                    &AUTH_XTTR_NAME_STATUS,\n                    FCFS_AUTH_POOL_STATUS_NORMAL)) != 0)\n    {\n        return result;\n    }\n\n    spool->id = inode;\n    return 0;\n}\n\nint dao_spool_remove(FDIRClientContext *client_ctx, const int64_t spool_id)\n{\n    return dao_set_xattr_integer(client_ctx, spool_id, &AUTH_XTTR_NAME_STATUS,\n            FCFS_AUTH_POOL_STATUS_DELETED);\n}\n\nint dao_spool_set_quota(FDIRClientContext *client_ctx,\n        const int64_t spool_id, const int64_t quota)\n{\n    return dao_set_xattr_integer(client_ctx, spool_id,\n            &AUTH_XTTR_NAME_QUOTA, quota);\n}\n\nstatic int dump_to_spool_array(FDIRClientContext *client_ctx,\n        struct fast_mpool_man *mpool, const FDIRClientDentryArray *darray,\n        FCFSAuthStoragePoolArray *parray)\n{\n    const FDIRClientDentry *entry;\n    const FDIRClientDentry *end;\n    FCFSAuthStoragePoolInfo *new_spools;\n    FCFSAuthStoragePoolInfo *spool;\n    int result;\n\n    if (darray->count > parray->alloc) {\n        new_spools = (FCFSAuthStoragePoolInfo *)fc_malloc(\n                sizeof(FCFSAuthStoragePoolInfo) * darray->count);\n        if (new_spools == NULL) {\n            return ENOMEM;\n        }\n\n        if (parray->spools != parray->fixed) {\n            free(parray->spools);\n        }\n        parray->spools = new_spools;\n        parray->alloc = darray->count;\n    }\n\n    end = darray->entries + darray->count;\n    for (entry=darray->entries, spool=parray->spools;\n            entry<end; entry++, spool++)\n    {\n        spool->id = entry->dentry.inode;\n        spool->name = entry->name;\n        if ((result=dao_get_xattr_int64(client_ctx, spool->id,\n                        &AUTH_XTTR_NAME_QUOTA, &spool->quota)) != 0)\n        {\n            return result;\n        }\n        if ((result=dao_get_xattr_int32(client_ctx, spool->id,\n                        &AUTH_XTTR_NAME_STATUS, &spool->status)) != 0)\n        {\n            return result;\n        }\n    }\n\n    parray->count = darray->count;\n    return 0;\n}\n\nint dao_spool_list(FDIRClientContext *client_ctx, const string_t *username,\n        struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *spool_array)\n{\n    const int flags = 0;\n    int result;\n    AuthFullPath fp;\n    FDIRClientOperFnamePair path;\n    FDIRClientDentryArray dentry_array;\n\n    if ((result=fdir_client_dentry_array_init_ex(\n                    &dentry_array, mpool)) != 0)\n    {\n        return result;\n    }\n\n    AUTH_SET_USER_PATH1(fp, username,\n            AUTH_DIR_NAME_CREATED_STR,\n            AUTH_DIR_NAME_CREATED_LEN);\n    AUTH_SET_PATH_OPER_FNAME(path, fp);\n    if ((result=fdir_client_list_dentry_by_path(client_ctx,\n                    &path, &dentry_array, flags)) != 0)\n    {\n        fdir_client_dentry_array_free(&dentry_array);\n        return result;\n    }\n\n    result = dump_to_spool_array(client_ctx, mpool,\n            &dentry_array, spool_array);\n    fdir_client_dentry_array_free(&dentry_array);\n    return result;\n}\n\nint dao_spool_set_base_path_inode(FDIRClientContext *client_ctx)\n{\n    AuthFullPath fp;\n    FDIRClientOperFnamePair path;\n\n    AUTH_SET_BASE_PATH(fp);\n    AUTH_SET_PATH_OPER_FNAME(path, fp);\n    return fdir_client_lookup_inode_by_path(client_ctx,\n            &path, &DAO_BASE_PATH_INODE);\n}\n\nint dao_spool_get_auto_id(FDIRClientContext *client_ctx, int64_t *auto_id)\n{\n    return dao_get_xattr_int64(client_ctx, DAO_BASE_PATH_INODE,\n            &AUTH_XTTR_NAME_POOL_AUTO_ID, auto_id);\n}\n\nint dao_spool_set_auto_id(FDIRClientContext *client_ctx, const int64_t auto_id)\n{\n    return dao_set_xattr_integer(client_ctx, DAO_BASE_PATH_INODE,\n            &AUTH_XTTR_NAME_POOL_AUTO_ID, auto_id);\n}\n"
  },
  {
    "path": "src/auth/server/db/dao/storage_pool.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _AUTH_DAO_STORAGE_POOL_H\n#define _AUTH_DAO_STORAGE_POOL_H\n\n#include \"fastcommon/fast_mpool.h\"\n#include \"types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint dao_spool_set_base_path_inode(FDIRClientContext *client_ctx);\n\nint dao_spool_get_auto_id(FDIRClientContext *client_ctx, int64_t *auto_id);\n\nint dao_spool_set_auto_id(FDIRClientContext *client_ctx, const int64_t auto_id);\n\nint dao_spool_create(FDIRClientContext *client_ctx,\n        const string_t *username, FCFSAuthStoragePoolInfo *spool);\n\nint dao_spool_remove(FDIRClientContext *client_ctx, const int64_t spool_id);\n\nint dao_spool_set_quota(FDIRClientContext *client_ctx,\n        const int64_t spool_id, const int64_t quota);\n\nint dao_spool_list(FDIRClientContext *client_ctx, const string_t *username,\n        struct fast_mpool_man *mpool, FCFSAuthStoragePoolArray *spool_array);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/db/dao/types.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _AUTH_DAO_TYPES_H\n#define _AUTH_DAO_TYPES_H\n\n#include <limits.h>\n#include \"fastdir/client/fdir_client.h\"\n#include \"common/auth_types.h\"\n\n#define AUTH_NAMESPACE_STR    \"sys-auth\"\n#define AUTH_NAMESPACE_LEN    (sizeof(AUTH_NAMESPACE_STR) - 1)\n\n#define AUTH_BASE_PATH_STR    \"/home\"\n#define AUTH_BASE_PATH_LEN    (sizeof(AUTH_BASE_PATH_STR) - 1)\n\ntypedef struct auth_full_path {\n    char buff[PATH_MAX];\n    FDIRDEntryFullName fullname;\n} AuthFullPath;\n\n#define AUTH_SET_GRANTED_POOL_PATH(fp, username, granted_id)  \\\n    do {  \\\n        char *p;  \\\n        FC_SET_STRING_EX(fp.fullname.ns, AUTH_NAMESPACE_STR, \\\n                AUTH_NAMESPACE_LEN); \\\n        fp.fullname.path.str = fp.buff;  \\\n        p = fp.buff; \\\n        memcpy(p, AUTH_BASE_PATH_STR, AUTH_BASE_PATH_LEN);  \\\n        p += AUTH_BASE_PATH_LEN;  \\\n        *p++ = '/';  \\\n        memcpy(p, (username)->str, (username)->len);  \\\n        p += (username)->len;  \\\n        *p++ = '/';  \\\n        memcpy(p, AUTH_DIR_NAME_GRANTED_STR, AUTH_DIR_NAME_GRANTED_LEN);  \\\n        p += AUTH_DIR_NAME_GRANTED_LEN;  \\\n        *p++ = '/';  \\\n        p += fc_itoa(granted_id, p); \\\n        *p = '\\0';  \\\n        fp.fullname.path.len = p - fp.buff;  \\\n    } while (0)\n\n#define AUTH_SET_USER_PATH2(fp, username, subdir1_str, subdir1_len, subdir2)  \\\n    do {  \\\n        char *p;  \\\n        FC_SET_STRING_EX(fp.fullname.ns, AUTH_NAMESPACE_STR, \\\n                AUTH_NAMESPACE_LEN); \\\n        fp.fullname.path.str = fp.buff;  \\\n        p = fp.buff; \\\n        memcpy(p, AUTH_BASE_PATH_STR, AUTH_BASE_PATH_LEN);  \\\n        p += AUTH_BASE_PATH_LEN;  \\\n        *p++ = '/';  \\\n        memcpy(p, (username)->str, (username)->len);  \\\n        p += (username)->len;  \\\n        *p++ = '/';  \\\n        memcpy(p, subdir1_str, subdir1_len);  \\\n        p += subdir1_len;  \\\n        *p++ = '/';  \\\n        memcpy(p, (subdir2).str, (subdir2).len);  \\\n        p += (subdir2).len;  \\\n        *p = '\\0';  \\\n        fp.fullname.path.len = p - fp.buff;  \\\n    } while (0)\n\n#define AUTH_SET_USER_PATH1(fp, username, subdir1_str, subdir1_len)  \\\n    do {  \\\n        char *p;  \\\n        FC_SET_STRING_EX(fp.fullname.ns, AUTH_NAMESPACE_STR,  \\\n                AUTH_NAMESPACE_LEN); \\\n        fp.fullname.path.str = fp.buff;  \\\n        p = fp.buff; \\\n        memcpy(p, AUTH_BASE_PATH_STR, AUTH_BASE_PATH_LEN);  \\\n        p += AUTH_BASE_PATH_LEN;  \\\n        *p++ = '/';  \\\n        memcpy(p, (username)->str, (username)->len);  \\\n        p += (username)->len;  \\\n        *p++ = '/';  \\\n        memcpy(p, subdir1_str, subdir1_len);  \\\n        p += subdir1_len;  \\\n        *p = '\\0';  \\\n        fp.fullname.path.len = p - fp.buff;  \\\n    } while (0)\n\n#define AUTH_SET_USER_HOME(fp, username)  \\\n    do {  \\\n        char *p;  \\\n        FC_SET_STRING_EX(fp.fullname.ns, AUTH_NAMESPACE_STR, \\\n                AUTH_NAMESPACE_LEN); \\\n        fp.fullname.path.str = fp.buff;  \\\n        p = fp.buff; \\\n        memcpy(p, AUTH_BASE_PATH_STR, AUTH_BASE_PATH_LEN);  \\\n        p += AUTH_BASE_PATH_LEN;  \\\n        *p++ = '/';  \\\n        memcpy(p, (username)->str, (username)->len);  \\\n        p += (username)->len;  \\\n        *p = '\\0';  \\\n        fp.fullname.path.len = p - fp.buff;  \\\n    } while (0)\n\n\n#define AUTH_SET_BASE_PATH(fp)  \\\n    do { \\\n        FC_SET_STRING_EX(fp.fullname.ns, AUTH_NAMESPACE_STR, \\\n                AUTH_NAMESPACE_LEN); \\\n        FC_SET_STRING_EX(fp.fullname.path, AUTH_BASE_PATH_STR, \\\n                AUTH_BASE_PATH_LEN); \\\n    } while (0)\n\n#define AUTH_SET_PATH_OPER_FNAME(path, fp) \\\n    FDIR_SET_OPERATOR(path.oper, 0, 0, 0, NULL); \\\n    path.fullname = fp.fullname\n\n#define AUTH_SET_OPER_INODE_PAIR(oino, _inode) \\\n    FDIR_SET_OPERATOR(oino.oper, 0, 0, 0, NULL); \\\n    oino.inode = _inode\n\n#endif\n"
  },
  {
    "path": "src/auth/server/db/dao/user.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"func.h\"\n#include \"user.h\"\n\nstatic int user_make_subdir(FDIRClientContext *client_ctx,\n        const string_t *username, const char *subdir_str,\n        const int subdir_len, const bool check_exist)\n{\n    AuthFullPath fp;\n    FDIRClientOperFnamePair path;\n    FDIRDEntryInfo dentry;\n    int result;\n\n    AUTH_SET_USER_PATH1(fp, username, subdir_str, subdir_len);\n    AUTH_SET_PATH_OPER_FNAME(path, fp);\n    if (check_exist) {\n        result = fdir_client_lookup_inode_by_path_ex(client_ctx,\n                &path, LOG_DEBUG, &dentry.inode);\n        if (result == 0) {\n            return 0;\n        } else if (result != ENOENT) {\n            return result;\n        }\n    }\n\n    result = fdir_client_create_dentry(client_ctx, &path,\n            DAO_MODE_DIR, &dentry);\n    return result == EEXIST ? 0 : result;\n}\n\nint dao_user_create(FDIRClientContext *client_ctx, FCFSAuthUserInfo *user)\n{\n    int64_t inode;\n    int result;\n    bool check_exist;\n    AuthFullPath home;\n    FDIRClientOperFnamePair path;\n    FDIRDEntryInfo dentry;\n\n    AUTH_SET_USER_HOME(home, &user->name);\n    AUTH_SET_PATH_OPER_FNAME(path, home);\n    result = fdir_client_create_dentry(client_ctx,\n            &path, DAO_MODE_DIR, &dentry);\n    if (result == 0) {\n        inode = dentry.inode;\n        check_exist = false;\n    } else if (result == EEXIST) {\n        if ((result=fdir_client_lookup_inode_by_path_ex(client_ctx,\n                        &path, LOG_ERR, &inode)) != 0)\n        {\n            return result;\n        }\n        check_exist = true;\n    } else {\n        return result;\n    }\n\n    if ((result=user_make_subdir(client_ctx, &user->name,\n                    AUTH_DIR_NAME_CREATED_STR,\n                    AUTH_DIR_NAME_CREATED_LEN,\n                    check_exist)) != 0)\n    {\n        return result;\n    }\n    if ((result=user_make_subdir(client_ctx, &user->name,\n                    AUTH_DIR_NAME_GRANTED_STR,\n                    AUTH_DIR_NAME_GRANTED_LEN,\n                    check_exist)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=dao_set_xattr_string(client_ctx, inode,\n                    &AUTH_XTTR_NAME_PASSWD, &user->passwd)) != 0)\n    {\n        return result;\n    }\n    if ((result=dao_set_xattr_integer(client_ctx, inode,\n                    &AUTH_XTTR_NAME_PRIV, user->priv)) != 0)\n    {\n        return result;\n    }\n    if ((result=dao_set_xattr_integer(client_ctx, inode, &AUTH_XTTR_NAME_STATUS,\n                    FCFS_AUTH_USER_STATUS_NORMAL)) != 0)\n    {\n        return result;\n    }\n\n    user->id = inode;\n    return 0;\n}\n\nint dao_user_update_priv(FDIRClientContext *client_ctx,\n        const int64_t user_id, const int64_t priv)\n{\n    return dao_set_xattr_integer(client_ctx, user_id, &AUTH_XTTR_NAME_PRIV, priv);\n}\n\nint dao_user_update_passwd(FDIRClientContext *client_ctx,\n        const int64_t user_id, const string_t *passwd)\n{\n    return dao_set_xattr_string(client_ctx, user_id,\n            &AUTH_XTTR_NAME_PASSWD, passwd);\n}\n\nint dao_user_remove(FDIRClientContext *client_ctx, const int64_t user_id)\n{\n    return dao_set_xattr_integer(client_ctx, user_id, &AUTH_XTTR_NAME_STATUS,\n            FCFS_AUTH_USER_STATUS_DELETED);\n}\n\nstatic int dump_to_user_array(FDIRClientContext *client_ctx,\n        struct fast_mpool_man *mpool, const FDIRClientDentryArray *darray,\n        FCFSAuthUserArray *uarray)\n{\n    const FDIRClientDentry *entry;\n    const FDIRClientDentry *end;\n    FCFSAuthUserInfo *new_users;\n    FCFSAuthUserInfo *user;\n    char buff[256];\n    string_t value;\n    int result;\n\n    if (darray->count > uarray->alloc) {\n        new_users = (FCFSAuthUserInfo *)fc_malloc(\n                sizeof(FCFSAuthUserInfo) * darray->count);\n        if (new_users == NULL) {\n            return ENOMEM;\n        }\n\n        if (uarray->users != uarray->fixed) {\n            free(uarray->users);\n        }\n        uarray->users = new_users;\n        uarray->alloc = darray->count;\n    }\n\n    end = darray->entries + darray->count;\n    for (entry=darray->entries, user=uarray->users;\n            entry<end; entry++, user++)\n    {\n        user->id = entry->dentry.inode;\n        user->name = entry->name;\n\n        value.str = buff;\n        if ((result=dao_get_xattr_string(client_ctx, user->id,\n                        &AUTH_XTTR_NAME_PASSWD, &value, sizeof(buff))) != 0)\n        {\n            return result;\n        }\n        if ((result=fast_mpool_alloc_string_ex2(mpool,\n                        &user->passwd, &value)) != 0)\n        {\n            return result;\n        }\n\n        if ((result=dao_get_xattr_int64(client_ctx, user->id,\n                        &AUTH_XTTR_NAME_PRIV, &user->priv)) != 0)\n        {\n            return result;\n        }\n        if ((result=dao_get_xattr_int32(client_ctx, user->id,\n                        &AUTH_XTTR_NAME_STATUS, &user->status)) != 0)\n        {\n            return result;\n        }\n    }\n\n    uarray->count = darray->count;\n    return 0;\n}\n\nint dao_user_list(FDIRClientContext *client_ctx, struct fast_mpool_man\n        *mpool, FCFSAuthUserArray *user_array)\n{\n    const int flags = 0;\n    int result;\n    FDIRDEntryInfo dentry;\n    AuthFullPath fp;\n    FDIRClientOperFnamePair root;\n    FDIRClientOperFnamePair path;\n    FDIRClientOperInodePair oino;\n    FDIRClientDentryArray dentry_array;\n\n    if ((result=fdir_client_dentry_array_init_ex(\n                    &dentry_array, mpool)) != 0)\n    {\n        return result;\n    }\n\n    AUTH_SET_BASE_PATH(fp);\n    AUTH_SET_PATH_OPER_FNAME(path, fp);\n    if ((result=fdir_client_lookup_inode_by_path_ex(client_ctx,\n                    &path, LOG_DEBUG, &dentry.inode)) != 0)\n    {\n        if (result != ENOENT) {\n            return result;\n        }\n\n        FC_SET_STRING_EX(root.fullname.ns, AUTH_NAMESPACE_STR,\n                AUTH_NAMESPACE_LEN);\n        FC_SET_STRING_EX(root.fullname.path, \"/\", 1);\n        FDIR_SET_OPERATOR(root.oper, 0, 0, 0, NULL);\n        if ((result=fdir_client_create_dentry(client_ctx,\n                        &root, DAO_MODE_DIR, &dentry)) != 0)\n        {\n            return result;\n        }\n        if ((result=fdir_client_create_dentry(client_ctx,\n                        &path, DAO_MODE_DIR, &dentry)) != 0)\n        {\n            return result;\n        }\n    }\n\n    AUTH_SET_OPER_INODE_PAIR(oino, dentry.inode);\n    if ((result=fdir_client_list_dentry_by_inode(client_ctx, &DAO_NAMESPACE,\n                    &oino, &dentry_array, flags)) != 0)\n    {\n        fdir_client_dentry_array_free(&dentry_array);\n        return result;\n    }\n\n    result = dump_to_user_array(client_ctx, mpool,\n            &dentry_array, user_array);\n    fdir_client_dentry_array_free(&dentry_array);\n    return result;\n}\n"
  },
  {
    "path": "src/auth/server/db/dao/user.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _AUTH_DAO_USER_H\n#define _AUTH_DAO_USER_H\n\n#include \"fastcommon/fast_mpool.h\"\n#include \"types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint dao_user_create(FDIRClientContext *client_ctx, FCFSAuthUserInfo *user);\n\nint dao_user_remove(FDIRClientContext *client_ctx, const int64_t user_id);\n\nint dao_user_update_priv(FDIRClientContext *client_ctx,\n        const int64_t user_id, const int64_t priv);\n\nint dao_user_update_passwd(FDIRClientContext *client_ctx,\n        const int64_t user_id, const string_t *passwd);\n\nint dao_user_list(FDIRClientContext *client_ctx, struct fast_mpool_man\n        *mpool, FCFSAuthUserArray *user_array);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/db/pool_usage_updater.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/fc_atomic.h\"\n#include \"fastdir/client/fdir_client.h\"\n#include \"../server_global.h\"\n#include \"auth_db.h\"\n#include \"pool_usage_updater.h\"\n\ntypedef struct {\n    bool inited;\n    volatile char running;\n    struct {\n        volatile uint32_t current;\n        volatile uint32_t next;\n    } generation;\n    FDIRClientNamespaceStatArray nss_array;\n} PoolUsageUpdaterContext;\n\nstatic PoolUsageUpdaterContext updater_ctx = {false, 0, {0, 0}};\n\n#define NSS_ARRAY updater_ctx.nss_array\n\n#define IS_SAME_GENERATION (updater_ctx.generation.current == \\\n        updater_ctx.generation.next)\n\nstatic int nss_fetch(ConnectionInfo *conn)\n{\n    int result;\n    bool is_last;\n    FDIRClientNamespaceStatEntry *entry;\n    FDIRClientNamespaceStatEntry *end;\n\n    do {\n        if ((result=fdir_client_proto_nss_fetch(&g_fdir_client_vars.\n                        client_ctx, conn, &NSS_ARRAY, &is_last)) != 0)\n        {\n            break;\n        }\n\n        if (NSS_ARRAY.count == 0) {\n            break;\n        }\n\n        end = NSS_ARRAY.entries + NSS_ARRAY.count;\n        for (entry=NSS_ARRAY.entries; entry<end; entry++) {\n            /*\n            logInfo(\"%d. ns name: %.*s, used bytes: %.3lf GB\",\n                    (int)(entry - NSS_ARRAY.entries + 1),\n                    entry->ns_name.len, entry->ns_name.str,\n                    (double)entry->used_bytes / (1024 * 1024 * 1024));\n                    */\n            adb_spool_set_used_bytes(&entry->ns_name, entry->used_bytes);\n        }\n\n    } while (!is_last && IS_SAME_GENERATION);\n\n    return result;\n}\n\nstatic int pool_usage_refresh(ConnectionInfo *conn)\n{\n    int i;\n    int result;\n\n    if ((result=fdir_client_proto_nss_subscribe(&g_fdir_client_vars.\n                    client_ctx, conn)) != 0)\n    {\n        return result;\n    }\n\n    while (SF_G_CONTINUE_FLAG && IS_SAME_GENERATION) {\n        if ((result=nss_fetch(conn)) != 0) {\n            break;\n        }\n\n        for (i=0; i<POOL_USAGE_REFRESH_INTERVAL &&\n                IS_SAME_GENERATION; i++)\n        {\n            sleep(1);\n        }\n    }\n\n    return result;\n}\n\nstatic void *pool_usage_refresh_thread_func(void *arg)\n{\n    const bool shared = false;\n    SFConnectionManager *cm;\n    ConnectionInfo *conn;\n    int result;\n\n#ifdef OS_LINUX\n    prctl(PR_SET_NAME, \"pool-usage-updater\");\n#endif\n\n    FC_ATOMIC_SET(updater_ctx.running, 1);\n    cm = &g_fdir_client_vars.client_ctx.cm;\n    while (SF_G_CONTINUE_FLAG) {\n        if ((conn=cm->ops.get_master_connection(cm, 0,\n                        shared, &result)) == NULL)\n        {\n            sleep(1);\n            continue;\n        }\n\n        pool_usage_refresh(conn);\n        cm->ops.close_connection(cm, conn);\n        if (IS_SAME_GENERATION) {\n            sleep(1);\n        } else {\n            break;\n        }\n    }\n\n    FC_ATOMIC_SET(updater_ctx.running, 0);\n    return NULL;\n}\n\nstatic int pool_usage_updater_init()\n{\n    int result;\n\n    if ((result=fdir_client_namespace_stat_array_init(&NSS_ARRAY)) != 0) {\n        return result;\n    }\n\n    if ((result=fdir_client_simple_init(g_server_global_vars.\n                    fdir_client_cfg_filename)) != 0)\n    {\n        return result;\n    }\n    return 0;\n}\n\nint pool_usage_updater_start()\n{\n    int result;\n    pthread_t tid;\n\n    if (!updater_ctx.inited) {\n        if ((result=pool_usage_updater_init()) != 0) {\n            return result;\n        }\n\n        updater_ctx.inited = true;\n    }\n\n    if (FC_ATOMIC_GET(updater_ctx.running) != 0) {\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"pool_usage_updater thread already running\",\n                __LINE__);\n        return 0;\n    }\n\n    updater_ctx.generation.current = ++updater_ctx.generation.next;\n    return fc_create_thread(&tid, pool_usage_refresh_thread_func,\n            NULL, SF_G_THREAD_STACK_SIZE);\n}\n\nvoid pool_usage_updater_terminate()\n{\n    int count;\n\n    if (!updater_ctx.inited || FC_ATOMIC_GET(updater_ctx.running) == 0) {\n        return;\n    }\n\n    count = 0;\n    ++updater_ctx.generation.next;\n    while (FC_ATOMIC_GET(updater_ctx.running) != 0) {\n        sleep(1);\n        count++;\n    }\n\n    logInfo(\"file: \"__FILE__\", line: %d, \"\n            \"pool_usage_updater thread exit after waiting count: %d\",\n            __LINE__, count);\n}\n"
  },
  {
    "path": "src/auth/server/db/pool_usage_updater.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_POOL_USAGE_UPDATER_H\n#define _FCFS_POOL_USAGE_UPDATER_H\n\n#include \"../server_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint pool_usage_updater_start();\nvoid pool_usage_updater_terminate();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/fcfs_authd.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <unistd.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <pthread.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"fastcommon/process_ctrl.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"sf/sf_global.h\"\n#include \"sf/sf_func.h\"\n#include \"sf/sf_nio.h\"\n#include \"sf/sf_service.h\"\n#include \"sf/sf_util.h\"\n#include \"common/auth_proto.h\"\n#include \"server_global.h\"\n#include \"server_func.h\"\n#include \"session_subscribe.h\"\n#include \"cluster_relationship.h\"\n#include \"common_handler.h\"\n#include \"cluster_handler.h\"\n#include \"service_handler.h\"\n\nstatic int setup_server_env(const char *config_filename);\n\nstatic bool daemon_mode = true;\nstatic const char *config_filename;\nstatic char g_pid_filename[MAX_PATH_SIZE];\n\nstatic int process_cmdline(int argc, char *argv[], bool *continue_flag)\n{\n    char *action;\n    bool stop;\n    int result;\n\n    *continue_flag = false;\n    if (argc < 2) {\n        sf_usage(argv[0]);\n        return 1;\n    }\n\n    config_filename = sf_parse_daemon_mode_and_action(argc, argv,\n            &g_fcfs_auth_global_vars.version, &daemon_mode, &action);\n    if (config_filename == NULL) {\n        return 0;\n    }\n\n    log_init2();\n    //log_set_time_precision(&g_log_context, LOG_TIME_PRECISION_USECOND);\n\n    result = sf_get_base_path_from_conf_file(config_filename);\n    if (result != 0) {\n        log_destroy();\n        return result;\n    }\n\n    snprintf(g_pid_filename, sizeof(g_pid_filename), \n             \"%s/authd.pid\", SF_G_BASE_PATH_STR);\n\n    stop = false;\n    result = process_action(g_pid_filename, action, &stop);\n    if (result != 0) {\n        if (result == EINVAL) {\n            sf_usage(argv[0]);\n        }\n        log_destroy();\n        return result;\n    }\n\n    if (stop) {\n        log_destroy();\n        return 0;\n    }\n\n    *continue_flag = true;\n    return 0;\n}\n\nint main(int argc, char *argv[])\n{\n    pthread_t schedule_tid;\n    int wait_count;\n    int result;\n\n    result = process_cmdline(argc, argv, (bool *)&SF_G_CONTINUE_FLAG);\n    if (!SF_G_CONTINUE_FLAG) {\n        return result;\n    }\n\n    sf_enable_exit_on_oom();\n    srand(time(NULL));\n    fast_mblock_manager_init();\n\n    //sched_set_delay_params(300, 1024);\n    do {\n        if ((result=setup_server_env(config_filename)) != 0) {\n            break;\n        }\n\n        if ((result=sf_startup_schedule(&schedule_tid)) != 0) {\n            break;\n        }\n\n        if ((result=sf_add_slow_log_schedule(&g_server_global_vars.\n                        slow_log)) != 0)\n        {\n            break;\n        }\n\n        if ((result=sf_socket_server()) != 0) {\n            break;\n        }\n        if ((result=sf_socket_server_ex(&CLUSTER_SF_CTX)) != 0) {\n            break;\n        }\n\n        if ((result=write_to_pid_file(g_pid_filename)) != 0) {\n            break;\n        }\n\n        if ((result=service_handler_init()) != 0) {\n            break;\n        }\n\n        if ((result=session_subscribe_init()) != 0) {\n            break;\n        }\n\n        common_handler_init();\n        //sched_print_all_entries();\n\n        result = sf_service_init_ex(&CLUSTER_SF_CTX, \"cluster\",\n                cluster_alloc_thread_extra_data,\n                cluster_thread_loop_callback, NULL,\n                sf_proto_set_body_length, NULL, cluster_deal_task,\n                cluster_task_finish_cleanup, cluster_recv_timeout_callback,\n                1000, sizeof(FCFSAuthProtoHeader), sizeof(AuthServerTaskArg));\n        if (result != 0) {\n            break;\n        }\n        sf_enable_thread_notify_ex(&CLUSTER_SF_CTX, true);\n        sf_accept_loop_ex(&CLUSTER_SF_CTX, false);\n\n        result = sf_service_init_ex(&SERVICE_SF_CTX, \"service\",\n                service_alloc_thread_extra_data, NULL, NULL,\n                sf_proto_set_body_length, NULL, service_deal_task,\n                service_task_finish_cleanup, NULL, 1000,\n                sizeof(FCFSAuthProtoHeader), sizeof(AuthServerTaskArg));\n        if (result != 0) {\n            break;\n        }\n        sf_enable_thread_notify(true);\n\n        if ((result=cluster_relationship_init()) != 0) {\n            break;\n        }\n    } while (0);\n\n    if (result != 0) {\n        lcrit(\"program exit abnomally\");\n        log_destroy();\n        return result;\n    }\n\n    //sched_print_all_entries();\n    sf_accept_loop();\n\n    if (g_schedule_flag) {\n        pthread_kill(schedule_tid, SIGINT);\n    }\n\n    wait_count = 0;\n    while ((SF_G_ALIVE_THREAD_COUNT != 0) || g_schedule_flag) {\n        fc_sleep_ms(10);\n        if (++wait_count > 1000) {\n            lwarning(\"waiting timeout, exit!\");\n            break;\n        }\n    }\n\n    sf_service_destroy();\n    delete_pid_file(g_pid_filename);\n    logInfo(\"file: \"__FILE__\", line: %d, \"\n            \"program exit normally.\\n\", __LINE__);\n    log_destroy();\n    return 0;\n}\n\nstatic int setup_server_env(const char *config_filename)\n{\n    int result;\n\n    sf_set_current_time();\n    if ((result=server_load_config(config_filename)) != 0) {\n        return result;\n    }\n\n    if (daemon_mode) {\n        daemon_init(false);\n    }\n    umask(0);\n\n    result = sf_setup_signal_handler();\n\n    log_set_cache(true);\n    return result;\n}\n"
  },
  {
    "path": "src/auth/server/server_func.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/ini_file_reader.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/local_ip_func.h\"\n#include \"sf/sf_global.h\"\n#include \"sf/sf_configs.h\"\n#include \"sf/sf_service.h\"\n#include \"sf/sf_cluster_cfg.h\"\n#include \"fastcfs/vote/fcfs_vote_client.h\"\n#include \"common/auth_proto.h\"\n#include \"common/auth_func.h\"\n#include \"common/server_session.h\"\n#include \"server_global.h\"\n#include \"session_subscribe.h\"\n#include \"cluster_info.h\"\n#include \"server_func.h\"\n\n#define SECTION_NAME_FASTDIR  \"FastDIR\"\n\nstatic int server_load_fdir_client_config(IniContext *ini_context,\n        const char *config_filename)\n{\n#define ITEM_NAME_FDIR_CLIENT_CFG_FILENAME  \"client_config_filename\"\n\n    char full_filename[PATH_MAX];\n    char *client_cfg_filename;\n    int result;\n\n    if ((client_cfg_filename=iniGetRequiredStrValue(SECTION_NAME_FASTDIR,\n                    ITEM_NAME_FDIR_CLIENT_CFG_FILENAME, ini_context)) == NULL)\n    {\n        return ENOENT;\n    }\n\n    resolve_path(config_filename, client_cfg_filename,\n            full_filename, sizeof(full_filename));\n    if (!fileExists(full_filename)) {\n        result = errno != 0 ? errno : ENOENT;\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"config file: %s, item: %s, value: %s, access file %s fail, \"\n                \"errno: %d, error info: %s\", __LINE__, config_filename,\n                ITEM_NAME_FDIR_CLIENT_CFG_FILENAME, client_cfg_filename,\n                full_filename, result, STRERROR(result));\n        return result;\n    }\n\n    if ((g_server_global_vars.fdir_client_cfg_filename=\n                fc_strdup(full_filename)) == NULL)\n    {\n        return ENOMEM;\n    }\n    return 0;\n}\n\nstatic int server_load_admin_generate_config(IniContext *ini_context,\n        const char *config_filename)\n{\n#define SECTION_NAME_ADMIN_GENERATE  \"admin-generate\"\n#define USERNAME_VARIABLE_STR  \"${username}\"\n#define USERNAME_VARIABLE_LEN  (sizeof(USERNAME_VARIABLE_STR) - 1)\n\n    char *mode;\n    string_t username;\n    string_t secret_key_filename;\n    FilenameString new_filename;\n    char full_filename[PATH_MAX];\n    int result;\n\n    mode = iniGetStrValue(SECTION_NAME_ADMIN_GENERATE, \"mode\", ini_context);\n    if (mode != NULL && strcmp(mode, \"always\") == 0) {\n        ADMIN_GENERATE_MODE = AUTH_ADMIN_GENERATE_MODE_ALWAYS;\n    } else {\n        ADMIN_GENERATE_MODE = AUTH_ADMIN_GENERATE_MODE_FIRST;\n    }\n\n    username.str = iniGetStrValue(SECTION_NAME_ADMIN_GENERATE,\n            \"username\", ini_context);\n    if (username.str == NULL || *username.str == '\\0') {\n        username.str = \"admin\";\n    }\n    username.len = strlen(username.str);\n    if ((result=fc_check_filename(&username, \"username\")) != 0) {\n        return result;\n    }\n\n    secret_key_filename.str = iniGetStrValue(SECTION_NAME_ADMIN_GENERATE,\n            \"secret_key_filename\", ini_context);\n    if (secret_key_filename.str == NULL || *secret_key_filename.str == '\\0') {\n        secret_key_filename.str = \"keys/\"USERNAME_VARIABLE_STR\".key\";\n    }\n    secret_key_filename.len = strlen(secret_key_filename.str);\n    fcfs_auth_replace_filename_with_username(&secret_key_filename,\n            &username, &new_filename);\n\n    resolve_path(config_filename, FC_FILENAME_STRING_PTR(new_filename),\n            full_filename, sizeof(full_filename));\n    FC_SET_STRING(secret_key_filename, full_filename);\n\n    if (!fileExists(full_filename)) {\n        char abs_path[PATH_MAX];\n        int create_count;\n\n        getAbsolutePath(full_filename, abs_path, sizeof(abs_path));\n        if ((result=fc_mkdirs_ex(abs_path, 0755, &create_count)) != 0) {\n            return result;\n        }\n        SF_CHOWN_TO_RUNBY_RETURN_ON_ERROR(abs_path);\n    }\n\n    ADMIN_GENERATE_BUFF = (char *)fc_malloc(username.len +\n            secret_key_filename.len + 2);\n    if (ADMIN_GENERATE_BUFF == NULL) {\n        return ENOMEM;\n    }\n    ADMIN_GENERATE_USERNAME.str = ADMIN_GENERATE_BUFF;\n    memcpy(ADMIN_GENERATE_USERNAME.str,\n            username.str, username.len + 1);\n    ADMIN_GENERATE_USERNAME.len = username.len;\n\n    ADMIN_GENERATE_KEY_FILENAME.str =\n        ADMIN_GENERATE_BUFF + username.len + 1;\n    memcpy(ADMIN_GENERATE_KEY_FILENAME.str,\n            secret_key_filename.str,\n            secret_key_filename.len + 1);\n    ADMIN_GENERATE_KEY_FILENAME.len = secret_key_filename.len;\n\n    return 0;\n}\n\nstatic int server_load_pool_generate_config(IniContext *ini_context,\n        const char *config_filename)\n{\n#define SECTION_NAME_POOL_GENERATE  \"pool-generate\"\n\n    char *name_template;\n    int result;\n\n    AUTO_ID_INITIAL = iniGetInt64Value(SECTION_NAME_POOL_GENERATE,\n            \"auto_id_initial\", ini_context, 1);\n\n    name_template = iniGetStrValue(SECTION_NAME_POOL_GENERATE,\n            \"pool_name_template\", ini_context);\n    if (name_template == NULL || *name_template == '\\0') {\n        name_template = FCFS_AUTH_AUTO_ID_TAG_STR;\n    } else if (strstr(name_template, FCFS_AUTH_AUTO_ID_TAG_STR) == NULL) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"config file: %s, pool_name_template: %s is invalid, \"\n                \"must contains %s\", __LINE__, config_filename,\n                name_template, FCFS_AUTH_AUTO_ID_TAG_STR);\n        return EINVAL;\n    }\n\n    POOL_NAME_TEMPLATE.len = strlen(name_template);\n    POOL_NAME_TEMPLATE.str = (char *)fc_malloc(POOL_NAME_TEMPLATE.len + 1);\n    if (POOL_NAME_TEMPLATE.str == NULL) {\n        return ENOMEM;\n    }\n    memcpy(POOL_NAME_TEMPLATE.str, name_template, POOL_NAME_TEMPLATE.len + 1);\n    if ((result=fc_check_filename(&POOL_NAME_TEMPLATE,\n                    \"pool_name_template\")) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nstatic void log_cluster_server_config()\n{\n    FastBuffer buffer;\n\n    if (fast_buffer_init1(&buffer, 1024) != 0) {\n        return;\n    }\n    fc_server_to_config_string(&CLUSTER_SERVER_CONFIG, &buffer);\n    log_it1(LOG_INFO, buffer.data, buffer.length);\n    fast_buffer_destroy(&buffer);\n\n    fc_server_to_log(&CLUSTER_SERVER_CONFIG);\n}\n\nstatic void server_log_configs()\n{\n    char sz_server_config[512];\n    char sz_global_config[512];\n    char sz_slowlog_config[256];\n    char sz_service_config[256];\n    char sz_session_config[512];\n\n    sf_global_config_to_string(sz_global_config, sizeof(sz_global_config));\n    sf_slow_log_config_to_string(&SLOW_LOG_CFG, \"slow-log\",\n            sz_slowlog_config, sizeof(sz_slowlog_config));\n    sf_context_config_to_string(&SERVICE_SF_CTX,\n            sz_service_config, sizeof(sz_service_config));\n    server_session_cfg_to_string(sz_session_config,\n            sizeof(sz_session_config));\n\n    snprintf(sz_server_config, sizeof(sz_server_config),\n            \"admin-generate {mode: %s, username: %s, \"\n            \"secret_key_filename: %s}, pool-generate: \"\n            \"{auto_id_initial: %\"PRId64\", pool_name_template: %s}, \"\n            \"master-election {quorum: %s, vote_node_enabled: %d, \"\n            \"master_lost_timeout: %ds, max_wait_time: %ds}\",\n            (ADMIN_GENERATE_MODE == AUTH_ADMIN_GENERATE_MODE_FIRST ?\n             \"first\" : \"always\"), ADMIN_GENERATE_USERNAME.str,\n            ADMIN_GENERATE_KEY_FILENAME.str, AUTO_ID_INITIAL,\n            POOL_NAME_TEMPLATE.str, sf_get_election_quorum_caption(\n                MASTER_ELECTION_QUORUM), VOTE_NODE_ENABLED,\n            ELECTION_MASTER_LOST_TIMEOUT, ELECTION_MAX_WAIT_TIME);\n\n    logInfo(\"FCFSAuth V%d.%d.%d, %s, %s, service: {%s}, %s\",\n            g_fcfs_auth_global_vars.version.major,\n            g_fcfs_auth_global_vars.version.minor,\n            g_fcfs_auth_global_vars.version.patch,\n            sz_global_config, sz_slowlog_config,\n            sz_service_config, sz_server_config);\n\n    logInfo(\"FastDIR {client_config_filename: %s, \"\n            \"pool_usage_refresh_interval: %d}, %s\",\n            g_server_global_vars.fdir_client_cfg_filename,\n            POOL_USAGE_REFRESH_INTERVAL, sz_session_config);\n\n    log_local_host_ip_addrs();\n    log_cluster_server_config();\n}\n\nstatic int load_master_election_config(const char *cluster_filename)\n{\n    IniContext ini_context;\n    IniFullContext ini_ctx;\n    int result;\n\n    if ((result=iniLoadFromFile(cluster_filename, &ini_context)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                __LINE__, cluster_filename, result);\n        return result;\n    }\n\n    FAST_INI_SET_FULL_CTX_EX(ini_ctx, cluster_filename,\n            \"master-election\", &ini_context);\n    ELECTION_MASTER_LOST_TIMEOUT = iniGetIntCorrectValue(\n            &ini_ctx, \"master_lost_timeout\", 3, 1, 30);\n    ELECTION_MAX_WAIT_TIME = iniGetIntCorrectValue(\n            &ini_ctx, \"max_wait_time\", 5, 1, 300);\n    if ((result=sf_load_election_quorum_config(&MASTER_ELECTION_QUORUM,\n                    &ini_ctx)) == 0)\n    {\n        result = fcfs_vote_client_init_for_server(\n                &ini_ctx, &VOTE_NODE_ENABLED);\n    }\n\n    iniFreeContext(&ini_context);\n    return result;\n}\n\nstatic int load_cluster_config(IniFullContext *ini_ctx,\n        char *full_cluster_filename)\n{\n    int result;\n\n    if ((result=sf_load_cluster_config_ex(&CLUSTER_CONFIG,\n                    ini_ctx, FCFS_AUTH_DEFAULT_CLUSTER_PORT,\n                    full_cluster_filename, PATH_MAX)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=load_master_election_config(full_cluster_filename)) != 0) {\n        return result;\n    }\n\n    if ((result=cluster_info_init(full_cluster_filename)) != 0) {\n        return result;\n    }\n\n    sf_set_address_family_by_ip(&SERVICE_SF_CTX, &SERVICE_GROUP_ADDRESS_ARRAY(\n                CLUSTER_MYSELF_PTR->server));\n    sf_set_address_family_by_ip(&CLUSTER_SF_CTX, &CLUSTER_GROUP_ADDRESS_ARRAY(\n                CLUSTER_MYSELF_PTR->server));\n    return 0;\n}\n\nint server_load_config(const char *filename)\n{\n    const int fixed_buffer_size = 0;\n    const int task_buffer_extra_size = 0;\n    IniContext ini_context;\n    IniFullContext ini_ctx;\n    char full_cluster_filename[PATH_MAX];\n    int result;\n\n    if ((result=iniLoadFromFile(filename, &ini_context)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                __LINE__, filename, result);\n        return result;\n    }\n\n    if ((result=sf_load_config(\"fcfs_authd\", fc_comm_type_sock,\n                    filename, &ini_context, \"service\",\n                    FCFS_AUTH_DEFAULT_SERVICE_PORT,\n                    FCFS_AUTH_DEFAULT_SERVICE_PORT,\n                    fixed_buffer_size,\n                    task_buffer_extra_size)) != 0)\n    {\n        return result;\n    }\n    if ((result=sf_load_context_from_config(&CLUSTER_SF_CTX,\n                    fc_comm_type_sock, filename,\n                    &ini_context, \"cluster\",\n                    FCFS_AUTH_DEFAULT_CLUSTER_PORT,\n                    FCFS_AUTH_DEFAULT_CLUSTER_PORT,\n                    fixed_buffer_size,\n                    task_buffer_extra_size)) != 0)\n    {\n        return result;\n    }\n\n    FAST_INI_SET_FULL_CTX_EX(ini_ctx, filename, NULL, &ini_context);\n    if ((result=load_cluster_config(&ini_ctx,\n                    full_cluster_filename)) != 0)\n    {\n        return result;\n    }\n\n    ini_ctx.section_name = \"session\";\n    if ((result=server_session_init_ex(&ini_ctx,\n                    sizeof(ServerSessionFields),\n                    &g_server_session_callbacks)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=server_load_admin_generate_config(\n                    &ini_context, filename)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=server_load_pool_generate_config(\n                    &ini_context, filename)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=server_load_fdir_client_config(\n                    &ini_context, filename)) != 0)\n    {\n        return result;\n    }\n\n    POOL_USAGE_REFRESH_INTERVAL = iniGetIntValue(SECTION_NAME_FASTDIR,\n            \"pool_usage_refresh_interval\", &ini_context, 3);\n    if (POOL_USAGE_REFRESH_INTERVAL <= 0) {\n        POOL_USAGE_REFRESH_INTERVAL = 1;\n    }\n\n    if ((result=sf_load_slow_log_config(filename, &ini_context,\n                    &SLOW_LOG_CTX, &SLOW_LOG_CFG)) != 0)\n    {\n        return result;\n    }\n\n    iniFreeContext(&ini_context);\n\n    load_local_host_ip_addrs();\n    server_log_configs();\n\n    return 0;\n}\n"
  },
  {
    "path": "src/auth/server/server_func.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_AUTH_SERVER_FUNC_H\n#define _FCFS_AUTH_SERVER_FUNC_H\n\n#include \"server_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint server_load_config(const char *filename);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/server_global.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"server_global.h\"\n\nAuthServerGlobalVars g_server_global_vars;\n"
  },
  {
    "path": "src/auth/server/server_global.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _SERVER_GLOBAL_H\n#define _SERVER_GLOBAL_H\n\n#include \"fastcommon/common_define.h\"\n#include \"sf/sf_global.h\"\n#include \"../common/auth_global.h\"\n#include \"server_types.h\"\n\n#define AUTH_ADMIN_GENERATE_MODE_FIRST  'F'\n#define AUTH_ADMIN_GENERATE_MODE_ALWAYS 'A'\n\ntypedef struct server_global_vars {\n    struct {\n        FCFSAuthClusterServerInfo *master;\n        FCFSAuthClusterServerInfo *myself;\n        FCFSAuthClusterServerInfo *next_master;\n        SFClusterConfig config;\n        FCFSAuthClusterServerArray server_array;\n\n        struct {\n            SFElectionQuorum quorum;\n            bool vote_node_enabled;\n            int master_lost_timeout;\n            int max_wait_time;\n        } master_election;\n\n        SFContext sf_context;  //for cluster communication\n    } cluster;\n\n    struct {\n        int mode;\n        char *buff; //space for username and secret_key_filename\n        string_t username;\n        string_t secret_key_filename;\n    } admin_generate;\n\n    struct {\n        int64_t auto_id_initial;\n        string_t pool_name_template;\n    } pool_generate;\n\n    char *fdir_client_cfg_filename;\n    int pool_usage_refresh_interval;\n\n    SFSlowLogContext slow_log;\n} AuthServerGlobalVars;\n\n#define MASTER_ELECTION_QUORUM g_server_global_vars.cluster. \\\n    master_election.quorum\n#define VOTE_NODE_ENABLED      g_server_global_vars.cluster. \\\n    master_election.vote_node_enabled\n#define ELECTION_MASTER_LOST_TIMEOUT g_server_global_vars.cluster. \\\n    master_election.master_lost_timeout\n#define ELECTION_MAX_WAIT_TIME   g_server_global_vars.cluster. \\\n    master_election.max_wait_time\n\n#define CLUSTER_CONFIG          g_server_global_vars.cluster.config\n#define CLUSTER_SERVER_CONFIG   CLUSTER_CONFIG.server_cfg\n\n#define CLUSTER_NEXT_MASTER     g_server_global_vars.cluster.next_master\n#define CLUSTER_MYSELF_PTR      g_server_global_vars.cluster.myself\n#define CLUSTER_MASTER_PTR      g_server_global_vars.cluster.master\n#define CLUSTER_MASTER_ATOM_PTR ((FCFSAuthClusterServerInfo *)  \\\n        __sync_add_and_fetch(&CLUSTER_MASTER_PTR, 0))\n#define MYSELF_IS_MASTER        (CLUSTER_MASTER_ATOM_PTR == CLUSTER_MYSELF_PTR)\n\n#define CLUSTER_SERVER_ARRAY    g_server_global_vars.cluster.server_array\n#define CLUSTER_MY_SERVER_ID    CLUSTER_MYSELF_PTR->server->id\n\n#define SERVICE_SF_CTX          g_sf_context\n#define CLUSTER_SF_CTX          g_server_global_vars.cluster.sf_context\n\n#define CLUSTER_CONNECT_TIMEOUT   CLUSTER_SF_CTX.net_buffer_cfg.connect_timeout\n#define CLUSTER_NETWORK_TIMEOUT   CLUSTER_SF_CTX.net_buffer_cfg.network_timeout\n\n#define ADMIN_GENERATE               g_server_global_vars.admin_generate\n#define ADMIN_GENERATE_MODE          ADMIN_GENERATE.mode\n#define ADMIN_GENERATE_BUFF          ADMIN_GENERATE.buff\n#define ADMIN_GENERATE_USERNAME      ADMIN_GENERATE.username\n#define ADMIN_GENERATE_KEY_FILENAME  ADMIN_GENERATE.secret_key_filename\n\n#define POOL_GENERATE           g_server_global_vars.pool_generate\n#define AUTO_ID_INITIAL         POOL_GENERATE.auto_id_initial\n#define POOL_NAME_TEMPLATE      POOL_GENERATE.pool_name_template\n\n#define POOL_USAGE_REFRESH_INTERVAL g_server_global_vars. \\\n    pool_usage_refresh_interval\n\n#define SLOW_LOG                g_server_global_vars.slow_log\n#define SLOW_LOG_CFG            SLOW_LOG.cfg\n#define SLOW_LOG_CTX            SLOW_LOG.ctx\n\n#define CLUSTER_GROUP_INDEX     g_server_global_vars.cluster.config.cluster_group_index\n#define SERVICE_GROUP_INDEX     g_server_global_vars.cluster.config.service_group_index\n\n#define CLUSTER_GROUP_ADDRESS_ARRAY(server) \\\n    (server)->group_addrs[CLUSTER_GROUP_INDEX].address_array\n#define SERVICE_GROUP_ADDRESS_ARRAY(server) \\\n    (server)->group_addrs[SERVICE_GROUP_INDEX].address_array\n\n#define CLUSTER_GROUP_ADDRESS_FIRST_PTR(server) \\\n    (*(server)->group_addrs[CLUSTER_GROUP_INDEX].address_array.addrs)\n#define SERVICE_GROUP_ADDRESS_FIRST_PTR(server) \\\n    (*(server)->group_addrs[SERVICE_GROUP_INDEX].address_array.addrs)\n\n#define CLUSTER_GROUP_ADDRESS_FIRST_IP(server) \\\n    CLUSTER_GROUP_ADDRESS_FIRST_PTR(server)->conn.ip_addr\n#define CLUSTER_GROUP_ADDRESS_FIRST_PORT(server) \\\n    CLUSTER_GROUP_ADDRESS_FIRST_PTR(server)->conn.port\n\n#define SERVICE_GROUP_ADDRESS_FIRST_IP(server) \\\n    SERVICE_GROUP_ADDRESS_FIRST_PTR(server)->conn.ip_addr\n#define SERVICE_GROUP_ADDRESS_FIRST_PORT(server) \\\n    SERVICE_GROUP_ADDRESS_FIRST_PTR(server)->conn.port\n\n#define CLUSTER_CONFIG_SIGN_BUF g_server_global_vars.cluster.config.md5_digest\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern AuthServerGlobalVars g_server_global_vars;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/server_types.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _SERVER_TYPES_H\n#define _SERVER_TYPES_H\n\n#include \"fastcommon/uniq_skiplist.h\"\n#include \"fastcommon/fc_queue.h\"\n#include \"fastcommon/fast_mblock.h\"\n#include \"fastcommon/locked_list.h\"\n#include \"sf/sf_types.h\"\n#include \"common/auth_types.h\"\n#include \"common/server_session.h\"\n\n#define TASK_STATUS_CONTINUE           12345\n#define TASK_ARG           ((AuthServerTaskArg *)task->arg)\n#define TASK_CTX           TASK_ARG->context\n#define SESSION_ENTRY      TASK_CTX.shared.service.session\n#define SESSION_FIELDS     ((ServerSessionFields *)SESSION_ENTRY->fields)\n#define SESSION_DBUSER     SESSION_FIELDS->dbuser\n#define SESSION_DBPOOL     SESSION_FIELDS->dbpool\n#define SESSION_USER       SESSION_FIELDS->dbuser->user\n#define SESSION_SUBSCRIBER TASK_CTX.shared.cluster.subscriber\n#define REQUEST            TASK_CTX.common.request\n#define RESPONSE           TASK_CTX.common.response\n#define RESPONSE_STATUS    RESPONSE.header.status\n#define REQUEST_STATUS     REQUEST.header.status\n#define SERVER_TASK_TYPE   TASK_CTX.task_type\n#define CLUSTER_PEER       TASK_CTX.shared.cluster.peer\n\n#define AUTH_SERVER_TASK_TYPE_SESSION      1\n#define AUTH_SERVER_TASK_TYPE_SUBSCRIBE    2\n#define AUTH_SERVER_TASK_TYPE_RELATIONSHIP 3\n\n#define SERVER_CTX        ((AuthServerContext *)task->thread_data->arg)\n#define SESSION_HOLDER    SERVER_CTX->service.session_holder\n\ntypedef struct fcfs_auth_cluster_server_info {\n    FCServerInfo *server;\n    volatile bool is_online;\n} FCFSAuthClusterServerInfo;\n\ntypedef struct fcfs_auth_cluster_server_array {\n    FCFSAuthClusterServerInfo *servers;\n    int count;\n} FCFSAuthClusterServerArray;\n\nstruct db_user_info;\nstruct db_storage_pool_info;\n\ntypedef struct server_session_fields {\n    bool publish;\n    const struct db_user_info *dbuser;\n    const struct db_storage_pool_info *dbpool;\n    FCFSAuthSPoolPriviledges pool_privs;\n\n    struct fc_list_head dlink; //for publish list\n} ServerSessionFields;\n\ntypedef struct server_session_subscriber {\n    struct fc_queue queue;     //element: ServerSessionSubscribeEntry\n    struct fc_list_head dlink; //for global subscriber's chain\n    struct {\n        volatile int in_queue;   //if in the subscriber queue of NIO thread\n        struct fast_task_info *task;\n        struct fc_list_head dlink; //for nio thread subscriber's chain\n    } nio;\n} ServerSessionSubscriber;\n\ntypedef struct server_task_arg {\n    struct {\n        SFCommonTaskContext common;\n        int task_type;\n        union {\n            struct {\n                ServerSessionEntry *session;\n            } service;\n\n            union {\n                ServerSessionSubscriber *subscriber;\n                FCFSAuthClusterServerInfo *peer;   //the peer server in the cluster\n            } cluster;\n        } shared;\n\n    } context;\n} AuthServerTaskArg;\n\ntypedef struct auth_server_context {\n    void *dao_ctx;\n    union {\n        struct {\n            FCLockedList subscribers;   //element: ServerSessionSubscriber\n        } cluster;\n        struct {\n            ServerSessionEntry *session_holder;\n        } service;\n    };\n} AuthServerContext;\n\n#endif\n"
  },
  {
    "path": "src/auth/server/service_handler.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//service_handler.c\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <sys/stat.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <limits.h>\n#include <fcntl.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"fastcommon/ioevent_loop.h\"\n#include \"sf/sf_util.h\"\n#include \"sf/sf_func.h\"\n#include \"sf/sf_nio.h\"\n#include \"sf/sf_service.h\"\n#include \"sf/sf_global.h\"\n#include \"common/auth_proto.h\"\n#include \"common/auth_func.h\"\n#include \"db/dao/dao.h\"\n#include \"db/auth_db.h\"\n#include \"server_global.h\"\n#include \"server_func.h\"\n#include \"common_handler.h\"\n#include \"service_handler.h\"\n\nint service_handler_init()\n{\n    g_fcfs_auth_client_vars.need_load_passwd = false;\n    return 0;\n}\n\nint service_handler_destroy()\n{\n    return 0;\n}\n\nvoid service_task_finish_cleanup(struct fast_task_info *task)\n{\n    switch (SERVER_TASK_TYPE) {\n        case AUTH_SERVER_TASK_TYPE_SESSION:\n            if (SESSION_ENTRY != NULL) {\n                server_session_delete(SESSION_ENTRY->id_info.id);\n                SESSION_ENTRY = NULL;\n            }\n            SERVER_TASK_TYPE = SF_SERVER_TASK_TYPE_NONE;\n            break;\n        default:\n            break;\n    }\n\n    sf_task_finish_clean_up(task);\n}\n\nstatic int check_user_priv(struct fast_task_info *task)\n{\n    int64_t the_priv;\n\n    switch (REQUEST.header.cmd) {\n        case SF_PROTO_ACTIVE_TEST_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_REQ:\n            return 0;\n        case FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_USER_LIST_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_USER_GRANT_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_REQ:\n            the_priv = FCFS_AUTH_USER_PRIV_USER_MANAGE;\n            break;\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ:\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ:\n            the_priv = FCFS_AUTH_USER_PRIV_CREATE_POOL;\n            break;\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ:\n            the_priv = 0;\n            break;\n        default:\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"unkown cmd: %d\", REQUEST.header.cmd);\n            return -EINVAL;\n    }\n\n    if (SESSION_ENTRY == NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"please login first!\");\n        return EPERM;\n    }\n\n    if ((the_priv != 0) && ((SESSION_USER.priv & the_priv) == 0)) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"permission denied\");\n        return EPERM;\n    }\n\n    return 0;\n}\n\nstatic int service_deal_user_login(struct fast_task_info *task)\n{\n    FCFSAuthProtoUserLoginReq *req;\n    FCFSAuthProtoUserLoginResp *resp;\n    ServerSessionFields *fields;\n    FCFSAuthProtoNameInfo *proto_poolname;\n    string_t username;\n    string_t passwd;\n    string_t poolname;\n    int flags;\n    int result;\n\n    if ((result=server_check_min_body_length(sizeof(\n                        FCFSAuthProtoUserLoginReq) + 1)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoUserLoginReq *)REQUEST.body;\n    FC_SET_STRING_EX(username, req->up_pair.username.str,\n            req->up_pair.username.len);\n    FC_SET_STRING_EX(passwd, req->up_pair.passwd,\n            FCFS_AUTH_PASSWD_LEN);\n\n    proto_poolname = (FCFSAuthProtoNameInfo *)(req->\n            up_pair.username.str + username.len);\n    FC_SET_STRING_EX(poolname, proto_poolname->str, proto_poolname->len);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoUserLoginReq)\n                    + username.len + poolname.len)) != 0)\n    {\n        return result;\n    }\n\n    if (SESSION_ENTRY != NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"user already logined\");\n        TASK_CTX.common.log_level = LOG_NOTHING;\n        return -EEXIST;\n    }\n\n    if (SERVER_TASK_TYPE != SF_SERVER_TASK_TYPE_NONE) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"task type: %d != %d\", SERVER_TASK_TYPE,\n                SF_SERVER_TASK_TYPE_NONE);\n        return -EEXIST;\n    }\n\n    fields = (ServerSessionFields *)(SESSION_HOLDER->fields);\n    if (!((fields->dbuser=adb_user_get(SERVER_CTX, &username)) != NULL &&\n            fc_string_equal(&fields->dbuser->user.passwd, &passwd)))\n    {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"user login fail, username or password not correct\");\n        return EPERM;\n    }\n\n    if (poolname.len > 0) {\n        if ((fields->dbpool=adb_spool_global_get(&poolname)) == NULL) {\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"pool %.*s not exist\", poolname.len, poolname.str);\n            return ENOENT;\n        }\n\n        if ((result=adb_granted_privs_get(SERVER_CTX, fields->dbuser,\n                        fields->dbpool, &fields->pool_privs)) != 0)\n        {\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"pool %.*s, not priviledge\", poolname.len, poolname.str);\n            return EPERM;\n        }\n    } else {\n        fields->dbpool = NULL;\n        fields->pool_privs.fdir = 0;\n        fields->pool_privs.fstore = 0;\n    }\n\n    flags = req->flags;\n    fields->publish = (flags & FCFS_AUTH_SESSION_FLAGS_PUBLISH) != 0;\n    SESSION_HOLDER->id_info.id = 0;\n    if ((SESSION_ENTRY=server_session_add(SESSION_HOLDER,\n                    fields->publish)) == NULL)\n    {\n        return ENOMEM;\n    }\n    SERVER_TASK_TYPE = AUTH_SERVER_TASK_TYPE_SESSION;\n\n    /*\n    logInfo(\"session id: %\"PRId64\", pool: %.*s, pool_privs \"\n            \"{fdir: %d, fstore: %d}\", SESSION_ENTRY->id_info.id,\n            poolname.len, poolname.str, fields->pool_privs.fdir,\n            fields->pool_privs.fstore);\n            */\n\n    resp = (FCFSAuthProtoUserLoginResp *)SF_PROTO_SEND_BODY(task);\n    long2buff(SESSION_ENTRY->id_info.id, resp->session_id);\n    RESPONSE.header.body_len = sizeof(FCFSAuthProtoUserLoginResp);\n    RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_RESP;\n    TASK_ARG->context.common.response_done = true;\n    return 0;\n}\n\nstatic int service_deal_user_create(struct fast_task_info *task)\n{\n    FCFSAuthProtoUserCreateReq *req;\n    FCFSAuthUserInfo user;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoUserCreateReq)\n                    + 1, sizeof(FCFSAuthProtoUserCreateReq) + NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoUserCreateReq *)REQUEST.body;\n    FC_SET_STRING_EX(user.name, req->up_pair.username.str,\n            req->up_pair.username.len);\n    FC_SET_STRING_EX(user.passwd, req->up_pair.passwd,\n            FCFS_AUTH_PASSWD_LEN);\n    user.priv = buff2long(req->priv);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoUserCreateReq)\n                    + user.name.len)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fc_check_filename_ex(&user.name, \"username\",\n                    RESPONSE.error.message, &RESPONSE.error.length,\n                    sizeof(RESPONSE.error.message))) != 0)\n    {\n        return result;\n    }\n\n    user.status = FCFS_AUTH_USER_STATUS_NORMAL;\n    return adb_user_create(SERVER_CTX, &user);\n}\n\nstatic int service_deal_user_passwd(struct fast_task_info *task)\n{\n    FCFSAuthProtoUserPasswdReq *req;\n    string_t username;\n    string_t passwd;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoUserPasswdReq)\n                    + 1, sizeof(FCFSAuthProtoUserPasswdReq) + NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoUserPasswdReq *)REQUEST.body;\n    FC_SET_STRING_EX(username, req->up_pair.username.str,\n            req->up_pair.username.len);\n    FC_SET_STRING_EX(passwd, req->up_pair.passwd,\n            FCFS_AUTH_PASSWD_LEN);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoUserPasswdReq)\n                    + username.len)) != 0)\n    {\n        return result;\n    }\n\n    return adb_user_update_passwd(SERVER_CTX, &username, &passwd);\n}\n\nstatic int service_parse_limit(struct fast_task_info *task,\n        const SFProtoLimitInfo *limit_proto, SFListLimitInfo *limit_info)\n{\n    sf_proto_extract_limit(limit_proto, limit_info);\n    if (limit_info->count <= 0) {\n        limit_info->count = 1024;\n    }\n    return 0;\n}\n\nstatic int service_deal_user_list(struct fast_task_info *task)\n{\n    FCFSAuthUserArray array;\n    const DBUserInfo *dbuser;\n    const FCFSAuthUserInfo *user;\n    const FCFSAuthUserInfo *end;\n    string_t username;\n    SFListLimitInfo limit;\n    FCFSAuthProtoUserListReq *req;\n    FCFSAuthProtoListRespHeader *resp_header;\n    FCFSAuthProtoUserListRespBodyPart *body_part;\n    char *p;\n    char *buff_end;\n    bool truncated;\n    int len;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoUserListReq),\n                    sizeof(FCFSAuthProtoUserListReq) + NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoUserListReq *)REQUEST.body;\n    FC_SET_STRING_EX(username, req->username.str, req->username.len);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoUserListReq)\n                    + username.len)) != 0)\n    {\n        return result;\n    }\n\n    if (username.len > 0) {\n        if ((dbuser=adb_user_get(SERVER_CTX, &username)) == NULL) {\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"username:%.*s not exist\", username.len, username.str);\n            return ENOENT;\n        }\n\n        fcfs_auth_user_init_array(&array);\n        array.users[0] = dbuser->user;\n        array.count = 1;\n        limit.count = 100;\n    } else {\n        if ((result=service_parse_limit(task, &req->limit, &limit)) != 0) {\n            return result;\n        }\n        fcfs_auth_user_init_array(&array);\n        if ((result=adb_user_list(SERVER_CTX, &limit, &array)) != 0) {\n            fcfs_auth_user_free_array(&array);\n            return result;\n        }\n    }\n\n    resp_header = (FCFSAuthProtoListRespHeader *)SF_PROTO_SEND_BODY(task);\n    p = (char *)(resp_header + 1);\n    buff_end = SF_SEND_BUFF_END(task);\n    end = array.users + array.count;\n    truncated = false;\n    for (user=array.users; user<end; user++) {\n        len = sizeof(FCFSAuthProtoUserListRespBodyPart) + user->name.len;\n        if (p + len > buff_end) {\n            truncated = true;\n            break;\n        }\n\n        body_part = (FCFSAuthProtoUserListRespBodyPart *)p;\n        long2buff(user->priv, body_part->priv);\n        body_part->username.len = user->name.len;\n        memcpy(body_part->username.str, user->name.str, user->name.len);\n        p += len;\n    }\n    resp_header->is_last = (array.count < limit.count) && !truncated;\n    int2buff(user - array.users, resp_header->count);\n    RESPONSE.header.body_len = p - SF_PROTO_SEND_BODY(task);\n    RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_LIST_RESP;\n    TASK_ARG->context.common.response_done = true;\n\n    fcfs_auth_user_free_array(&array);\n    return 0;\n}\n\nstatic int service_deal_user_grant(struct fast_task_info *task)\n{\n    FCFSAuthProtoUserGrantReq *req;\n    string_t username;\n    int64_t priv;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoUserGrantReq)\n                    + 1, sizeof(FCFSAuthProtoUserGrantReq) + NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoUserGrantReq *)REQUEST.body;\n    FC_SET_STRING_EX(username, req->username.str, req->username.len);\n    priv = buff2long(req->priv);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoUserGrantReq)\n                    + username.len)) != 0)\n    {\n        return result;\n    }\n\n    return adb_user_update_priv(SERVER_CTX, &username, priv);\n}\n\nstatic int service_deal_user_remove(struct fast_task_info *task)\n{\n    FCFSAuthProtoUserRemoveReq *req;\n    string_t username;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoUserRemoveReq)\n                    + 1, sizeof(FCFSAuthProtoUserRemoveReq) + NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoUserRemoveReq *)REQUEST.body;\n    FC_SET_STRING_EX(username, req->username.str, req->username.len);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoUserRemoveReq)\n                    + username.len)) != 0)\n    {\n        return result;\n    }\n\n    return adb_user_remove(SERVER_CTX, &username);\n}\n\nstatic int spool_create_by_template(struct fast_task_info *task,\n        const string_t *username, const string_t *pool_name,\n        FCFSAuthStoragePoolInfo *spool, const bool dryrun)\n{\n    int result;\n    int name_size;\n    struct {\n        char buff[32];\n        string_t tag;\n        struct {\n            int64_t n;\n            string_t s;\n        } value;\n    } auto_id;\n\n    name_size = spool->name.len;\n    FC_SET_STRING_EX(auto_id.tag, FCFS_AUTH_AUTO_ID_TAG_STR,\n            FCFS_AUTH_AUTO_ID_TAG_LEN);\n    auto_id.value.n = adb_spool_get_auto_id(SERVER_CTX);\n    auto_id.value.s.str = auto_id.buff;\n    do {\n        auto_id.value.s.len = fc_itoa(auto_id.value.n, auto_id.value.s.str);\n        if ((result=str_replace(pool_name, &auto_id.tag, &auto_id.value.s,\n                        &spool->name, name_size)) != 0)\n        {\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"invalid pool name: %.*s\\n\",\n                    pool_name->len, pool_name->str);\n            return result;\n        }\n\n        if (dryrun) {\n            result = adb_spool_access(SERVER_CTX, &spool->name);\n            if (result == 0) {  //pool exist\n                result = adb_spool_next_auto_id(SERVER_CTX, &auto_id.value.n);\n                continue;\n            } else if (result == ENOENT) {\n                result = 0;\n            }\n            break;\n        }\n\n        result = adb_spool_create(SERVER_CTX, username, spool);\n        if (result == 0) {\n            result = adb_spool_inc_auto_id(SERVER_CTX);\n            break;\n        } else if (result == EEXIST) {\n            result = adb_spool_next_auto_id(SERVER_CTX, &auto_id.value.n);\n        } else {\n            break;\n        }\n    } while (result == 0);\n\n    return result;\n}\n\nstatic int service_deal_spool_create(struct fast_task_info *task)\n{\n    FCFSAuthProtoSPoolCreateReq *req;\n    FCFSAuthProtoSPoolCreateResp *resp;\n    char name_buff[NAME_MAX + 1];\n    string_t username;\n    string_t pool_name;\n    FCFSAuthStoragePoolInfo spool;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolCreateReq),\n                    sizeof(FCFSAuthProtoSPoolCreateReq) + NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoSPoolCreateReq *)REQUEST.body;\n    FC_SET_STRING_EX(spool.name, req->poolname.str, req->poolname.len);\n    spool.quota = buff2long(req->quota);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoSPoolCreateReq)\n                    + spool.name.len)) != 0)\n    {\n        return result;\n    }\n\n    if (spool.name.len == 0) {\n        spool.name = POOL_NAME_TEMPLATE;\n    } else if ((result=fc_check_filename_ex(&spool.name, \"pool name\",\n                    RESPONSE.error.message, &RESPONSE.error.length,\n                    sizeof(RESPONSE.error.message))) != 0)\n    {\n        return result;\n    }\n\n    spool.status = FCFS_AUTH_POOL_STATUS_NORMAL;\n    username = SESSION_USER.name;\n    if (spool.name.len >= FCFS_AUTH_AUTO_ID_TAG_LEN &&\n            strstr(spool.name.str, FCFS_AUTH_AUTO_ID_TAG_STR) != NULL)\n    {\n        pool_name = spool.name;\n        spool.name.str = name_buff;\n        spool.name.len = sizeof(name_buff);\n        result = spool_create_by_template(task, &username,\n                &pool_name, &spool, req->dryrun);\n    } else {\n        if (req->dryrun) {\n            result = adb_spool_access(SERVER_CTX, &spool.name);\n            if (result == 0) {\n                result = EEXIST;\n            } else if (result == ENOENT) {\n                result = 0;\n            }\n        } else {\n            result = adb_spool_create(SERVER_CTX, &username, &spool);\n        }\n    }\n\n    if (result == 0) {\n        resp = (FCFSAuthProtoSPoolCreateResp *)SF_PROTO_SEND_BODY(task);\n        resp->poolname.len = spool.name.len;\n        memcpy(resp->poolname.str, spool.name.str, spool.name.len);\n        RESPONSE.header.body_len = sizeof(*resp) + spool.name.len;\n        RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_RESP;\n        TASK_ARG->context.common.response_done = true;\n    } else if (result == EEXIST) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"pool %.*s already exist\", spool.name.len, spool.name.str);\n    }\n    return result;\n}\n\n#define SPOOL_GET(username, poolname) \\\n    do { \\\n        if ((spool=adb_spool_get(SERVER_CTX, &username, &poolname)) == NULL) { \\\n            RESPONSE.error.length = sprintf(RESPONSE.error.message, \\\n                    \"user: %.*s, pool: %.*s not exist\", username.len, \\\n                    username.str, poolname.len, poolname.str); \\\n            return ENOENT; \\\n        } \\\n    } while (0)\n\nstatic int service_deal_spool_list(struct fast_task_info *task)\n{\n    FCFSAuthStoragePoolArray array;\n    const FCFSAuthStoragePoolInfo *spool;\n    const FCFSAuthStoragePoolInfo *end;\n    string_t username;\n    string_t poolname;\n    SFListLimitInfo limit;\n    FCFSAuthProtoSPoolListReq *req;\n    FCFSAuthProtoListRespHeader *resp_header;\n    FCFSAuthProtoSPoolListRespBodyPart *body_part;\n    char *p;\n    char *buff_end;\n    bool truncated;\n    int len;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolListReq),\n                    sizeof(FCFSAuthProtoSPoolListReq) + NAME_MAX * 2)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoSPoolListReq *)REQUEST.body;\n    fcfs_auth_parse_user_pool_pair(&req->up_pair, &username, &poolname);\n    if ((result=server_expect_body_length(sizeof(FCFSAuthProtoSPoolListReq)\n                    + username.len + poolname.len)) != 0)\n    {\n        return result;\n    }\n\n    if (username.len == 0) {\n        username = SESSION_USER.name;\n    } else if (fc_string_equal(&username, &SESSION_USER.name)) {\n        //do nothing\n    } else {\n        if ((SESSION_USER.priv & FCFS_AUTH_USER_PRIV_USER_MANAGE) == 0) {\n            if (poolname.len > 0) {\n                FCFSAuthGrantedPoolFullInfo gf;\n                SPOOL_GET(username, poolname);\n                if ((result=adb_granted_full_get(SERVER_CTX, &SESSION_USER.\n                                name, spool->id, &gf)) != 0)\n                {\n                    result = EPERM;\n                }\n            } else {\n                result = EPERM;\n            }\n\n            if (result == EPERM) {\n                RESPONSE.error.length = sprintf(\n                        RESPONSE.error.message,\n                        \"permission denied\");\n                return result;\n            }\n        }\n    }\n\n    if (poolname.len > 0) {\n        SPOOL_GET(username, poolname);\n        fcfs_auth_spool_init_array(&array);\n        array.spools[0] = *spool;\n        array.count = 1;\n        limit.count = 100;\n    } else {\n        if ((result=service_parse_limit(task, &req->limit, &limit)) != 0) {\n            return result;\n        }\n\n        fcfs_auth_spool_init_array(&array);\n        if ((result=adb_spool_list(SERVER_CTX, &username,\n                        &limit, &array)) != 0)\n        {\n            fcfs_auth_spool_free_array(&array);\n            return result;\n        }\n    }\n\n    resp_header = (FCFSAuthProtoListRespHeader *)SF_PROTO_SEND_BODY(task);\n    p = (char *)(resp_header + 1);\n    end = array.spools + array.count;\n    buff_end = SF_SEND_BUFF_END(task);\n    truncated = false;\n    for (spool=array.spools; spool<end; spool++) {\n        len = sizeof(FCFSAuthProtoSPoolListRespBodyPart) +\n            spool->name.len;\n        if (p + len > buff_end) {\n            truncated = true;\n            break;\n        }\n\n        body_part = (FCFSAuthProtoSPoolListRespBodyPart *)p;\n        long2buff(spool->quota, body_part->quota);\n        body_part->poolname.len = spool->name.len;\n        memcpy(body_part->poolname.str, spool->name.str, spool->name.len);\n        p += len;\n    }\n    resp_header->is_last = (array.count < limit.count) && !truncated;\n    int2buff(spool - array.spools, resp_header->count);\n    RESPONSE.header.body_len = p - SF_PROTO_SEND_BODY(task);\n    RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_RESP;\n    TASK_ARG->context.common.response_done = true;\n\n    fcfs_auth_spool_free_array(&array);\n    return 0;\n}\n\nstatic int service_deal_spool_remove(struct fast_task_info *task)\n{\n    FCFSAuthProtoSPoolRemoveReq *req;\n    string_t username;\n    string_t poolname;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolRemoveReq)\n                    + 1, sizeof(FCFSAuthProtoSPoolRemoveReq) + NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoSPoolRemoveReq *)REQUEST.body;\n    FC_SET_STRING_EX(poolname, req->poolname.str, req->poolname.len);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoSPoolRemoveReq)\n                    + poolname.len)) != 0)\n    {\n        return result;\n    }\n\n    username = SESSION_USER.name;\n    return adb_spool_remove(SERVER_CTX, &username, &poolname);\n}\n\nstatic int service_deal_spool_set_quota(struct fast_task_info *task)\n{\n    FCFSAuthProtoSPoolSetQuotaReq *req;\n    string_t username;\n    string_t poolname;\n    int64_t quota;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolSetQuotaReq)\n                    + 1, sizeof(FCFSAuthProtoSPoolSetQuotaReq) +\n                    NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoSPoolSetQuotaReq *)REQUEST.body;\n    FC_SET_STRING_EX(poolname, req->poolname.str, req->poolname.len);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoSPoolSetQuotaReq)\n                    + poolname.len)) != 0)\n    {\n        return result;\n    }\n\n    quota = buff2long(req->quota);\n    username = SESSION_USER.name;\n    return adb_spool_set_quota(SERVER_CTX, &username, &poolname, quota);\n}\n\nstatic int service_deal_spool_get_quota(struct fast_task_info *task)\n{\n    FCFSAuthProtoSPoolGetQuotaReq *req;\n    FCFSAuthProtoSPoolGetQuotaResp *resp;\n    string_t poolname;\n    int64_t quota;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolGetQuotaReq)\n                    + 1, sizeof(FCFSAuthProtoSPoolGetQuotaReq)\n                    + NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoSPoolGetQuotaReq *)REQUEST.body;\n    FC_SET_STRING_EX(poolname, req->poolname.str, req->poolname.len);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoSPoolGetQuotaReq)\n                    + poolname.len)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=adb_spool_get_quota(SERVER_CTX, &poolname, &quota)) == 0) {\n        resp = (FCFSAuthProtoSPoolGetQuotaResp *)SF_PROTO_SEND_BODY(task);\n        long2buff(quota, resp->quota);\n        RESPONSE.header.body_len = sizeof(*resp);\n        TASK_ARG->context.common.response_done = true;\n    }\n\n    return result;\n}\n\nstatic int service_deal_spool_grant(struct fast_task_info *task)\n{\n    FCFSAuthProtoSPoolGrantReq *req;\n    struct {\n        string_t my;\n        string_t dest;\n    } usernames;\n    string_t poolname;\n    const FCFSAuthStoragePoolInfo *spool;\n    FCFSAuthGrantedPoolInfo granted;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolGrantReq)\n                    + 2, sizeof(FCFSAuthProtoSPoolGrantReq)\n                    + 2 * NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoSPoolGrantReq *)REQUEST.body;\n    fcfs_auth_parse_user_pool_pair(&req->up_pair, &usernames.dest, &poolname);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoSPoolGrantReq)\n                    + usernames.dest.len + poolname.len)) != 0)\n    {\n        return result;\n    }\n\n    usernames.my = SESSION_USER.name;\n    if ((spool=adb_spool_get(SERVER_CTX, &usernames.my, &poolname)) == NULL) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"user: %.*s, pool: %.*s not exist\", usernames.my.len,\n                usernames.my.str, poolname.len, poolname.str);\n        return ENOENT;\n    }\n\n    granted.pool_id = spool->id;\n    granted.privs.fdir = buff2int(req->privs.fdir);\n    granted.privs.fstore = buff2int(req->privs.fstore);\n    if ((granted.privs.fdir == FCFS_AUTH_POOL_ACCESS_NONE) &&\n            (granted.privs.fstore == FCFS_AUTH_POOL_ACCESS_NONE))\n    {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"no access priviledges to grant\");\n        return EINVAL;\n    }\n\n    /*\n    logInfo(\"sizeof(FCFSAuthProtoSPoolGrantReq): %d, dest: %.*s(%d), \"\n            \"poolname: %.*s, pool_id: %\"PRId64,\n            (int)sizeof(FCFSAuthProtoSPoolGrantReq),\n            usernames.dest.len, usernames.dest.str, usernames.dest.len,\n            poolname.len, poolname.str, granted.pool_id);\n            */\n\n    return adb_granted_create(SERVER_CTX, &usernames.dest, &granted);\n}\n\nstatic int service_deal_spool_withdraw(struct fast_task_info *task)\n{\n    FCFSAuthProtoSPoolWithdrawReq *req;\n    struct {\n        string_t my;\n        string_t dest;\n    } usernames;\n    string_t poolname;\n    const FCFSAuthStoragePoolInfo *spool;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoSPoolWithdrawReq)\n                    + 2, sizeof(FCFSAuthProtoSPoolWithdrawReq)\n                    + 2 * NAME_MAX)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoSPoolWithdrawReq *)REQUEST.body;\n    fcfs_auth_parse_user_pool_pair(&req->up_pair, &usernames.dest, &poolname);\n    if ((result=server_expect_body_length(\n                    sizeof(FCFSAuthProtoSPoolWithdrawReq)\n                    + usernames.dest.len + poolname.len)) != 0)\n    {\n        return result;\n    }\n\n    usernames.my = SESSION_USER.name;\n    if ((spool=adb_spool_get(SERVER_CTX, &usernames.my, &poolname)) == NULL) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"user: %.*s, pool: %.*s not exist\", usernames.my.len,\n                usernames.my.str, poolname.len, poolname.str);\n        return ENOENT;\n    }\n\n    return adb_granted_remove(SERVER_CTX, &usernames.dest, spool->id);\n}\n\nstatic int service_deal_gpool_list(struct fast_task_info *task)\n{\n    FCFSAuthGrantedPoolArray array;\n    const FCFSAuthGrantedPoolFullInfo *gpool;\n    const FCFSAuthGrantedPoolFullInfo *end;\n    char pname_buff[NAME_MAX];\n    string_t username;\n    string_t poolname;\n    SFListLimitInfo limit;\n    FCFSAuthProtoGPoolListReq *req;\n    FCFSAuthProtoListRespHeader *resp_header;\n    FCFSAuthProtoGPoolListRespBodyPart *body_part;\n    char *p;\n    char *buff_end;\n    bool truncated;\n    int count;\n    int len;\n    int result;\n\n    if ((result=server_check_body_length(sizeof(FCFSAuthProtoGPoolListReq),\n                    sizeof(FCFSAuthProtoGPoolListReq) + NAME_MAX * 2)) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSAuthProtoGPoolListReq *)REQUEST.body;\n    fcfs_auth_parse_user_pool_pair(&req->up_pair, &username, &poolname);\n    if ((result=server_expect_body_length(sizeof(FCFSAuthProtoGPoolListReq)\n                    + username.len + poolname.len)) != 0)\n    {\n        return result;\n    }\n\n    if (username.len == 0) {\n        username = SESSION_USER.name;\n    } else {\n        if ((SESSION_USER.priv & FCFS_AUTH_USER_PRIV_USER_MANAGE) == 0) {\n            RESPONSE.error.length = sprintf(\n                    RESPONSE.error.message,\n                    \"permission denied\");\n            return EPERM;\n        }\n    }\n\n    if (poolname.len > 0) {\n        memcpy(pname_buff, poolname.str, poolname.len);\n        poolname.str = pname_buff;\n        limit.offset = 0;\n        limit.count = 1000 * 1000;  //to list all\n    } else {\n        if ((result=service_parse_limit(task, &req->limit, &limit)) != 0) {\n            return result;\n        }\n    }\n\n    fcfs_auth_granted_init_array(&array);\n    if ((result=adb_granted_list(SERVER_CTX, &username,\n                    &limit, &array)) != 0)\n    {\n        fcfs_auth_granted_free_array(&array);\n        return result;\n    }\n\n    count = 0;\n    resp_header = (FCFSAuthProtoListRespHeader *)SF_PROTO_SEND_BODY(task);\n    p = (char *)(resp_header + 1);\n    buff_end = SF_SEND_BUFF_END(task);\n    end = array.gpools + array.count;\n    truncated = false;\n    for (gpool=array.gpools; gpool<end; gpool++) {\n        if (poolname.len == 0 || fc_string_equals(\n                    &gpool->pool_name, &poolname))\n        {\n            len = sizeof(FCFSAuthProtoGPoolListRespBodyPart) +\n                gpool->username.len + gpool->pool_name.len;\n            if (p + len > buff_end) {\n                truncated = true;\n                break;\n            }\n\n            body_part = (FCFSAuthProtoGPoolListRespBodyPart *)p;\n            int2buff(gpool->granted.privs.fdir, body_part->privs.fdir);\n            int2buff(gpool->granted.privs.fstore, body_part->privs.fstore);\n            fcfs_auth_pack_user_pool_pair(&gpool->username,\n                    &gpool->pool_name, &body_part->up_pair);\n            p += len;\n            count++;\n        }\n    }\n\n    resp_header->is_last = (array.count < limit.count) && !truncated;\n    int2buff(count, resp_header->count);\n    RESPONSE.header.body_len = p - SF_PROTO_SEND_BODY(task);\n    RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_RESP;\n    TASK_ARG->context.common.response_done = true;\n\n    fcfs_auth_granted_free_array(&array);\n    return 0;\n}\n\nstatic int service_deal_cluster_stat(struct fast_task_info *task)\n{\n    int result;\n    FCFSAuthProtoClusterStatRespBodyHeader *body_header;\n    FCFSAuthProtoClusterStatRespBodyPart *body_part;\n    FCFSAuthClusterServerInfo *cs;\n    FCFSAuthClusterServerInfo *send;\n\n    if ((result=server_expect_body_length(0)) != 0) {\n        return result;\n    }\n\n    body_header = (FCFSAuthProtoClusterStatRespBodyHeader *)\n        SF_PROTO_SEND_BODY(task);\n    body_part = (FCFSAuthProtoClusterStatRespBodyPart *)(body_header + 1);\n    int2buff(CLUSTER_SERVER_ARRAY.count, body_header->count);\n\n    send = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count;\n    for (cs=CLUSTER_SERVER_ARRAY.servers; cs<send; cs++, body_part++) {\n        int2buff(cs->server->id, body_part->server_id);\n        body_part->is_online = ((cs == CLUSTER_MASTER_ATOM_PTR ||\n                    cs->is_online) ? 1 : 0);\n        body_part->is_master = (cs == CLUSTER_MASTER_ATOM_PTR ? 1 : 0);\n        fc_safe_strcpy(body_part->ip_addr, SERVICE_GROUP_ADDRESS_FIRST_IP(\n                    cs->server));\n        short2buff(SERVICE_GROUP_ADDRESS_FIRST_PORT(cs->server),\n                body_part->port);\n    }\n\n    RESPONSE.header.body_len = (char *)body_part - SF_PROTO_SEND_BODY(task);\n    RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_RESP;\n    TASK_CTX.common.response_done = true;\n    return 0;\n}\n\nstatic int service_process(struct fast_task_info *task)\n{\n    int result;\n\n    if (!MYSELF_IS_MASTER) {\n        if (!(REQUEST.header.cmd == FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ ||\n                    REQUEST.header.cmd == SF_SERVICE_PROTO_GET_LEADER_REQ ||\n                    REQUEST.header.cmd == SF_PROTO_ACTIVE_TEST_REQ))\n        {\n            RESPONSE.error.length = sprintf(\n                    RESPONSE.error.message,\n                    \"i am not master\");\n            return SF_RETRIABLE_ERROR_NOT_MASTER;\n        }\n    }\n\n    switch (REQUEST.header.cmd) {\n        case SF_PROTO_ACTIVE_TEST_REQ:\n            RESPONSE.header.cmd = SF_PROTO_ACTIVE_TEST_RESP;\n            return sf_proto_deal_active_test(task, &REQUEST, &RESPONSE);\n        case FCFS_AUTH_SERVICE_PROTO_USER_LOGIN_REQ:\n            return service_deal_user_login(task);\n        case FCFS_AUTH_SERVICE_PROTO_USER_CREATE_REQ:\n            RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_CREATE_RESP;\n            return service_deal_user_create(task);\n        case FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_REQ:\n            RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_PASSWD_RESP;\n            return service_deal_user_passwd(task);\n        case FCFS_AUTH_SERVICE_PROTO_USER_LIST_REQ:\n            return service_deal_user_list(task);\n        case FCFS_AUTH_SERVICE_PROTO_USER_GRANT_REQ:\n            RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_GRANT_RESP;\n            return service_deal_user_grant(task);\n        case FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_REQ:\n            RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_USER_REMOVE_RESP;\n            return service_deal_user_remove(task);\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_CREATE_REQ:\n            return service_deal_spool_create(task);\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_LIST_REQ:\n            return service_deal_spool_list(task);\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_REQ:\n            RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SPOOL_REMOVE_RESP;\n            return service_deal_spool_remove(task);\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_REQ:\n            RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SPOOL_SET_QUOTA_RESP;\n            return service_deal_spool_set_quota(task);\n        case FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_REQ:\n            RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_SPOOL_GET_QUOTA_RESP;\n            return service_deal_spool_get_quota(task);\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_REQ:\n            RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_GPOOL_GRANT_RESP;\n            return service_deal_spool_grant(task);\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_REQ:\n            RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_GPOOL_WITHDRAW_RESP;\n            return service_deal_spool_withdraw(task);\n        case FCFS_AUTH_SERVICE_PROTO_GPOOL_LIST_REQ:\n            return service_deal_gpool_list(task);\n        case FCFS_AUTH_SERVICE_PROTO_CLUSTER_STAT_REQ:\n            return service_deal_cluster_stat(task);\n        case FCFS_AUTH_SERVICE_PROTO_GET_MASTER_REQ:\n            if ((result=fcfs_auth_deal_get_master(task,\n                            SERVICE_GROUP_INDEX)) == 0)\n            {\n                RESPONSE.header.cmd = FCFS_AUTH_SERVICE_PROTO_GET_MASTER_RESP;\n            }\n            return result;\n        case SF_SERVICE_PROTO_GET_LEADER_REQ:\n            if ((result=fcfs_auth_deal_get_master(task,\n                            SERVICE_GROUP_INDEX)) == 0)\n            {\n                RESPONSE.header.cmd = SF_SERVICE_PROTO_GET_LEADER_RESP;\n            }\n            return result;\n        default:\n            RESPONSE.error.length = sprintf(\n                    RESPONSE.error.message,\n                    \"unkown cmd: %d\", REQUEST.header.cmd);\n            return -EINVAL;\n    }\n}\n\nint service_deal_task(struct fast_task_info *task, const int stage)\n{\n    int result;\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, \"\n            \"task: %p, sock: %d, nio stage: %d, continue: %d, \"\n            \"cmd: %d (%s)\", __LINE__, task, task->event.fd, stage,\n            stage == SF_NIO_STAGE_CONTINUE,\n            ((FCFSAuthProtoHeader *)task->recv.ptr->data)->cmd,\n            fdir_get_cmd_caption(((FCFSAuthProtoHeader *)\n            task->recv.ptr->data)->cmd));\n            */\n\n    if (stage == SF_NIO_STAGE_CONTINUE) {\n        if (task->continue_callback != NULL) {\n            result = task->continue_callback(task);\n        } else {\n            result = RESPONSE_STATUS;\n            if (result == TASK_STATUS_CONTINUE) {\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"unexpect status: %d\", __LINE__, result);\n                result = EBUSY;\n            }\n        }\n    } else {\n        sf_proto_init_task_context(task, &TASK_CTX.common);\n        if ((result=check_user_priv(task)) == 0) {\n            result = service_process(task);\n        }\n    }\n\n    if (result == TASK_STATUS_CONTINUE) {\n        return 0;\n    } else {\n        RESPONSE_STATUS = result;\n        return sf_proto_deal_task_done(task, \"service\", &TASK_CTX.common);\n    }\n}\n\nstatic int create_session_for_access_fdir(ServerSessionEntry\n        *session_holder, char *session_id)\n{\n    const bool persistent = true;\n    ServerSessionFields *fields;\n    ServerSessionEntry *session;\n\n    fields = (ServerSessionFields *)(session_holder->fields);\n    fields->publish = false;\n    fields->pool_privs.fdir = FCFS_AUTH_POOL_ACCESS_ALL;\n    fields->pool_privs.fstore = FCFS_AUTH_POOL_ACCESS_ALL;\n    session_holder->id_info.id = 0;\n    if ((session=server_session_add_ex(session_holder,\n                    fields->publish, persistent)) == NULL)\n    {\n        return ENOMEM;\n    }\n\n    long2buff(session->id_info.id, session_id);\n    return 0;\n}\n\nvoid *service_alloc_thread_extra_data(const int thread_index)\n{\n    short alloc_size;\n    short dao_context_size;\n    static bool dao_session_inited = false;\n    static char dao_session_id[FCFS_AUTH_SESSION_ID_LEN];\n    AuthServerContext *server_context;\n\n    dao_context_size = dao_get_context_size();\n    alloc_size = sizeof(AuthServerContext) + dao_context_size +\n        sizeof(ServerSessionEntry) + sizeof(ServerSessionFields);\n    server_context = (AuthServerContext *)fc_malloc(alloc_size);\n    if (server_context == NULL) {\n        return NULL;\n    }\n\n    memset(server_context, 0, alloc_size);\n    server_context->dao_ctx = (void *)(server_context + 1);\n    server_context->service.session_holder = (ServerSessionEntry *)(((char *)\n                server_context->dao_ctx) + dao_context_size);\n    server_context->service.session_holder->fields =\n        server_context->service.session_holder + 1;\n    if (!dao_session_inited) {\n        dao_session_inited = true;\n        if (create_session_for_access_fdir(server_context->\n                    service.session_holder, dao_session_id) != 0)\n        {\n            return NULL;\n        }\n    }\n\n    if (dao_init_context(thread_index, server_context->\n                dao_ctx, dao_session_id) != 0)\n    {\n        sf_terminate_myself();\n        return NULL;\n    }\n\n    return server_context;\n}\n"
  },
  {
    "path": "src/auth/server/service_handler.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//service_handler.h\n\n#ifndef FCFS_AUTH_SERVER_HANDLER_H\n#define FCFS_AUTH_SERVER_HANDLER_H\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"fastcommon/fast_task_queue.h\"\n#include \"server_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint service_handler_init();\nint service_handler_destroy();\nint service_deal_task(struct fast_task_info *task, const int stage);\nvoid service_task_finish_cleanup(struct fast_task_info *task);\nvoid *service_alloc_thread_extra_data(const int thread_index);\n//int service_thread_loop(struct nio_thread_data *thread_data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/auth/server/session_subscribe.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <limits.h>\n#include <sys/stat.h>\n#include <sys/xattr.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/locked_list.h\"\n#include \"sf/sf_global.h\"\n#include \"sf/sf_service.h\"\n#include \"db/auth_db.h\"\n#include \"server_global.h\"\n#include \"cluster_handler.h\"\n#include \"server_session.h\"\n#include \"session_subscribe.h\"\n\ntypedef struct server_session_subscribe_context {\n    struct fast_mblock_man entry_allocator; //element: ServerSessionSubscribeEntry\n    struct fast_mblock_man subs_allocator;  //element: ServerSessionSubscriber\n\n    /* queue element: ServerSessionSubscriber */\n    FCLockedList subscribers;\n\n    /* queue element: ServerSessionFields */\n    FCLockedList sessions;   //publish session only\n} ServerSessionSubscribeContext;\n\nstatic ServerSessionSubscribeContext subscribe_ctx;\n\nstatic void set_session_subscribe_entry(const ServerSessionEntry *session,\n        ServerSessionSubscribeEntry *subs_entry)\n{\n    ServerSessionFields *fields;\n\n    subs_entry->operation = FCFS_AUTH_SESSION_OP_TYPE_CREATE;\n    subs_entry->session_id = session->id_info.id;\n\n    fields = (ServerSessionFields *)(session->fields);\n    subs_entry->fields.user.id = fields->dbuser->user.id;\n    subs_entry->fields.user.priv = fields->dbuser->user.priv;\n\n    if (fields->dbpool != NULL) {\n        subs_entry->fields.pool.id = fields->dbpool->pool.id;\n        subs_entry->fields.pool.available = FCFS_AUTH_POOL_AVAILABLE(\n                fields->dbpool->pool);\n        subs_entry->fields.pool.privs = fields->pool_privs;\n    } else {\n        subs_entry->fields.pool.id = 0;\n        subs_entry->fields.pool.available = 0;\n        subs_entry->fields.pool.privs.fdir = 0;\n        subs_entry->fields.pool.privs.fstore = 0;\n    }\n}\n\nstatic int publish_entry_to_all_subscribers(\n        const ServerSessionSubscribeEntry *src_entry)\n{\n    int result;\n    bool notify;\n    ServerSessionSubscriber *subscriber;\n    ServerSessionSubscribeEntry *subs_entry;\n\n    result = 0;\n    PTHREAD_MUTEX_LOCK(&subscribe_ctx.subscribers.lock);\n    fc_list_for_each_entry(subscriber, &subscribe_ctx.\n            subscribers.head, dlink)\n    {\n        subs_entry = (ServerSessionSubscribeEntry *)\n            fast_mblock_alloc_object(&subscribe_ctx.entry_allocator);\n        if (subs_entry == NULL) {\n            result = ENOMEM;\n            break;\n        }\n\n        *subs_entry = *src_entry;\n        fc_queue_push_ex(&subscriber->queue, subs_entry, &notify);\n        if (notify) {\n            cluster_subscriber_queue_push(subscriber);\n        }\n    }\n    PTHREAD_MUTEX_UNLOCK(&subscribe_ctx.subscribers.lock);\n\n    return result;\n}\n\nstatic inline int publish_session_to_all_subscribers(\n        const ServerSessionEntry *session)\n{\n    ServerSessionSubscribeEntry subs_entry;\n\n    set_session_subscribe_entry(session, &subs_entry);\n    return publish_entry_to_all_subscribers(&subs_entry);\n}\n\nstatic void server_session_add_callback(ServerSessionEntry *session)\n{\n    ServerSessionFields *fields;\n\n    fields = (ServerSessionFields *)(session->fields);\n    if (fields->publish) {\n        locked_list_add_tail(&fields->dlink, &subscribe_ctx.sessions);\n        publish_session_to_all_subscribers(session);\n    }\n}\n\nstatic void server_session_del_callback(ServerSessionEntry *session)\n{\n    ServerSessionFields *fields;\n    ServerSessionSubscribeEntry subs_entry;\n\n    fields = (ServerSessionFields *)(session->fields);\n    if (fields->publish && MYSELF_IS_MASTER) {\n        locked_list_del(&fields->dlink, &subscribe_ctx.sessions);\n\n        memset(&subs_entry, 0, sizeof(subs_entry));\n        subs_entry.operation = FCFS_AUTH_SESSION_OP_TYPE_REMOVE;\n        subs_entry.session_id = session->id_info.id;\n        publish_entry_to_all_subscribers(&subs_entry);\n    }\n}\n\nServerSessionCallbacks g_server_session_callbacks = {\n    server_session_add_callback, server_session_del_callback\n};\n\ntypedef struct {\n    int64_t user_id;\n    int64_t pool_id;\n    const FCFSAuthSPoolPriviledges *pool_privs;\n} ServerSessionMatchParam;\n\nstatic void publish_matched_server_sessions(\n        const ServerSessionMatchParam *mparam)\n{\n    ServerSessionFields *fields;\n    ServerSessionEntry *session;\n    int matched_count;\n\n    if (fc_list_empty(&subscribe_ctx.subscribers.head)) {\n        return;\n    }\n\n    matched_count = 0;\n    PTHREAD_MUTEX_LOCK(&subscribe_ctx.sessions.lock);\n    fc_list_for_each_entry(fields, &subscribe_ctx.sessions.head, dlink) {\n        session = FCFS_AUTH_SERVER_SESSION_BY_FIELDS(fields);\n        if (mparam->user_id != 0) {\n            if (fields->dbuser->user.id != mparam->user_id) {\n                continue;\n            }\n        }\n\n        if (mparam->pool_id != 0) {\n            if (!(fields->dbpool != NULL && fields->dbpool->\n                        pool.id == mparam->pool_id))\n            {\n                continue;\n            }\n        }\n\n        if (mparam->pool_privs != NULL) {\n            fields->pool_privs = *mparam->pool_privs;\n        }\n\n        ++matched_count;\n        publish_session_to_all_subscribers(session);\n    }\n    PTHREAD_MUTEX_UNLOCK(&subscribe_ctx.sessions.lock);\n\n    if (matched_count > 0) {\n    }\n}\n\nstatic void user_priv_change_callback(const int64_t user_id,\n        const int64_t new_priv)\n{\n    ServerSessionMatchParam mparam;\n\n    mparam.user_id = user_id;\n    mparam.pool_id = 0;\n    mparam.pool_privs = NULL;\n    publish_matched_server_sessions(&mparam);\n}\n\nstatic void pool_priv_change_callback(const int64_t user_id,\n    const int64_t pool_id, const FCFSAuthSPoolPriviledges *pool_privs)\n{\n    ServerSessionMatchParam mparam;\n\n    mparam.user_id = user_id;\n    mparam.pool_id = pool_id;\n    mparam.pool_privs = pool_privs;\n    publish_matched_server_sessions(&mparam);\n}\n\nstatic void pool_quota_avail_change_callback(\n        const int64_t pool_id, const bool available)\n{\n    ServerSessionMatchParam mparam;\n\n    mparam.user_id = 0;\n    mparam.pool_id = pool_id;\n    mparam.pool_privs = NULL;\n    publish_matched_server_sessions(&mparam);\n}\n\nDBPrivChangeCallbacks g_db_priv_change_callbacks = {\n    user_priv_change_callback,\n    pool_priv_change_callback,\n    pool_quota_avail_change_callback\n};\n\nstatic int push_all_sessions_to_queue(ServerSessionSubscriber *subscriber)\n{\n    int result;\n    ServerSessionFields *fields;\n    ServerSessionEntry *session;\n    ServerSessionSubscribeEntry *head;\n    ServerSessionSubscribeEntry *tail;\n    ServerSessionSubscribeEntry *subs_entry;\n\n    result = 0;\n    head = tail = NULL;\n    PTHREAD_MUTEX_LOCK(&subscribe_ctx.sessions.lock);\n    fc_list_for_each_entry(fields, &subscribe_ctx.sessions.head, dlink) {\n        session = FCFS_AUTH_SERVER_SESSION_BY_FIELDS(fields);\n        if (!session->id_info.fields.publish) {\n            continue;\n        }\n\n        subs_entry = (ServerSessionSubscribeEntry *)\n            fast_mblock_alloc_object(&subscribe_ctx.entry_allocator);\n        if (subs_entry == NULL) {\n            result = ENOMEM;\n            break;\n        }\n\n        set_session_subscribe_entry(session, subs_entry);\n\n        if (head == NULL) {\n            head = subs_entry;\n        } else {\n            tail->next = subs_entry;\n        }\n        tail = subs_entry;\n    }\n    PTHREAD_MUTEX_UNLOCK(&subscribe_ctx.sessions.lock);\n\n    if (result == 0 && head != NULL) {\n        struct fc_queue_info qinfo;\n\n        tail->next = NULL;\n        qinfo.head = head;\n        qinfo.tail = tail;\n        fc_queue_push_queue_to_tail_silence(&subscriber->queue, &qinfo);\n    }\n    return result;\n}\n\nint subscriber_alloc_init_func(ServerSessionSubscriber *subscriber, void *args)\n{\n    FC_INIT_LIST_HEAD(&subscriber->dlink);\n    return fc_queue_init(&subscriber->queue, (long)\n            (&((ServerSessionSubscribeEntry *)NULL)->next));\n}\n\nint session_subscribe_init()\n{\n    int result;\n\n    if ((result=fast_mblock_init_ex1(&subscribe_ctx.entry_allocator,\n                    \"subscribe_entry\", sizeof(ServerSessionSubscribeEntry),\n                    8 * 1024, 0, NULL, NULL, true)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fast_mblock_init_ex1(&subscribe_ctx.subs_allocator,\n                    \"session_subscriber\", sizeof(ServerSessionSubscriber),\n                    1024, 0, (fast_mblock_object_init_func)\n                    subscriber_alloc_init_func, NULL, true)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=locked_list_init(&subscribe_ctx.subscribers)) != 0) {\n        return result;\n    }\n\n    if ((result=locked_list_init(&subscribe_ctx.sessions)) != 0) {\n        return result;\n    }\n\n    return 0;\n}\n\nvoid session_subscribe_destroy()\n{\n}\n\nServerSessionSubscriber *session_subscribe_alloc()\n{\n    return (ServerSessionSubscriber *)fast_mblock_alloc_object(\n            &subscribe_ctx.subs_allocator);\n}\n\nvoid session_subscribe_register(ServerSessionSubscriber *subscriber)\n{\n    push_all_sessions_to_queue(subscriber);\n    locked_list_add_tail(&subscriber->dlink,\n            &subscribe_ctx.subscribers);\n}\n\nvoid session_subscribe_unregister(ServerSessionSubscriber *subscriber)\n{\n    locked_list_del(&subscriber->dlink, &subscribe_ctx.subscribers);\n}\n\nvoid session_subscribe_free_entries(ServerSessionSubscribeEntry *entry)\n{\n    ServerSessionSubscribeEntry *current;\n    while (entry != NULL) {\n        current = entry;\n        entry = entry->next;\n\n        fast_mblock_free_object(&subscribe_ctx.entry_allocator, current);\n    }\n}\n\nvoid session_subscribe_release(ServerSessionSubscriber *subscriber)\n{\n    ServerSessionSubscribeEntry *entry;\n\n    entry = (ServerSessionSubscribeEntry *)fc_queue_try_pop_all(\n            &subscriber->queue);\n    if (entry != NULL) {\n        session_subscribe_free_entries(entry);\n    }\n    fast_mblock_free_object(&subscribe_ctx.subs_allocator, subscriber);\n}\n\nvoid session_subscribe_clear_session()\n{\n    ServerSessionFields *fields;\n    ServerSessionFields *tmp;\n\n    PTHREAD_MUTEX_LOCK(&subscribe_ctx.sessions.lock);\n    fc_list_for_each_entry_safe(fields, tmp,\n            &subscribe_ctx.sessions.head, dlink)\n    {\n        fc_list_del_init(&fields->dlink);\n    }\n    PTHREAD_MUTEX_UNLOCK(&subscribe_ctx.sessions.lock);\n}\n"
  },
  {
    "path": "src/auth/server/session_subscribe.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _AUTH_SESSION_SUBSCRIBE_H\n#define _AUTH_SESSION_SUBSCRIBE_H\n\n#include \"fastcommon/fast_mblock.h\"\n#include \"server_types.h\"\n\ntypedef struct auth_session_subscribe_entry {\n    char operation;\n    uint64_t session_id;\n    SessionSyncedFields fields;\n    struct auth_session_subscribe_entry *next;  //for fc_queue\n} ServerSessionSubscribeEntry;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern ServerSessionCallbacks g_server_session_callbacks;\n\n    int session_subscribe_init();\n    void session_subscribe_destroy();\n\n    void session_subscribe_free_entries(ServerSessionSubscribeEntry *entry);\n\n    ServerSessionSubscriber *session_subscribe_alloc();\n\n    void session_subscribe_register(ServerSessionSubscriber *subscriber);\n\n    void session_subscribe_unregister(ServerSessionSubscriber *subscriber);\n\n    void session_subscribe_release(ServerSessionSubscriber *subscriber);\n\n    void session_subscribe_clear_session();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/common/fcfs_global.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"fcfs_global.h\"\n\nFCFSGlobalVars g_fcfs_global_vars = {\n    {5, 5, 0}\n};\n"
  },
  {
    "path": "src/common/fcfs_global.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_GLOBAL_H\n#define _FCFS_GLOBAL_H\n\n#include \"fastcommon/common_define.h\"\n\ntypedef struct fcfs_global_vars {\n    Version version;\n} FCFSGlobalVars;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern FCFSGlobalVars g_fcfs_global_vars;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/fuse/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I.. -I../common -I../include\nLIB_PATH = -L../api $(LIBS) -lfuse3 -lfcfsapi -lfdirclient -lfsapi  \\\n\t\t   -lfsclient -lfcfsauthclient -lfastcommon -lserverframe\nTARGET_PATH = $(TARGET_PREFIX)/bin\n\nSTATIC_OBJS = global.o getgroups.o groups_htable.o fuse_wrapper.o\n\nALL_PRGS = fcfs_fused\n\nall: $(STATIC_OBJS) $(ALL_PRGS)\n\n$(ALL_PRGS): $(STATIC_OBJS)\n.o:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n\ninstall:\n\tmkdir -p $(TARGET_PATH)\n\tcp -f $(ALL_PRGS) $(TARGET_PATH)\n\nclean:\n\trm -f $(STATIC_OBJS) $(ALL_PRGS)\n\n"
  },
  {
    "path": "src/fuse/fcfs_fused.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <getopt.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/process_ctrl.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"sf/sf_global.h\"\n#include \"sf/sf_service.h\"\n#include \"sf/sf_func.h\"\n#include \"sf/sf_util.h\"\n#include \"sf/idempotency/client/client_channel.h\"\n#include \"sf/idempotency/client/receipt_handler.h\"\n#include \"global.h\"\n#include \"fuse_wrapper.h\"\n\n//#define FDIR_MBLOCK_CHECK  1\n\n#ifdef FDIR_MBLOCK_CHECK\nstatic int setup_mblock_stat_task();\n#endif\n\nstatic struct fuse_session *fuse_instance;\n\nstatic int setup_server_env(const char *config_filename);\nstatic struct fuse_session *create_fuse_session(char *argv0,\n        struct fuse_lowlevel_ops *ops);\nstatic void fuse_exit_handler(int sig);\n\nstatic bool daemon_mode = true;\nstatic bool need_delete_pid_file = false;\nstatic const char *config_filename;\nstatic char g_pid_filename[MAX_PATH_SIZE];\n\nstatic int parse_cmd_options(int argc, char *argv[])\n{\n    int ch;\n    const struct option longopts[] = {\n        {\"user\", required_argument, NULL, 'u'},\n        {\"key\",  required_argument, NULL, 'k'},\n        {\"namespace\",  required_argument, NULL, 'n'},\n        {\"mountpoint\", required_argument, NULL, 'm'},\n        {\"base-path\",  required_argument, NULL, 'b'},\n        SF_COMMON_LONG_OPTIONS,\n        {NULL, 0, NULL, 0}\n    };\n\n    while ((ch = getopt_long(argc, argv, SF_COMMON_OPT_STRING\"u:k:n:m:b:\",\n                    longopts, NULL)) != -1)\n    {\n        switch (ch) {\n            case 'u':\n                FC_SET_STRING(g_fcfs_auth_client_vars.client_ctx.\n                        auth_cfg.username, optarg);\n                break;\n            case 'k':\n                FC_SET_STRING(g_fcfs_auth_client_vars.client_ctx.\n                        auth_cfg.secret_key_filename, optarg);\n                break;\n            case 'n':\n                g_fuse_global_vars.nsmp.ns = optarg;\n                break;\n            case 'm':\n                g_fuse_global_vars.nsmp.mountpoint = optarg;\n                break;\n            case 'b':\n                sf_set_global_base_path(optarg);\n                break;\n            case '?':\n                return EINVAL;\n            default:\n                break;\n        }\n    }\n\n    return 0;\n}\n\n#define OPTION_NAME_USER_STR \"user\"\n#define OPTION_NAME_USER_LEN (sizeof(OPTION_NAME_USER_STR) - 1)\n\n#define OPTION_NAME_KEY_STR  \"key\"\n#define OPTION_NAME_KEY_LEN  (sizeof(OPTION_NAME_KEY_STR) - 1)\n\n#define OPTION_NAME_NAMESPACE_STR  \"namespace\"\n#define OPTION_NAME_NAMESPACE_LEN  (sizeof(OPTION_NAME_NAMESPACE_STR) - 1)\n\n#define OPTION_NAME_MOUNTPOINT_STR \"mountpoint\"\n#define OPTION_NAME_MOUNTPOINT_LEN (sizeof(OPTION_NAME_MOUNTPOINT_STR) - 1)\n\n#define OPTION_NAME_BASE_PATH_STR \"base-path\"\n#define OPTION_NAME_BASE_PATH_LEN (sizeof(OPTION_NAME_BASE_PATH_STR) - 1)\n\nstatic int process_cmdline(int argc, char *argv[], bool *continue_flag)\n{\n    const SFCMDOption other_options[] = {\n        {{OPTION_NAME_USER_STR, OPTION_NAME_USER_LEN},\n            'u', true, \"-u | --user: the username\"},\n        {{OPTION_NAME_KEY_STR, OPTION_NAME_KEY_LEN},\n            'k', true, \"-k | --key: the secret key filename\"},\n        {{OPTION_NAME_NAMESPACE_STR, OPTION_NAME_NAMESPACE_LEN},\n            'n', true, \"-n | --namespace: the FastDIR namespace\"},\n        {{OPTION_NAME_MOUNTPOINT_STR, OPTION_NAME_MOUNTPOINT_LEN},\n            'm', true, \"-m | --mountpoint: the mountpoint\"},\n        {{OPTION_NAME_BASE_PATH_STR, OPTION_NAME_BASE_PATH_LEN},\n            'b', true, \"-b | --base-path: the base path\"},\n        {{NULL, 0}, 0, false, NULL}\n    };\n    char *action;\n    bool stop;\n    int result;\n\n    *continue_flag = false;\n    if (argc < 2) {\n        sf_usage_ex(argv[0], other_options);\n        return 1;\n    }\n\n    config_filename = sf_parse_daemon_mode_and_action_ex(\n            argc, argv, &g_fcfs_global_vars.version,\n            &daemon_mode, &action, \"start\", other_options);\n    if (config_filename == NULL) {\n        return 0;\n    }\n\n    log_init2();\n    //log_set_time_precision(&g_log_context, LOG_TIME_PRECISION_MSECOND);\n\n    if ((result=parse_cmd_options(argc, argv)) != 0) {\n        return result;\n    }\n\n    if (!SF_G_BASE_PATH_INITED) {\n        result = sf_get_base_path_from_conf_file(config_filename);\n        if (result != 0) {\n            log_destroy();\n            return result;\n        }\n        SF_G_BASE_PATH_INITED = true;\n    }\n\n    fc_get_full_filename(SF_G_BASE_PATH_STR, SF_G_BASE_PATH_LEN,\n            \"fused.pid\", sizeof(\"fused.pid\") - 1, g_pid_filename);\n    stop = false;\n    result = process_action(g_pid_filename, action, &stop);\n    if (result != 0) {\n        if (result == EINVAL) {\n            sf_usage_ex(argv[0], other_options);\n        }\n        log_destroy();\n        return result;\n    }\n\n    if (stop) {\n        log_destroy();\n        return 0;\n    }\n\n    *continue_flag = true;\n    return result;\n}\n\nstatic void sig_usr1_handler(int sig)\n{\n    ConnectionPoolStat fdir_stat;\n    ConnectionPoolStat fs_stat;\n    double fdir_avg_servers;\n    double fs_avg_servers;\n\n    conn_pool_stat(&g_fdir_client_vars.client_ctx.cm.cpool, &fdir_stat);\n    if (fdir_stat.bucket_used > 0) {\n        fdir_avg_servers = (double)fdir_stat.server_count /\n            (double)fdir_stat.bucket_used;\n    } else {\n        fdir_avg_servers = 1.00;\n    }\n    logInfo(\"fdir connection pool stat {htable capacity: %d, \"\n            \"server count: %d, used-buckets: %d, \"\n            \"servers / used-buckets: %.2f, \"\n            \"connections {total: %d, used: %d, free: %d}\",\n            fdir_stat.htable_capacity, fdir_stat.server_count,\n            fdir_stat.bucket_used, fdir_avg_servers,\n            fdir_stat.connection.total_count,\n            (fdir_stat.connection.total_count -\n             fdir_stat.connection.free_count),\n            fdir_stat.connection.free_count);\n\n    conn_pool_stat(&g_fs_client_vars.client_ctx.cm.cpool, &fs_stat);\n    if (fs_stat.bucket_used > 0) {\n        fs_avg_servers = (double)fs_stat.server_count /\n            (double)fs_stat.bucket_used;\n    } else {\n        fs_avg_servers = 1.00;\n    }\n    logInfo(\"fstore connection pool stat {htable capacity: %d, \"\n            \"server count: %d, used-buckets: %d, \"\n            \"servers / used-buckets: %.2f, \"\n            \"connections {total: %d, used: %d, free: %d}\",\n            fs_stat.htable_capacity, fs_stat.server_count,\n            fs_stat.bucket_used, fs_avg_servers,\n            fs_stat.connection.total_count,\n            (fs_stat.connection.total_count -\n             fs_stat.connection.free_count),\n            fs_stat.connection.free_count);\n}\n\nstatic int setup_user_signal_handler()\n{\n    struct sigaction act;\n    memset(&act, 0, sizeof(act));\n    sigemptyset(&act.sa_mask);\n\n    act.sa_handler = sig_usr1_handler;\n    if (sigaction(SIGUSR1, &act, NULL) < 0 ||\n        sigaction(SIGUSR2, &act, NULL) < 0)\n    {\n        logCrit(\"file: \"__FILE__\", line: %d, \"\n                \"call sigaction fail, errno: %d, error info: %s\",\n                __LINE__, errno, strerror(errno));\n        return errno;\n    }\n\n    return 0;\n}\n\nint main(int argc, char *argv[])\n{\n    int result;\n    int wait_count;\n    pthread_t schedule_tid;\n    struct fuse_lowlevel_ops fuse_operations;\n    struct fuse_session *se;\n\n    result = process_cmdline(argc, argv, (bool *)&SF_G_CONTINUE_FLAG);\n    if (!SF_G_CONTINUE_FLAG) {\n        return result;\n    }\n\n#ifdef FDIR_MBLOCK_CHECK\n    fast_mblock_manager_init();\n#endif\n\n    sf_enable_exit_on_oom();\n\n    do {\n        if ((result=setup_server_env(config_filename)) != 0) {\n            break;\n        }\n\n        if ((result=fs_fuse_wrapper_init(&fuse_operations)) != 0) {\n            break;\n        }\n\n        if ((result=sf_startup_schedule(&schedule_tid)) != 0) {\n            break;\n        }\n\n        if ((se=create_fuse_session(argv[0], &fuse_operations)) == NULL) {\n            result = ENOMEM;\n            break;\n        }\n\n        fuse_instance = se;\n        sf_set_sig_quit_handler(fuse_exit_handler);\n        setup_user_signal_handler();\n\n#ifdef FDIR_MBLOCK_CHECK\n    setup_mblock_stat_task();\n#endif\n\n        if ((result=fuse_session_mount(se, g_fuse_global_vars.\n                        nsmp.mountpoint)) != 0)\n        {\n            break;\n        }\n\n        /* Block until ctrl+c or fusermount -u */\n        if (g_fuse_global_vars.singlethread) {\n            result = fuse_session_loop(se);\n        } else {\n            struct {\n#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 12)\n                struct fuse_loop_config holder;\n#endif\n                struct fuse_loop_config *ptr;\n            } fuse_config;\n\n#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 12)\n            fuse_config.ptr = &fuse_config.holder;\n            fuse_config.holder.clone_fd = g_fuse_global_vars.clone_fd;\n            fuse_config.holder.max_idle_threads =\n                g_fuse_global_vars.max_idle_threads;\n#else\n            fuse_config.ptr = fuse_loop_cfg_create();\n            fuse_loop_cfg_set_clone_fd(fuse_config.ptr,\n                    g_fuse_global_vars.clone_fd);\n            if (g_fuse_global_vars.max_idle_threads <\n                    g_fuse_global_vars.max_threads)\n            {\n                fuse_loop_cfg_set_idle_threads(fuse_config.ptr,\n                        g_fuse_global_vars.max_idle_threads);\n            }\n            fuse_loop_cfg_set_max_threads(fuse_config.ptr,\n                    g_fuse_global_vars.max_threads);\n#endif\n            result = fuse_session_loop_mt(se, fuse_config.ptr);\n        }\n\n        fuse_session_unmount(se);\n        fcfs_api_terminate();\n    } while (0);\n\n    if (g_schedule_flag) {\n        pthread_kill(schedule_tid, SIGINT);\n    }\n\n    wait_count = 0;\n    while (g_schedule_flag) {\n        fc_sleep_ms(100);\n        if (++wait_count > 100) {\n            lwarning(\"waiting timeout, exit!\");\n            break;\n        }\n    }\n\n    if (result == 0) {\n        logInfo(\"file: \"__FILE__\", line: %d, \"\n                \"program exit normally.\\n\", __LINE__);\n    } else {\n        logCrit(\"file: \"__FILE__\", line: %d, \"\n                \"program exit abnormally with errno: %d!\\n\",\n                __LINE__, result);\n    }\n\n    if (need_delete_pid_file) {\n        delete_pid_file(g_pid_filename);\n    }\n    log_destroy();\n\n\treturn result < 0 ? 1 : result;\n}\n\nstatic int setup_server_env(const char *config_filename)\n{\n    int result;\n\n    if ((result=sf_global_init(\"fcfs_fused\")) != 0) {\n        return result;\n    }\n    if (daemon_mode) {\n        daemon_init(false);\n    }\n\n    if ((result=fcfs_fuse_global_init(config_filename)) != 0) {\n        return result;\n    }\n    umask(0);\n\n    log_set_use_file_write_lock(true);\n    if ((result=log_reopen()) != 0) {\n        if (result == EAGAIN || result == EACCES) {\n            logCrit(\"file: \"__FILE__\", line: %d, \"\n                    \"the process already running, please kill \"\n                    \"the old process then try again!\", __LINE__);\n        }\n        return result;\n    }\n\n    if ((result=sf_setup_signal_handler()) != 0) {\n        return result;\n    }\n\n    if ((result=write_to_pid_file(g_pid_filename)) != 0) {\n        return result;\n    }\n\n    log_set_cache(true);\n    need_delete_pid_file = true;\n    return fcfs_api_start_ex(&g_fcfs_api_ctx);\n}\n\nstatic struct fuse_session *create_fuse_session(char *argv0,\n        struct fuse_lowlevel_ops *ops)\n{\n    struct fuse_args args;\n    char *argv[16];\n    int argc;\n\n    argc = 0;\n    argv[argc++] = argv0;\n\n    if (g_log_context.log_level == LOG_DEBUG) {\n        argv[argc++] = \"-d\";\n    }\n\n    if (g_fuse_global_vars.auto_unmount) {\n        argv[argc++] = \"-o\";\n        argv[argc++] = \"auto_unmount\";\n    }\n\n    if (g_fuse_global_vars.allow_others == allow_root) {\n        argv[argc++] = \"-o\";\n        argv[argc++] = \"allow_root\";\n    } else if (g_fuse_global_vars.allow_others == allow_all) {\n        argv[argc++] = \"-o\";\n        argv[argc++] = \"allow_other\";\n    }\n\n    if (g_fuse_global_vars.writeback_cache) {\n        argv[argc++] = \"-o\";\n        argv[argc++] = \"writeback_cache\";\n\n        argv[argc++] = \"-o\";\n        argv[argc++] = \"time_gran=1000000000\";\n    }\n\n    if (g_fuse_global_vars.read_only) {\n        argv[argc++] = \"-o\";\n        argv[argc++] = \"ro\";\n    }\n\n    args.argc = argc;\n    args.argv = argv;\n    args.allocated = 0;\n    if ((g_fuse_cinfo_opts=fuse_parse_conn_info_opts(&args)) == NULL) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"fuse_parse_conn_info_opts fail!\", __LINE__);\n        return NULL;\n    }\n\n    return fuse_session_new(&args, ops, sizeof(*ops), NULL);\n}\n\nstatic void fuse_exit_handler(int sig)\n{\n    if (fuse_instance != NULL) {\n        fuse_session_exit(fuse_instance);\n        fuse_instance = NULL;\n    }\n}\n\n#ifdef FDIR_MBLOCK_CHECK\nstatic int mblock_stat_task_func(void *args)\n{\n    fast_mblock_manager_stat_print_ex(false, FAST_MBLOCK_ORDER_BY_ELEMENT_SIZE);\n    return 0;\n}\n\nstatic int setup_mblock_stat_task()\n{\n    ScheduleEntry schedule_entry;\n    ScheduleArray schedule_array;\n\n    INIT_SCHEDULE_ENTRY(schedule_entry, sched_generate_next_id(),\n            0, 0, 0, 60, mblock_stat_task_func, NULL);\n    schedule_entry.new_thread = true;\n\n    schedule_array.count = 1;\n    schedule_array.entries = &schedule_entry;\n    return sched_add_entries(&schedule_array);\n}\n#endif\n"
  },
  {
    "path": "src/fuse/fuse_wrapper.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <pwd.h>\n#include <grp.h>\n#include \"fastcommon/logger.h\"\n#include \"global.h\"\n#include \"groups_htable.h\"\n#include \"getgroups.h\"\n#include \"fuse_wrapper.h\"\n\n#define FS_READDIR_BUFFER_INIT_NONE        0\n#define FS_READDIR_BUFFER_INIT_NORMAL      1\n#define FS_READDIR_BUFFER_INIT_PLUS        2\n\nstruct fuse_conn_info_opts *g_fuse_cinfo_opts;\nstatic struct fast_mblock_man fh_allocator;\n\nstatic inline void set_operator_by_req(const struct fuse_ctx *fctx,\n        FDIRDentryOperator *oper, char *buff)\n{\n    int count;\n\n    if (g_fcfs_api_ctx.owner.type == fcfs_api_owner_type_fixed) {\n        *oper = g_fcfs_api_ctx.owner.oper;\n        return;\n    }\n\n    if (fctx->uid == 0 || !ADDITIONAL_GROUPS_ENABLED) {\n        FDIR_SET_OPERATOR(*oper, fctx->uid, fctx->gid, 0, buff);\n        return;\n    }\n\n    if (GROUPS_CACHE_ENABLED) {\n        if (fcfs_groups_htable_find(fctx->pid, fctx->uid,\n                    fctx->gid, &count, buff) != 0)\n        {\n            count = fcfs_get_groups(fctx->pid, fctx->uid, fctx->gid, buff);\n            fcfs_groups_htable_insert(fctx->pid, fctx->uid,\n                    fctx->gid, count, buff);\n\n            /*\n            logInfo(\"SET pid: %d, uid: %d, gid: %d, groups count: %d, \"\n                    \"first group: %d\", fctx->pid, fctx->uid, fctx->gid,\n                    count, count > 0 ? buff2int(buff) : -1);\n                    */\n        } else {\n            /*\n            logInfo(\"get pid: %d, uid: %d, gid: %d, groups count: %d, \"\n                    \"first group: %d\", fctx->pid, fctx->uid, fctx->gid,\n                    count, count > 0 ? buff2int(buff) : -1);\n                    */\n        }\n    } else {\n        count = fcfs_get_groups(fctx->pid, fctx->uid, fctx->gid, buff);\n    }\n    FDIR_SET_OPERATOR(*oper, fctx->uid, fctx->gid, count, buff);\n}\n\nstatic inline void fill_entry_param(const FDIRDEntryInfo *dentry,\n        struct fuse_entry_param *param)\n{\n    memset(param, 0, sizeof(*param));\n    param->ino = dentry->inode;\n    param->attr_timeout = g_fuse_global_vars.attribute_timeout;\n    param->entry_timeout = g_fuse_global_vars.entry_timeout;\n    fcfs_api_fill_stat(dentry, &param->attr);\n}\n\nstatic inline int fs_convert_inode(fuse_req_t req,\n        const fuse_ino_t ino, int64_t *new_inode)\n{\n    int result;\n    FDIRDentryOperator oper;\n    static int64_t root_inode = 0;\n\n    if (ino == FUSE_ROOT_ID) {\n        if (root_inode == 0) {\n            char groups_buff[FDIR_MAX_USER_GROUP_BYTES];\n            set_operator_by_req(fuse_req_ctx(req), &oper, groups_buff);\n            if ((result=fcfs_api_lookup_inode_by_path(\"/\",\n                            &oper, new_inode)) != 0)\n            {\n                return result;\n            }\n            root_inode = *new_inode;\n        } else {\n            *new_inode = root_inode;\n        }\n    } else {\n        *new_inode = ino;\n    }\n    return 0;\n}\n\n#define SET_OPER_INODE_PAIR(req, oino, _inode) \\\n    char groups_buff[FDIR_MAX_USER_GROUP_BYTES]; \\\n    do { \\\n        const struct fuse_ctx *fctx; \\\n        fctx = fuse_req_ctx(req); \\\n        set_operator_by_req(fctx, &oino.oper, groups_buff); \\\n        oino.inode = _inode; \\\n    } while (0)\n\n\n#define SET_OPER_PNAME_PAIR(req, opname, parent_inode, name) \\\n    char groups_buff[FDIR_MAX_USER_GROUP_BYTES]; \\\n    do { \\\n        set_operator_by_req(fuse_req_ctx(req), &opname.oper, groups_buff); \\\n        FDIR_SET_DENTRY_PNAME_STR(&opname.pname, parent_inode, name); \\\n    } while (0)\n\nstatic inline void do_reply_attr(fuse_req_t req, FDIRDEntryInfo *dentry)\n{\n    struct stat stat;\n    memset(&stat, 0, sizeof(stat));\n    fcfs_api_fill_stat(dentry, &stat);\n    fuse_reply_attr(req, &stat, g_fuse_global_vars.attribute_timeout);\n}\n\nstatic void fs_do_getattr(fuse_req_t req, fuse_ino_t ino,\n\t\t\t     struct fuse_file_info *fi)\n{\n    const int flags = 0;\n    int result;\n    int64_t new_inode;\n    FDIRClientOperInodePair oino;\n    FDIRDEntryInfo dentry;\n\n    if (fs_convert_inode(req, ino, &new_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    SET_OPER_INODE_PAIR(req, oino, new_inode);\n    if ((result=fcfs_api_stat_dentry_by_inode(&oino, flags, &dentry)) == 0) {\n        do_reply_attr(req, &dentry);\n    } else {\n        fuse_reply_err(req, result);\n    }\n}\n\nvoid fs_do_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,\n             int to_set, struct fuse_file_info *fi)\n{\n    const int flags = 0;\n    int result;\n    int64_t new_inode;\n    FDIRStatModifyFlags options;\n    FDIRClientOperInodePair oino;\n    const struct fuse_ctx *fctx;\n    FDIRDEntryInfo *pe;\n    FDIRDEntryInfo dentry;\n\n    /*\n    logInfo(\"=====file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", to_set: %d, fi: %p, size bit: %d====\",\n            __LINE__, __FUNCTION__, ino, to_set, fi,\n            (to_set & FUSE_SET_ATTR_SIZE));\n            */\n\n    if (fs_convert_inode(req, ino, &new_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n    fctx = fuse_req_ctx(req);\n    SET_OPER_INODE_PAIR(req, oino, new_inode);\n\n    options.flags = 0;\n    if ((to_set & FUSE_SET_ATTR_MODE)) {\n        options.mode = 1;\n    }\n\n    if ((to_set & FUSE_SET_ATTR_UID)) {\n        options.uid = 1;\n    }\n\n    if ((to_set & FUSE_SET_ATTR_GID)) {\n        options.gid = 1;\n    }\n\n    if ((to_set & FUSE_SET_ATTR_SIZE)) {\n        FCFSAPIFileInfo *fh;\n\n        if (fi == NULL) {\n            if ((result=fcfs_api_file_truncate(&oino, attr->st_size,\n                            fctx->pid, &dentry)) != 0)\n            {\n                fuse_reply_err(req, result);\n                return;\n            }\n\n            dentry.stat.size = attr->st_size;\n            pe = &dentry;\n        } else {\n            fh = (FCFSAPIFileInfo *)fi->fh;\n            if (fh == NULL) {\n                fuse_reply_err(req, EBADF);\n                return;\n            }\n\n            /*\n            logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n                    \"SET file size from %\"PRId64\" to: %\"PRId64,\n                    __LINE__, __FUNCTION__, fh->dentry.stat.size,\n                    (int64_t)attr->st_size);\n                    */\n\n            if ((result=fcfs_api_ftruncate_ex(fh,\n                            attr->st_size, fctx->pid)) != 0)\n            {\n                fuse_reply_err(req, result);\n                return;\n            }\n\n            fh->dentry.stat.size = attr->st_size;\n            pe = &fh->dentry;\n        }\n    } else {\n        pe = NULL;\n    }\n\n    if ((to_set & FUSE_SET_ATTR_CTIME)) {\n        options.ctime = 1;\n    }\n\n    if ((to_set & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_ATIME_NOW))) {\n        options.atime = 1;\n        if ((to_set & FUSE_SET_ATTR_ATIME_NOW)) {\n            options.atime_now = 1;\n        }\n    }\n\n    if ((to_set & (FUSE_SET_ATTR_MTIME | FUSE_SET_ATTR_MTIME_NOW))) {\n        options.mtime = 1;\n        if ((to_set & FUSE_SET_ATTR_MTIME_NOW)) {\n            options.mtime_now = 1;\n        } else if (options.atime_now && attr->st_atime == attr->st_mtime) {\n            options.mtime_now = 1;\n        }\n    }\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, new_inode: %\"PRId64\", \"\n            \"flags: %\"PRId64\", to_set: %d, atime bit: %d, mtime bit: %d, ctime bit: %d, \"\n            \"uid bit: %d, uid_to_set: %d, gid bit: %d, gid_to_set: %d, \"\n            \"oper {uid: %d, gid: %d}, atime_now: %d, atime: %ld, \"\n            \"mtime_now: %d, mtime: %ld\", __LINE__, __FUNCTION__,\n            new_inode, options.flags, to_set, (to_set & FUSE_SET_ATTR_ATIME),\n            (to_set & FUSE_SET_ATTR_MTIME), options.ctime, options.uid,\n            attr->st_uid, options.gid, attr->st_gid, oino.oper.uid,\n            oino.oper.gid, (to_set & FUSE_SET_ATTR_ATIME_NOW),\n            attr->st_atim.tv_sec, (to_set & FUSE_SET_ATTR_MTIME_NOW),\n            attr->st_mtim.tv_sec);\n            */\n\n    if (options.flags == 0) {\n        if (pe == NULL) {\n            pe = &dentry;\n            result = fcfs_api_stat_dentry_by_inode(&oino, flags, &dentry);\n        } else {\n            result = 0;\n        }\n    } else {\n        pe = &dentry;\n        result = fcfs_api_modify_stat_by_inode(&oino,\n                attr, options.flags, flags, &dentry);\n    }\n    if (result != 0) {\n        fuse_reply_err(req, result);\n    } else {\n        do_reply_attr(req, pe);\n    }\n}\n\nstatic void fs_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)\n{\n    const int flags = 0;\n    int result;\n    int64_t parent_inode;\n    FDIRDEntryInfo dentry;\n    FDIRClientOperPnamePair opname;\n    struct fuse_entry_param param;\n\n    if (fs_convert_inode(req, parent, &parent_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    SET_OPER_PNAME_PAIR(req, opname, parent_inode, name);\n    if ((result=fcfs_api_stat_dentry_by_pname(&opname, flags, &dentry)) != 0) {\n        /*\n        logError(\"file: \"__FILE__\", line: %d, func: %s, \"\n                \"parent: %\"PRId64\", name: %s(%d), result: %d\",\n                __LINE__, __FUNCTION__, parent, name, (int)strlen(name), result);\n                */\n        fuse_reply_err(req, result);\n        return;\n    }\n\n    fill_entry_param(&dentry, &param);\n    fuse_reply_entry(req, &param);\n}\n\nstatic int dentry_list_to_buff(fuse_req_t req, FCFSAPIOpendirSession *session)\n{\n    FDIRClientDentry *cd;\n    FDIRClientDentry *end;\n    struct stat stat;\n    struct fuse_entry_param param;\n    int result;\n    int len;\n    int next_offset;\n    char name[NAME_MAX];\n\n    fast_buffer_reset(&session->buffer);\n    if (session->array.count == 0) {\n        return 0;\n    }\n\n    end = session->array.entries + session->array.count;\n    for (cd=session->array.entries; cd<end; cd++) {\n        if (cd->name.len >= sizeof(name)) {\n            snprintf(name, sizeof(name), \"%.*s\",\n                    cd->name.len, cd->name.str);\n        } else {\n            memcpy(name, cd->name.str, cd->name.len);\n            *(name + cd->name.len) = '\\0';\n        }\n        if (session->btype == FS_READDIR_BUFFER_INIT_NORMAL) {\n            len = fuse_add_direntry(req, NULL, 0, name, NULL, 0);\n        } else {\n            len = fuse_add_direntry_plus(req, NULL, 0, name, NULL, 0);\n        }\n        next_offset = session->buffer.length + len;\n        if (next_offset > session->buffer.alloc_size) {\n            if ((result=fast_buffer_set_capacity(&session->buffer,\n                            next_offset)) != 0)\n            {\n                return result;\n            }\n        }\n\n        if (session->btype == FS_READDIR_BUFFER_INIT_NORMAL) {\n            memset(&stat, 0, sizeof(stat));\n            fcfs_api_fill_stat(&cd->dentry, &stat);\n            fuse_add_direntry(req, session->buffer.data +\n                    session->buffer.length, session->buffer.\n                    alloc_size - session->buffer.length,\n                    name, &stat, next_offset);\n        } else {\n            fill_entry_param(&cd->dentry, &param);\n            fuse_add_direntry_plus(req, session->buffer.data +\n                    session->buffer.length, session->buffer.\n                    alloc_size - session->buffer.length,\n                    name, &param, next_offset);\n        }\n\n        session->buffer.length = next_offset;\n    }\n\n    return 0;\n}\n\nstatic void fs_do_opendir(fuse_req_t req, fuse_ino_t ino,\n        struct fuse_file_info *fi)\n{\n    int64_t new_inode;\n    FCFSAPIOpendirSession *session;\n    FDIRClientOperInodePair oino;\n    int result;\n\n    if (fs_convert_inode(req, ino, &new_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n    if ((session=fcfs_api_alloc_opendir_session()) == NULL) {\n        fuse_reply_err(req, ENOMEM);\n        return;\n    }\n\n    SET_OPER_INODE_PAIR(req, oino, new_inode);\n    session->btype = FS_READDIR_BUFFER_INIT_NONE;\n    if ((result=fcfs_api_list_dentry_by_inode(&oino,\n                    &session->array)) != 0)\n    {\n        fcfs_api_free_opendir_session(session);\n        fuse_reply_err(req, result);\n        return;\n    }\n\n    fi->fh = (long)session;\n    fuse_reply_open(req, fi);\n}\n\nstatic void do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,\n        off_t offset, struct fuse_file_info *fi, const int buffer_type)\n{\n    FCFSAPIOpendirSession *session;\n    int result;\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, buffer_type: %d, \"\n            \"ino: %\"PRId64\", fh: %\"PRId64\", offset: %\"PRId64\", size: %\"PRId64,\n            __LINE__, __FUNCTION__, buffer_type, ino, fi->fh,\n            (int64_t)offset, (int64_t)size);\n            */\n\n    session = (FCFSAPIOpendirSession *)fi->fh;\n    if (session == NULL) {\n        fuse_reply_err(req, EBUSY);\n        return;\n    }\n\n    if (session->btype == FS_READDIR_BUFFER_INIT_NONE) {\n        session->btype = buffer_type;\n        if ((result=dentry_list_to_buff(req, session)) != 0) {\n            fuse_reply_err(req, result);\n            return;\n        }\n    } else if (session->btype != buffer_type) {\n        logWarning(\"file: \"__FILE__\", line: %d, func: %s, \"\n                \"ino: %\"PRId64\", unexpect buffer type: %d != %d\",\n                __LINE__, __FUNCTION__, ino, session->btype,\n                buffer_type);\n        fuse_reply_buf(req, NULL, 0);\n        return;\n    }\n\n    if (offset < session->buffer.length) {\n        fuse_reply_buf(req, session->buffer.data + offset,\n                FC_MIN(session->buffer.length - offset, size));\n    } else {\n        fuse_reply_buf(req, NULL, 0);\n    }\n}\n\n/*\nstatic void fs_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,\n        off_t offset, struct fuse_file_info *fi)\n{\n    do_readdir(req, ino, size, offset, fi, FS_READDIR_BUFFER_INIT_NORMAL);\n}\n*/\n\nstatic void fs_do_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,\n\t\toff_t offset, struct fuse_file_info *fi)\n{\n    do_readdir(req, ino, size, offset, fi, FS_READDIR_BUFFER_INIT_PLUS);\n}\n\nstatic void fs_do_releasedir(fuse_req_t req, fuse_ino_t ino,\n        struct fuse_file_info *fi)\n{\n    FCFSAPIOpendirSession *session;\n\n    session = (FCFSAPIOpendirSession *)fi->fh;\n    if (session != NULL) {\n        fcfs_api_free_opendir_session(session);\n        fi->fh = 0;\n    }\n\n    fuse_reply_err(req, 0);\n}\n\nstatic int do_open(fuse_req_t req, FDIRDEntryInfo *dentry,\n        struct fuse_file_info *fi, const FCFSAPIFileContext *fctx)\n{\n    int result;\n    FCFSAPIFileInfo *fh;\n\n    fh = (FCFSAPIFileInfo *)fast_mblock_alloc_object(&fh_allocator);\n    if (fh == NULL) {\n        return ENOMEM;\n    }\n\n    if (fi->flags & O_DIRECT) {\n        if (OS_KERNEL_VERSION.major > 4 || (OS_KERNEL_VERSION.major == 4 &&\n            OS_KERNEL_VERSION.minor >= 18))\n        {\n             fi->direct_io = 1;\n        }\n    } else {\n        if (g_fuse_global_vars.kernel_cache) {\n            fi->keep_cache = 1;\n        }\n    }\n\n    if ((result=fcfs_api_open_by_dentry(fh, dentry, fi->flags, fctx)) != 0) {\n        fast_mblock_free_object(&fh_allocator, fh);\n        if (!(result == EISDIR || result == ENOENT)) {\n            result = EACCES;\n        }\n        return result;\n    }\n\n    fi->fh = (long)fh;\n    return 0;\n}\n\nstatic void fs_do_access(fuse_req_t req, fuse_ino_t ino, int mask)\n{\n    const int flags = 0;\n    int result;\n    int64_t new_inode;\n    FDIRClientOperInodePair oino;\n    FDIRDEntryInfo dentry;\n\n    if (fs_convert_inode(req, ino, &new_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    SET_OPER_INODE_PAIR(req, oino, new_inode);\n    result = fcfs_api_access_dentry_by_inode(&oino, mask, flags, &dentry);\n    fuse_reply_err(req, result);\n}\n\nstatic void fs_do_create(fuse_req_t req, fuse_ino_t parent,\n        const char *name, mode_t mode, struct fuse_file_info *fi)\n{\n    const dev_t rdev = 0;\n    FCFSAPIFileContext fctx;\n    int result;\n    int64_t parent_inode;\n    FDIRClientOperPnamePair opname;\n    FDIRDEntryInfo dentry;\n    struct fuse_entry_param param;\n\n    if (fs_convert_inode(req, parent, &parent_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"parent ino: %\"PRId64\", name: %s, mode: %03o\",\n            __LINE__, __FUNCTION__, parent_inode, name, mode);\n            */\n\n    SET_OPER_PNAME_PAIR(req, opname, parent_inode, name);\n    if ((result=fdir_client_create_dentry_by_pname_ex(g_fcfs_api_ctx.\n                    contexts.fdir, &g_fcfs_api_ctx.ns, &opname, mode,\n                    rdev, &dentry)) != 0)\n    {\n        if (result != EEXIST) {\n            fuse_reply_err(req, result);\n            return;\n        }\n\n        if ((fi->flags & O_EXCL)) {\n            fuse_reply_err(req, EEXIST);\n            return;\n        }\n\n        if ((result=fcfs_api_access_dentry_by_pname(&opname,\n                        FCFS_API_GET_ACCESS_MASK(fi->flags),\n                        FCFS_API_GET_ACCESS_FLAGS(fi->flags),\n                        &dentry)) != 0)\n        {\n            fuse_reply_err(req, result);\n            return;\n        }\n    }\n\n    FCFS_API_SET_FCTX(fctx, opname.oper, mode, fuse_req_ctx(req)->pid);\n    fi->flags &= ~(O_CREAT | O_EXCL);\n    if ((result=do_open(req, &dentry, fi, &fctx)) != 0) {\n        fuse_reply_err(req, result);\n        return;\n    }\n\n    fill_entry_param(&dentry, &param);\n    fuse_reply_create(req, &param, fi);\n}\n\nstatic void do_mknod(fuse_req_t req, fuse_ino_t parent,\n        const char *name, mode_t mode, const dev_t rdev)\n{\n    int result;\n    int64_t parent_inode;\n    FDIRClientOperPnamePair opname;\n    FDIRDEntryInfo dentry;\n    struct fuse_entry_param param;\n\n    if (fs_convert_inode(req, parent, &parent_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"parent ino: %\"PRId64\", name: %s, mode: %03o, \"\n            \"rdev: %lx, isdir: %d\", __LINE__, __FUNCTION__,\n            parent_inode, name, mode, (long)rdev, S_ISDIR(mode));\n            */\n\n    SET_OPER_PNAME_PAIR(req, opname, parent_inode, name);\n    if ((result=fdir_client_create_dentry_by_pname_ex(g_fcfs_api_ctx.\n                    contexts.fdir, &g_fcfs_api_ctx.ns, &opname, mode,\n                    rdev, &dentry)) != 0)\n    {\n        fuse_reply_err(req, result);\n        return;\n    }\n\n    fill_entry_param(&dentry, &param);\n    fuse_reply_entry(req, &param);\n}\n\nstatic void fs_do_mknod(fuse_req_t req, fuse_ino_t parent,\n        const char *name, mode_t mode, dev_t rdev)\n{\n    do_mknod(req, parent, name, mode, rdev);\n}\n\nstatic void fs_do_mkdir(fuse_req_t req, fuse_ino_t parent,\n        const char *name, mode_t mode)\n{\n    mode |= S_IFDIR;\n    do_mknod(req, parent, name, mode, 0);\n}\n\nstatic int remove_dentry(fuse_req_t req, fuse_ino_t parent,\n        const char *name, const int flags)\n{\n    const struct fuse_ctx *fctx;\n    int64_t parent_inode;\n    FDIRClientOperPnamePair opname;\n\n    if (fs_convert_inode(req, parent, &parent_inode) != 0) {\n        return ENOENT;\n    }\n\n    fctx = fuse_req_ctx(req);\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"parent ino: %\"PRId64\", name: %s, pid: %d, uid: %d, gid: %d\",\n            __LINE__, __FUNCTION__, parent_inode, name, fctx->pid, fctx->uid, fctx->gid);\n            */\n\n    SET_OPER_PNAME_PAIR(req, opname, parent_inode, name);\n    return fcfs_api_remove_dentry_by_pname(&opname, flags, fctx->pid);\n}\n\nstatic void fs_do_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)\n{\n    int result;\n\n    result = remove_dentry(req, parent, name, FDIR_UNLINK_FLAGS_MATCH_DIR);\n    fuse_reply_err(req, result);\n}\n\nstatic void fs_do_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)\n{\n    int result;\n\n    result = remove_dentry(req, parent, name, FDIR_UNLINK_FLAGS_MATCH_FILE);\n    fuse_reply_err(req, result);\n}\n\nvoid fs_do_rename(fuse_req_t req, fuse_ino_t oldparent, const char *oldname,\n            fuse_ino_t newparent, const char *newname, unsigned int flags)\n{\n    int64_t old_parent_inode;\n    int64_t new_parent_inode;\n    const struct fuse_ctx *fctx;\n    string_t old_nm;\n    string_t new_nm;\n    FDIRDentryOperator oper;\n    char groups_buff[FDIR_MAX_USER_GROUP_BYTES];\n    int result;\n\n    if (fs_convert_inode(req, oldparent, &old_parent_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    if (fs_convert_inode(req, newparent, &new_parent_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"parent ino: %\"PRId64\", name: %s, \"\n            \"newparent ino: %\"PRId64\", new name: %s\",\n            __LINE__, __FUNCTION__, old_parent_inode, oldname,\n            new_parent_inode, newname);\n            */\n\n    fctx = fuse_req_ctx(req);\n    FC_SET_STRING(old_nm, (char *)oldname);\n    FC_SET_STRING(new_nm, (char *)newname);\n\n    set_operator_by_req(fctx, &oper, groups_buff);\n    result = fcfs_api_rename_dentry_by_pname(old_parent_inode, &old_nm,\n            new_parent_inode, &new_nm, &oper, flags, fctx->pid);\n    fuse_reply_err(req, result);\n}\n\nstatic void fs_do_link(fuse_req_t req, fuse_ino_t ino,\n        fuse_ino_t parent, const char *name)\n{\n    const int flags = FDIR_FLAGS_FOLLOW_SYMLINK;\n    const struct fuse_ctx *fctx;\n    FDIRClientOperPnamePair opname;\n    FDIRDEntryInfo dentry;\n    struct fuse_entry_param param;\n    int64_t parent_inode;\n    int result;\n\n    if (fs_convert_inode(req, parent, &parent_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    fctx = fuse_req_ctx(req);\n    SET_OPER_PNAME_PAIR(req, opname, parent_inode, name);\n    if ((result=fdir_client_link_dentry_by_pname(g_fcfs_api_ctx.contexts.fdir,\n                    ino, &g_fcfs_api_ctx.ns, &opname, (0777 & (~fctx->umask)),\n                    flags, &dentry)) == 0)\n    {\n        fill_entry_param(&dentry, &param);\n        fuse_reply_entry(req, &param);\n    } else {\n        fuse_reply_err(req, result);\n    }\n}\n\nstatic void fs_do_symlink(fuse_req_t req, const char *link,\n        fuse_ino_t parent, const char *name)\n{\n    const struct fuse_ctx *fctx;\n    int64_t parent_inode;\n    FDIRClientOperPnamePair opname;\n    string_t lk;\n    FDIRDEntryInfo dentry;\n    struct fuse_entry_param param;\n    int result;\n\n    if (fs_convert_inode(req, parent, &parent_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"link length: %d, parent ino: %\"PRId64\", \"\n            \"name length: %d\", __LINE__, __FUNCTION__,\n            (int)strlen(link), parent_inode, (int)strlen(name));\n            */\n\n    fctx = fuse_req_ctx(req);\n    FC_SET_STRING(lk, (char *)link);\n    SET_OPER_PNAME_PAIR(req, opname, parent_inode, name);\n    if ((result=fdir_client_symlink_dentry_by_pname(g_fcfs_api_ctx.\n                    contexts.fdir, &lk, &g_fcfs_api_ctx.ns, &opname,\n                    (0777 & (~fctx->umask)), &dentry)) == 0)\n    {\n        fill_entry_param(&dentry, &param);\n        fuse_reply_entry(req, &param);\n    } else {\n        fuse_reply_err(req, result);\n    }\n}\n\nstatic void fs_do_readlink(fuse_req_t req, fuse_ino_t ino)\n{\n    int result;\n    char buff[PATH_MAX];\n    FDIRClientOperInodePair oino;\n    string_t link;\n\n    SET_OPER_INODE_PAIR(req, oino, ino);\n    link.str = buff;\n    if ((result=fcfs_api_readlink_by_inode(&oino, &link, PATH_MAX)) == 0) {\n        fuse_reply_readlink(req, link.str);\n    } else {\n        fuse_reply_err(req, result);\n    }\n}\n\nstatic void fs_do_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)\n{\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", nlookup: %\"PRId64,\n            __LINE__, __FUNCTION__, ino, nlookup);\n            */\n    fuse_reply_none(req);\n}\n\nstatic void fs_do_forget_multi(fuse_req_t req, size_t count,\n        struct fuse_forget_data *forgets)\n{\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"count: %d\", __LINE__, __FUNCTION__, (int)count);\n            */\n    fuse_reply_none(req);\n}\n\nstatic void fs_do_open(fuse_req_t req, fuse_ino_t ino,\n\t\t\t  struct fuse_file_info *fi)\n{\n    int result;\n    int64_t new_inode;\n    FCFSAPIFileContext fctx;\n    FDIRClientOperInodePair oino;\n    const struct fuse_ctx *fuse_ctx;\n    FDIRDEntryInfo dentry;\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", fh: %\"PRId64\", O_APPEND flag: %d, \"\n            \"O_WRONLY: %d, O_RDWR: %d, O_NONBLOCK flag: %d\",\n            __LINE__, __FUNCTION__, ino, fi->fh,\n            (fi->flags & O_APPEND), (fi->flags & O_WRONLY),\n            (fi->flags & O_RDWR), (fi->flags & O_NONBLOCK));\n            */\n\n    if (fs_convert_inode(req, ino, &new_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    fuse_ctx = fuse_req_ctx(req);\n    SET_OPER_INODE_PAIR(req, oino, new_inode);\n    if ((result=fcfs_api_access_dentry_by_inode(&oino,\n                    FCFS_API_GET_ACCESS_MASK(fi->flags),\n                    FCFS_API_GET_ACCESS_FLAGS(fi->flags),\n                    &dentry)) != 0)\n    {\n        fuse_reply_err(req, result);\n        return;\n    }\n\n    FCFS_API_SET_FCTX(fctx, oino.oper, dentry.stat.mode, fuse_ctx->pid);\n    if ((result=do_open(req, &dentry, fi, &fctx)) != 0) {\n        fuse_reply_err(req, result);\n        return;\n    }\n\n    fuse_reply_open(req, fi);\n}\n\nstatic void fs_do_flush(fuse_req_t req, fuse_ino_t ino,\n        struct fuse_file_info *fi)\n{\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", fh: %\"PRId64\"\\n\",\n            __LINE__, __FUNCTION__, ino, fi->fh);\n            */\n\n    fuse_reply_err(req, 0);\n}\n\nstatic void fs_do_fsync(fuse_req_t req, fuse_ino_t ino,\n        int datasync, struct fuse_file_info *fi)\n{\n    FCFSAPIFileInfo *fh;\n    const struct fuse_ctx *fctx;\n    int result;\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", fh: %\"PRId64\", datasync: %d\",\n            __LINE__, __FUNCTION__, ino, fi->fh, datasync);\n            */\n\n    fh = (FCFSAPIFileInfo *)fi->fh;\n    if (fh != NULL) {\n        fctx = fuse_req_ctx(req);\n        if (datasync) {\n            result = fcfs_api_fdatasync(fh, fctx->pid);\n        } else {\n            result = fcfs_api_fsync(fh, fctx->pid);\n        }\n    } else {\n        result = EBADF;\n    }\n    fuse_reply_err(req, result);\n}\n\nstatic void fs_do_release(fuse_req_t req, fuse_ino_t ino,\n             struct fuse_file_info *fi)\n{\n    int result;\n    FCFSAPIFileInfo *fh;\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", fh: %\"PRId64\"\\n\",\n            __LINE__, __FUNCTION__, ino, fi->fh);\n            */\n\n    fh = (FCFSAPIFileInfo *)fi->fh;\n    if (fh != NULL) {\n        result = fcfs_api_close(fh);\n        fast_mblock_free_object(&fh_allocator, fh);\n    } else {\n        result = EBADF;\n    }\n    fuse_reply_err(req, result);\n}\n\nstatic void fs_do_read(fuse_req_t req, fuse_ino_t ino, size_t size,\n\t\t\t  off_t offset, struct fuse_file_info *fi)\n{\n    FCFSAPIFileInfo *fh;\n    const struct fuse_ctx *fctx;\n    int result;\n    int read_bytes;\n    char fixed_buff[256 * 1024];\n    char *buff;\n    \n    if (size <= sizeof(fixed_buff)) {\n        buff = fixed_buff;\n    } else if ((buff=(char *)fc_malloc(size)) == NULL) {\n        fuse_reply_err(req, ENOMEM);\n        return;\n    }\n\n    fh = (FCFSAPIFileInfo *)fi->fh;\n    if (fh == NULL) {\n        fuse_reply_err(req, EBADF);\n        return;\n    }\n\n    fctx = fuse_req_ctx(req);\n    if ((result=fcfs_api_pread_ex(fh, buff, size, offset,\n                    &read_bytes, fctx->pid)) != 0)\n    {\n        fuse_reply_err(req, result);\n        return;\n    }\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", fh: %p, size: %\"PRId64\", offset: %\"PRId64\", \"\n            \"read_bytes: %d, flags: %d, O_DIRECT: %d, O_SYNC: %d, \"\n            \"O_DSYNC: %d\", __LINE__, __FUNCTION__, ino, fh, size,\n            offset, read_bytes, fh->flags, fh->flags & O_DIRECT,\n            fh->flags & O_SYNC, fh->flags & O_DSYNC);\n            */\n\n    fuse_reply_buf(req, buff, read_bytes);\n    if (buff != fixed_buff) {\n        free(buff);\n    }\n}\n\nvoid fs_do_write(fuse_req_t req, fuse_ino_t ino, const char *buff,\n        size_t size, off_t offset, struct fuse_file_info *fi)\n{\n    FCFSAPIFileInfo *fh;\n    const struct fuse_ctx *fctx;\n    int result;\n    int written_bytes;\n\n    fh = (FCFSAPIFileInfo *)fi->fh;\n    if (fh == NULL) {\n        fuse_reply_err(req, EBADF);\n        return;\n    }\n\n    fctx = fuse_req_ctx(req);\n    if ((result=fcfs_api_pwrite_ex(fh, buff, size, offset,\n                    &written_bytes, fctx->pid)) != 0)\n    {\n        fuse_reply_err(req, result);\n        return;\n    }\n\n    /*\n    logInfo(\"=======file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", size: %\"PRId64\", offset: %\"PRId64\", \"\n            \"written_bytes: %d, flags: %d, O_DIRECT: %d, O_SYNC: %d, \"\n            \"O_DSYNC: %d\", __LINE__, __FUNCTION__, ino, size, offset,\n            written_bytes, fh->flags, fh->flags & O_DIRECT,\n            fh->flags & O_SYNC, fh->flags & O_DSYNC);\n            */\n\n    fuse_reply_write(req, written_bytes);\n}\n\nvoid fs_do_lseek(fuse_req_t req, fuse_ino_t ino, off_t offset,\n        int whence, struct fuse_file_info *fi)\n{\n    FCFSAPIFileInfo *fh;\n    int result;\n\n    fh = (FCFSAPIFileInfo *)fi->fh;\n    if (fh == NULL) {\n        fuse_reply_err(req, EBADF);\n        return;\n    }\n\n    if ((result=fcfs_api_lseek(fh, offset, whence)) != 0) {\n        fuse_reply_err(req, result);\n        return;\n    }\n\n    fuse_reply_lseek(req, fh->offset);\n}\n\nstatic void fs_do_getlk(fuse_req_t req, fuse_ino_t ino,\n        struct fuse_file_info *fi, struct flock *lock)\n{\n    int result;\n    FCFSAPIFileInfo *fh;\n    int64_t owner_id;\n\n    fh = (FCFSAPIFileInfo *)fi->fh;\n    if (fh == NULL) {\n        result = EBADF;\n        owner_id = 0;\n    } else {\n        owner_id = fi->lock_owner;\n        result = fcfs_api_getlk_ex(fh, lock, &owner_id);\n    }\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", fh: %\"PRId64\", type: %d, \"\n            \"whence: %d, start: %\"PRId64\", len: %\"PRId64\", pid: %d, \"\n            \"owner_id: %\"PRId64\", result: %d\", __LINE__, __FUNCTION__,\n            ino, fi->fh, lock->l_type, lock->l_whence, lock->l_start,\n            lock->l_len, lock->l_pid, owner_id, result);\n            */\n\n    if (result == 0) {\n        fuse_reply_lock(req, lock);\n    } else {\n        fuse_reply_err(req, result);\n    }\n}\n\nstatic void fs_do_setlk(fuse_req_t req, fuse_ino_t ino,\n        struct fuse_file_info *fi, struct flock *lock, int sleep)\n{\n    const bool blocked = false;\n    int result;\n    FCFSAPIFileInfo *fh;\n\n    fh = (FCFSAPIFileInfo *)fi->fh;\n    if (fh == NULL) {\n        result = EBADF;\n    } else {\n        result = fcfs_api_setlk_ex(fh, lock, fi->lock_owner, blocked);\n    }\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", fh: %\"PRId64\", lock_owner: %\"PRId64\", \"\n            \"type: %d, whence: %d, start: %\"PRId64\", len: %\"PRId64\", \"\n            \"pid: %d, sleep: %d, result: %d\", __LINE__, __FUNCTION__,\n            ino, fi->fh, fi->lock_owner, lock->l_type, lock->l_whence,\n            lock->l_start, lock->l_len, lock->l_pid, sleep, result);\n            */\n\n    fuse_reply_err(req, result);\n}\n\nstatic void fs_do_flock(fuse_req_t req, fuse_ino_t ino,\n        struct fuse_file_info *fi, int op)\n{\n    int result;\n    FCFSAPIFileInfo *fh;\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64\", fh: %\"PRId64\", lock_owner: %\"PRId64\", \"\n            \"op: %d, operation: %d\", __LINE__, __FUNCTION__,\n            ino, fi->fh, fi->lock_owner, op,\n            (op & (LOCK_SH | LOCK_EX | LOCK_UN)));\n            */\n\n    fh = (FCFSAPIFileInfo *)fi->fh;\n    if (fh == NULL) {\n        result = EBADF;\n    } else {\n        result = fcfs_api_flock_ex(fh, op, fi->lock_owner);\n    }\n    fuse_reply_err(req, result);\n}\n\nstatic void fs_do_statfs(fuse_req_t req, fuse_ino_t ino)\n{\n    int result;\n    struct statvfs stbuf;\n\n    if ((result=fcfs_api_statvfs(\"/\", &stbuf)) == 0) {\n        fuse_reply_statfs(req, &stbuf);\n    } else {\n        fuse_reply_err(req, result);\n    }\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, func: %s, \"\n            \"ino: %\"PRId64, __LINE__, __FUNCTION__, ino);\n            */\n}\n\nstatic void fs_do_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,\n        off_t offset, off_t length, struct fuse_file_info *fi)\n{\n    int result;\n    FCFSAPIFileInfo *fh;\n    const struct fuse_ctx *fctx;\n\n    fh = (FCFSAPIFileInfo *)fi->fh;\n    if (fh == NULL) {\n        result = EBADF;\n    } else {\n        fctx = fuse_req_ctx(req);\n        result = fcfs_api_fallocate_ex(fh, mode, offset, length, fctx->pid);\n    }\n\n    fuse_reply_err(req, result);\n}\n\nstatic void fs_do_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,\n        const char *value, size_t size, int flags)\n{\n    int64_t new_inode;\n    int result;\n    FDIRClientOperInodePair oino;\n    key_value_pair_t xattr;\n\n    if (!g_fuse_global_vars.xattr_enabled) {\n        fuse_reply_err(req, ENOSYS);\n        return;\n    }\n\n    if (fs_convert_inode(req, ino, &new_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    SET_OPER_INODE_PAIR(req, oino, new_inode);\n    FC_SET_STRING(xattr.key, (char *)name);\n    FC_SET_STRING_EX(xattr.value, (char *)value, size);\n    result = fcfs_api_set_xattr_by_inode(&oino, &xattr, flags);\n    fuse_reply_err(req, result);\n}\n\nstatic void fs_do_removexattr(fuse_req_t req, fuse_ino_t ino,\n        const char *name)\n{\n    const int flags = 0;\n    int result;\n    int64_t new_inode;\n    FDIRClientOperInodePair oino;\n    string_t nm;\n\n    if (!g_fuse_global_vars.xattr_enabled) {\n        fuse_reply_err(req, ENOSYS);\n        return;\n    }\n\n    if (fs_convert_inode(req, ino, &new_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    SET_OPER_INODE_PAIR(req, oino, new_inode);\n    FC_SET_STRING(nm, (char *)name);\n    result = fcfs_api_remove_xattr_by_inode(&oino, &nm, flags);\n    fuse_reply_err(req, result);\n}\n\nstatic void fs_do_getxattr(fuse_req_t req, fuse_ino_t ino,\n        const char *name, size_t size)\n{\n    int result;\n    int flags;\n    int value_size;\n    int64_t new_inode;\n    char v[FDIR_XATTR_MAX_VALUE_SIZE];\n    FDIRClientOperInodePair oino;\n    string_t nm;\n    string_t value;\n\n    if (!g_fuse_global_vars.xattr_enabled) {\n        fuse_reply_err(req, ENOSYS);\n        return;\n    }\n\n    if (fs_convert_inode(req, ino, &new_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    /*\n    logInfo(\"getxattr ino: %\"PRId64\", name: %s, size: %d, req: %p\",\n            ino, name, (int)size, req);\n            */\n\n    if (size == 0) {\n        value_size = 0;\n        flags = FDIR_FLAGS_XATTR_GET_SIZE;\n    } else if (size <= FDIR_XATTR_MAX_VALUE_SIZE) {\n        value_size = size;\n        flags = 0;\n    } else {\n        value_size = FDIR_XATTR_MAX_VALUE_SIZE;\n        flags = 0;\n    }\n\n    SET_OPER_INODE_PAIR(req, oino, new_inode);\n    value.str = v;\n    FC_SET_STRING(nm, (char *)name);\n    if ((result=fcfs_api_get_xattr_by_inode(&oino,\n                    &nm, &value, value_size, flags)) != 0)\n    {\n        fuse_reply_err(req, result == EOVERFLOW ? ERANGE : result);\n        return;\n    }\n\n    if (size == 0) {\n        fuse_reply_xattr(req, value.len);\n    } else {\n        fuse_reply_buf(req, value.str, value.len);\n    }\n}\n\nstatic void fs_do_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)\n{\n#define MAX_LIST_SIZE  (8 * 1024)\n\n    int result;\n    int flags;\n    int list_size;\n    int64_t new_inode;\n    FDIRClientOperInodePair oino;\n    char v[MAX_LIST_SIZE];\n    string_t list;\n\n    if (!g_fuse_global_vars.xattr_enabled) {\n        fuse_reply_err(req, ENOSYS);\n        return;\n    }\n\n    if (fs_convert_inode(req, ino, &new_inode) != 0) {\n        fuse_reply_err(req, ENOENT);\n        return;\n    }\n\n    if (size == 0) {\n        list_size = 0;\n        flags = FDIR_FLAGS_XATTR_GET_SIZE;\n    } else if (size <= MAX_LIST_SIZE) {\n        list_size = size;\n        flags = 0;\n    } else {\n        list_size = MAX_LIST_SIZE;\n        flags = 0;\n    }\n\n    SET_OPER_INODE_PAIR(req, oino, new_inode);\n    list.str = v;\n    if ((result=fcfs_api_list_xattr_by_inode(&oino,\n                    &list, list_size, flags)) != 0)\n    {\n        fuse_reply_err(req, result == EOVERFLOW ? ERANGE : result);\n        return;\n    }\n\n    /*\n    logInfo(\"listxattr ino: %\"PRId64\", size: %d, list: %.*s, list len: %d\",\n            ino, (int)size, list.len, list.str, list.len);\n            */\n\n    if (size == 0) {\n        fuse_reply_xattr(req, list.len);\n    } else {\n        fuse_reply_buf(req, list.str, list.len);\n    }\n}\n\nstatic void fs_do_init(void *userdata, struct fuse_conn_info *conn)\n{\n    fuse_apply_conn_info_opts(g_fuse_cinfo_opts, conn);\n    conn->want |= FUSE_CAP_EXPORT_SUPPORT;\n}\n\nint fs_fuse_wrapper_init(struct fuse_lowlevel_ops *ops)\n{\n    int result;\n    if ((result=fast_mblock_init_ex1(&fh_allocator, \"fuse_fh\",\n                    sizeof(FCFSAPIFileInfo), 4096, 0, NULL, NULL, true)) != 0)\n    {\n        return result;\n    }\n\n    if (GROUPS_CACHE_ENABLED && g_fcfs_api_ctx.owner.type !=\n            fcfs_api_owner_type_fixed)\n    {\n        if ((result=fcfs_groups_htable_init()) != 0) {\n            return result;\n        }\n    }\n\n    memset(ops, 0, sizeof(*ops));\n    ops->init = fs_do_init;\n    ops->lookup  = fs_do_lookup;\n    ops->getattr = fs_do_getattr;\n    ops->setattr = fs_do_setattr;\n    ops->opendir = fs_do_opendir;\n    //ops->readdir = fs_do_readdir;\n    ops->readdirplus = fs_do_readdirplus;\n    ops->releasedir  = fs_do_releasedir;\n    ops->create  = fs_do_create;\n    ops->access  = fs_do_access;\n    ops->open    = fs_do_open;\n    ops->fsync   = fs_do_fsync;\n    ops->flush   = fs_do_flush;\n    ops->release = fs_do_release;\n    ops->read    = fs_do_read;\n    ops->write   = fs_do_write;\n    ops->mknod   = fs_do_mknod;\n    ops->mkdir   = fs_do_mkdir;\n    ops->rmdir   = fs_do_rmdir;\n    ops->unlink  = fs_do_unlink;\n    ops->link    = fs_do_link;\n    ops->symlink = fs_do_symlink;\n    ops->readlink = fs_do_readlink;\n    ops->rename  = fs_do_rename;\n    ops->forget  = fs_do_forget;\n    ops->forget_multi = fs_do_forget_multi;\n    ops->lseek   = fs_do_lseek;\n    ops->getlk   = fs_do_getlk;\n    ops->setlk   = fs_do_setlk;\n    ops->flock   = fs_do_flock;\n    ops->statfs  = fs_do_statfs;\n    ops->fallocate = fs_do_fallocate;\n    ops->setxattr = fs_do_setxattr;\n    ops->getxattr = fs_do_getxattr;\n    ops->listxattr = fs_do_listxattr;\n    ops->removexattr = fs_do_removexattr;\n\n    return 0;\n}\n"
  },
  {
    "path": "src/fuse/fuse_wrapper.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_FUSE_WRAPPER_H\n#define _FCFS_FUSE_WRAPPER_H\n\n#ifndef FUSE_USE_VERSION\n#define FUSE_USE_VERSION 312\n#endif\n\n#include \"fastcommon/common_define.h\"\n#include \"fastcfs/api/fcfs_api.h\"\n#include \"fastcfs/api/fcfs_api_util.h\"\n#include \"fuse3/fuse_lowlevel.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern struct fuse_conn_info_opts *g_fuse_cinfo_opts;\n\n\tint fs_fuse_wrapper_init(struct fuse_lowlevel_ops *ops);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/fuse/getgroups.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <pwd.h>\n#include <grp.h>\n#include \"fastcommon/logger.h\"\n#include \"global.h\"\n#include \"getgroups.h\"\n\nstatic inline int get_last_id(const char *buff, const char *tag_str,\n        const int tag_len, const char **next)\n{\n    const char *start;\n    const char *last;\n\n    if ((start=strstr(buff, tag_str)) == NULL) {\n        *next = buff;\n        return -1;\n    }\n\n    start += tag_len;\n    if ((last=strchr(start, '\\n')) == NULL) {\n        *next = buff;\n        return -1;\n    }\n\n    *next = last;\n    do {\n        --last;\n    } while (last >= start && (*last >= '0' && *last <= '9'));\n    return strtol(last + 1, NULL, 10);\n}\n\nint fcfs_getgroups(const pid_t pid, const uid_t fsuid,\n        const gid_t fsgid, const int size, gid_t *list)\n{\n#define UID_TAG_STR   \"\\nUid:\"\n#define UID_TAG_LEN   (sizeof(UID_TAG_STR) - 1)\n#define GID_TAG_STR   \"\\nGid:\"\n#define GID_TAG_LEN   (sizeof(GID_TAG_STR) - 1)\n#define GROUPS_TAG_STR   \"\\nGroups:\"\n#define GROUPS_TAG_LEN   (sizeof(GROUPS_TAG_STR) - 1)\n\n    char filename[64];\n    char buff[1024];\n    const char *next;\n    struct passwd *user;\n    char *p;\n    char *end;\n    int fd;\n    int len;\n    int euid;\n    int egid;\n    gid_t val;\n    int count;\n\n    p = filename;\n    memcpy(p, \"/proc/\", 6);\n    p += 6;\n    p += fc_itoa(pid, p);\n    *p++ = '/';\n    memcpy(p, \"status\", 6);\n    p += 6;\n    *p = '\\0';\n    fd = open(filename, O_RDONLY);\n    if (fd < 0) {\n        return 0;\n    }\n\n    len = read(fd, buff, sizeof(buff));\n    close(fd);\n    if (len <= 0) {\n        return 0;\n    }\n\n    buff[len - 1] = '\\0';\n    euid = get_last_id(buff, UID_TAG_STR, UID_TAG_LEN, &next);\n    if ((egid=get_last_id(next, GID_TAG_STR, GID_TAG_LEN, &next)) < 0) {\n        egid = get_last_id(buff, GID_TAG_STR, GID_TAG_LEN, &next);\n    }\n    if (fsuid == euid && fsgid == egid) {\n        if ((p=strstr(next, GROUPS_TAG_STR)) == NULL) {\n            if ((p=strstr(buff, GROUPS_TAG_STR)) == NULL) {\n                return 0;\n            }\n        }\n\n        p += GROUPS_TAG_LEN;\n        count = 0;\n        while (count < size) {\n            while (*p == ' ' || *p == '\\t') {\n                ++p;\n            }\n            val = strtoul(p, &end, 10);\n            if (end == p) {\n                break;\n            }\n\n            if (val != fsgid) {\n                list[count++] = val;\n            }\n            p = end;\n        }\n    } else {\n        /*\n        logInfo(\"line: %d, fsuid: %d, fsgid: %d, euid: %d, egid: %d\",\n                __LINE__, fsuid, fsgid, euid, egid);\n                */\n        if ((user=getpwuid(fsuid)) == NULL) {\n            return 0;\n        }\n\n        count = size;\n        if (getgrouplist(user->pw_name, fsgid, list, &count) < 0) {\n            return 0;\n        }\n    }\n\n    return count;\n}\n\nint fcfs_get_groups(const pid_t pid, const uid_t fsuid,\n        const gid_t fsgid, char *buff)\n{\n    int count;\n    gid_t list[FDIR_MAX_USER_GROUP_COUNT];\n    const gid_t *group;\n    const gid_t *end;\n    char *p;\n\n    count = fcfs_getgroups(pid, fsuid, fsgid,\n            FDIR_MAX_USER_GROUP_COUNT, list);\n    if (count <= 0) {\n        count = 0;\n    } else if (count == 1 && list[0] == fsgid) {\n        count = 0;\n    } else {\n        end = list + count;\n        for (group=list, p=buff; group<end; group++) {\n            if (*group != fsgid) {\n                int2buff(*group, p);\n                p += 4;\n\n            }\n            //logInfo(\"%d. gid: %d\", (int)(group - list) + 1, *group);\n        }\n        count = (p - buff) / 4;\n    }\n    /*\n    logInfo(\"line: %d, count: %d, first gid: %d\", __LINE__,\n            count, count > 0 ? buff2int(buff) : -1);\n            */\n    return count;\n}\n"
  },
  {
    "path": "src/fuse/getgroups.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_GETGROUPS_H\n#define _FCFS_GETGROUPS_H\n\n#include \"fastcommon/common_define.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    int fcfs_getgroups(const pid_t pid, const uid_t fsuid,\n            const gid_t fsgid, const int size, gid_t *list);\n\n    int fcfs_get_groups(const pid_t pid, const uid_t fsuid,\n            const gid_t fsgid, char *buff);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/fuse/global.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <grp.h>\n#include <pwd.h>\n#include \"fastcommon/sched_thread.h\"\n#include \"fastcommon/system_info.h\"\n#include \"sf/sf_global.h\"\n#include \"sf/idempotency/client/client_channel.h\"\n#include \"fuse_wrapper.h\"\n#include \"global.h\"\n\n#define INI_FUSE_SECTION_NAME             \"FUSE\"\n#define INI_GROUPS_CACHE_SECTION_NAME     \"groups-cache\"\n\n#define FUSE_ALLOW_ALL_STR   \"all\"\n#define FUSE_ALLOW_ROOT_STR  \"root\"\n\n#define FUSE_MIN_SHARED_ALLOCATOR_COUNT             1\n#define FUSE_MAX_SHARED_ALLOCATOR_COUNT           100\n#define FUSE_DEFAULT_SHARED_ALLOCATOR_COUNT         7\n\n#define FUSE_MIN_HASHTABLE_SHARDING_COUNT           1\n#define FUSE_MAX_HASHTABLE_SHARDING_COUNT        1000\n#define FUSE_DEFAULT_HASHTABLE_SHARDING_COUNT     163\n\n#define FUSE_MIN_HASHTABLE_TOTAL_CAPACITY       10949\n#define FUSE_MAX_HASHTABLE_TOTAL_CAPACITY     1000000\n#define FUSE_DEFAULT_HASHTABLE_TOTAL_CAPACITY  175447\n\nFUSEGlobalVars g_fuse_global_vars = {{NULL, NULL}};\n\nstatic int load_fuse_config(IniFullContext *ini_ctx)\n{\n    string_t mountpoint;\n    char *writeback_cache;\n    char *allow_others;\n    int result;\n\n    get_kernel_version(&OS_KERNEL_VERSION);\n\n    ini_ctx->section_name = INI_FUSE_SECTION_NAME;\n    if (g_fuse_global_vars.nsmp.mountpoint == NULL) {\n        FC_SET_STRING_NULL(mountpoint);\n    } else {\n        FC_SET_STRING(mountpoint, g_fuse_global_vars.nsmp.mountpoint);\n    }\n    if ((result=fcfs_api_load_ns_mountpoint(ini_ctx,\n                    FCFS_API_DEFAULT_FASTDIR_SECTION_NAME,\n                    &g_fuse_global_vars.nsmp, &mountpoint, true)) != 0)\n    {\n        return result;\n    }\n\n    g_fuse_global_vars.max_threads = iniGetIntCorrectValue(ini_ctx,\n            \"max_threads\", 10, 1, 1024);\n    g_fuse_global_vars.max_idle_threads = iniGetIntCorrectValue(ini_ctx,\n            \"max_idle_threads\", g_fuse_global_vars.max_threads, 1, 1024);\n    g_fuse_global_vars.singlethread = iniGetBoolValue(ini_ctx->\n            section_name, \"singlethread\", ini_ctx->context, false);\n\n    g_fuse_global_vars.clone_fd = iniGetBoolValue(ini_ctx->\n            section_name, \"clone_fd\", ini_ctx->context, false);\n    if (g_fuse_global_vars.clone_fd) {\n        if (OS_KERNEL_VERSION.major < 4 || (OS_KERNEL_VERSION.major == 4 &&\n            OS_KERNEL_VERSION.minor < 2))\n        {\n            logWarning(\"file: \"__FILE__\", line: %d, \"\n                    \"kernel version %d.%d < 4.2, do NOT support \"\n                    \"FUSE feature clone_fd\", __LINE__,\n                    OS_KERNEL_VERSION.major, OS_KERNEL_VERSION.minor);\n            g_fuse_global_vars.clone_fd = false;\n        }\n    }\n\n    g_fuse_global_vars.auto_unmount = iniGetBoolValue(ini_ctx->\n            section_name, \"auto_unmount\", ini_ctx->context, false);\n    g_fuse_global_vars.read_only = iniGetBoolValue(ini_ctx->\n            section_name, \"read_only\", ini_ctx->context, false);\n\n    allow_others = iniGetStrValue(ini_ctx->section_name,\n            \"allow_others\", ini_ctx->context);\n    if (allow_others == NULL || *allow_others == '\\0') {\n        g_fuse_global_vars.allow_others = allow_none;\n    } else if (strcasecmp(allow_others, FUSE_ALLOW_ALL_STR) == 0) {\n        g_fuse_global_vars.allow_others = allow_all;\n    } else if (strcasecmp(allow_others, FUSE_ALLOW_ROOT_STR) == 0) {\n        g_fuse_global_vars.allow_others = allow_root;\n    } else {\n        g_fuse_global_vars.allow_others = allow_none;\n    }\n\n    g_fuse_global_vars.attribute_timeout = iniGetDoubleValue(ini_ctx->\n            section_name, \"attribute_timeout\", ini_ctx->context,\n            FCFS_FUSE_DEFAULT_ATTRIBUTE_TIMEOUT);\n\n    g_fuse_global_vars.entry_timeout = iniGetDoubleValue(ini_ctx->\n            section_name, \"entry_timeout\", ini_ctx->context,\n            FCFS_FUSE_DEFAULT_ENTRY_TIMEOUT);\n\n    g_fuse_global_vars.xattr_enabled = iniGetBoolValue(ini_ctx->\n            section_name, \"xattr_enabled\", ini_ctx->context, false);\n\n    writeback_cache = iniGetStrValue(ini_ctx->section_name,\n            \"writeback_cache\", ini_ctx->context);\n    if (writeback_cache == NULL) {\n        g_fuse_global_vars.writeback_cache = (OS_KERNEL_VERSION.major > 3 ||\n                (OS_KERNEL_VERSION.major == 3 &&\n                 OS_KERNEL_VERSION.minor >= 15));\n    } else {\n        g_fuse_global_vars.writeback_cache = FAST_INI_STRING_IS_TRUE(\n                writeback_cache);\n        if (g_fuse_global_vars.writeback_cache) {\n            if (OS_KERNEL_VERSION.major < 3 || (OS_KERNEL_VERSION.major == 3 &&\n                        OS_KERNEL_VERSION.minor < 15))\n            {\n                logWarning(\"file: \"__FILE__\", line: %d, \"\n                        \"kernel version %d.%d < 3.15, do NOT support \"\n                        \"FUSE feature writeback_cache\", __LINE__,\n                        OS_KERNEL_VERSION.major, OS_KERNEL_VERSION.minor);\n                g_fuse_global_vars.writeback_cache = false;\n            }\n        }\n    }\n\n    g_fuse_global_vars.kernel_cache = iniGetBoolValue(ini_ctx->\n            section_name, \"kernel_cache\", ini_ctx->context, true);\n    ADDITIONAL_GROUPS_ENABLED = iniGetBoolValue(ini_ctx->section_name,\n            \"groups_enabled\", ini_ctx->context, true);\n    return 0;\n}\n\nstatic void load_additional_groups_config(IniFullContext *ini_ctx)\n{\n    ini_ctx->section_name = INI_GROUPS_CACHE_SECTION_NAME;\n    GROUPS_CACHE_ENABLED = iniGetBoolValue(ini_ctx->\n            section_name, \"enabled\", ini_ctx->context, true);\n\n    GROUPS_CACHE_TIMEOUT = iniGetIntCorrectValue(ini_ctx,\n            \"timeout\", 300, 1, 1e8);\n\n    GROUPS_CACHE_ALLOCATOR_COUNT = iniGetIntCorrectValueEx(\n            ini_ctx, \"shared_allocator_count\",\n            FUSE_DEFAULT_SHARED_ALLOCATOR_COUNT,\n            FUSE_MIN_SHARED_ALLOCATOR_COUNT,\n            FUSE_MAX_SHARED_ALLOCATOR_COUNT, true);\n\n    GROUPS_CACHE_SHARDING_COUNT = iniGetIntCorrectValue(\n            ini_ctx, \"hashtable_sharding_count\",\n            FUSE_DEFAULT_HASHTABLE_SHARDING_COUNT,\n            FUSE_MIN_HASHTABLE_SHARDING_COUNT,\n            FUSE_MAX_HASHTABLE_SHARDING_COUNT);\n\n    GROUPS_CACHE_HTABLE_CAPACITY = iniGetIntCorrectValue(\n            ini_ctx, \"hashtable_total_capacity\",\n            FUSE_DEFAULT_HASHTABLE_TOTAL_CAPACITY,\n            FUSE_MIN_HASHTABLE_TOTAL_CAPACITY,\n            FUSE_MAX_HASHTABLE_TOTAL_CAPACITY);\n\n    GROUPS_CACHE_ELEMENT_LIMIT = iniGetIntCorrectValue(ini_ctx,\n            \"element_limit\", 64 * 1024, 16 * 1024, 1024 * 1024);\n}\n\nstatic void additional_groups_config_to_string(char *buff, const int size)\n{\n    if (!ADDITIONAL_GROUPS_ENABLED) {\n        snprintf(buff, size, \"groups_enabled: 0\");\n        return;\n    }\n\n    if (GROUPS_CACHE_ENABLED) {\n        snprintf(buff, size, \"groups_enabled: 1, \"\n                \"groups-cache {enabled: 1, timeout: %d, \"\n                \"shared_allocator_count: %d, \"\n                \"hashtable_sharding_count: %d, \"\n                \"hashtable_total_capacity: %d, \"\n                \"element_limit: %d}\",\n                GROUPS_CACHE_TIMEOUT, GROUPS_CACHE_ALLOCATOR_COUNT,\n                GROUPS_CACHE_SHARDING_COUNT, GROUPS_CACHE_HTABLE_CAPACITY,\n                GROUPS_CACHE_ELEMENT_LIMIT);\n    } else {\n        snprintf(buff, size, \"groups_enabled: 1, \"\n                \"groups-cache {enabled: 0}\");\n    }\n}\n\nstatic const char *get_allow_others_caption(\n        const FUSEAllowOthersMode allow_others)\n{\n    switch (allow_others) {\n        case allow_all:\n            return FUSE_ALLOW_ALL_STR;\n        case allow_root:\n            return FUSE_ALLOW_ROOT_STR;\n        default:\n            return \"\";\n    }\n}\n\nint fcfs_fuse_global_init(const char *config_filename)\n{\n    const bool publish = true;\n    int result;\n    BufferInfo sf_idempotency_config;\n    char buff[256];\n    char rdma_busy_polling[128];\n    char owner_config[2 * NAME_MAX + 64];\n    char additional_groups_config[256];\n    char max_threads_buff[64];\n    IniContext iniContext;\n    IniFullContext ini_ctx;\n\n    if ((result=iniLoadFromFile(config_filename, &iniContext)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                __LINE__, config_filename, result);\n        return result;\n    }\n\n    FAST_INI_SET_FULL_CTX_EX(ini_ctx, config_filename,\n            FCFS_API_DEFAULT_FASTDIR_SECTION_NAME, &iniContext);\n    do {\n        if ((result=load_fuse_config(&ini_ctx)) != 0) {\n            break;\n        }\n        if (ADDITIONAL_GROUPS_ENABLED) {\n            load_additional_groups_config(&ini_ctx);\n        }\n\n        if ((result=fcfs_api_pooled_init1_with_auth(\n                        g_fuse_global_vars.nsmp.ns,\n                        &ini_ctx, publish)) != 0)\n        {\n            break;\n        }\n\n        if ((result=fcfs_api_check_mountpoint1(config_filename,\n                        g_fuse_global_vars.nsmp.mountpoint)) != 0)\n        {\n            break;\n        }\n\n        if ((result=fcfs_api_load_idempotency_config(\n                        NULL, &ini_ctx)) != 0)\n        {\n            break;\n        }\n    } while (0);\n\n    iniFreeContext(&iniContext);\n    if (result != 0) {\n        return result;\n    }\n\n    sf_idempotency_config.buff = buff;\n    sf_idempotency_config.alloc_size = sizeof(buff);\n    fcfs_api_log_client_common_configs(&g_fcfs_api_ctx,\n            FCFS_API_DEFAULT_FASTDIR_SECTION_NAME,\n            FCFS_API_DEFAULT_FASTSTORE_SECTION_NAME,\n            &sf_idempotency_config, owner_config);\n\n#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 12)\n    sprintf(max_threads_buff, \"max_idle_threads: %d\",\n            g_fuse_global_vars.max_idle_threads);\n#else\n    sprintf(max_threads_buff, \"max_threads: %d, max_idle_threads: %d\",\n            g_fuse_global_vars.max_threads, g_fuse_global_vars.\n            max_idle_threads);\n#endif\n\n    additional_groups_config_to_string(additional_groups_config,\n            sizeof(additional_groups_config));\n\n    if (g_fcfs_api_ctx.rdma.enabled) {\n        sprintf(rdma_busy_polling, \"rdma busy polling: %s, \",\n                g_fcfs_api_ctx.rdma.busy_polling ? \"true\" : \"false\");\n    } else {\n        *rdma_busy_polling = '\\0';\n    }\n\n    logInfo(\"FastCFS V%d.%d.%d, FUSE library version \"\n            \"{compile: %d.%d, runtime: %s}, %s\"\n            \"FastDIR namespace: %s, %sFUSE mountpoint: %s, \"\n            \"%s, singlethread: %d, clone_fd: %d, \"\n            \"%s, allow_others: %s, auto_unmount: %d, read_only: %d, \"\n            \"attribute_timeout: %.1fs, entry_timeout: %.1fs, \"\n            \"xattr_enabled: %d, writeback_cache: %d, kernel_cache: %d, %s\",\n            g_fcfs_global_vars.version.major,\n            g_fcfs_global_vars.version.minor,\n            g_fcfs_global_vars.version.patch,\n            FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION,\n            fuse_pkgversion(), rdma_busy_polling,\n            g_fuse_global_vars.nsmp.ns,\n            sf_idempotency_config.buff,\n            g_fuse_global_vars.nsmp.mountpoint,\n            owner_config, g_fuse_global_vars.singlethread,\n            g_fuse_global_vars.clone_fd, max_threads_buff,\n            get_allow_others_caption(g_fuse_global_vars.allow_others),\n            g_fuse_global_vars.auto_unmount,\n            g_fuse_global_vars.read_only,\n            g_fuse_global_vars.attribute_timeout,\n            g_fuse_global_vars.entry_timeout,\n            g_fuse_global_vars.xattr_enabled,\n            g_fuse_global_vars.writeback_cache,\n            g_fuse_global_vars.kernel_cache,\n            additional_groups_config);\n\n    return 0;\n}\n"
  },
  {
    "path": "src/fuse/global.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_FUSE_GLOBAL_H\n#define _FCFS_FUSE_GLOBAL_H\n\n#include \"fastcfs/api/fcfs_api.h\"\n#include \"common/fcfs_global.h\"\n\n#define FCFS_FUSE_DEFAULT_ATTRIBUTE_TIMEOUT 1.0\n#define FCFS_FUSE_DEFAULT_ENTRY_TIMEOUT     1.0\n\ntypedef enum {\n    allow_none,\n    allow_all,\n    allow_root\n} FUSEAllowOthersMode;\n\ntypedef struct {\n    FCFSAPINSMountpointHolder nsmp;\n    bool singlethread;\n    bool clone_fd;\n    bool auto_unmount;\n    bool read_only;\n    bool xattr_enabled;\n    bool writeback_cache;\n    bool kernel_cache;\n    bool groups_enabled;\n    struct {\n        bool enabled;\n        int timeout;\n        int sharding_count;\n        int htable_capacity;\n        int allocator_count;\n        int element_limit;\n    } groups_cache;\n    int max_idle_threads;\n    int max_threads;      //libfuse >= 3.12\n    double attribute_timeout;\n    double entry_timeout;\n    FUSEAllowOthersMode allow_others;\n    Version kernel_version;\n} FUSEGlobalVars;\n\n#define OS_KERNEL_VERSION g_fuse_global_vars.kernel_version\n\n#define ADDITIONAL_GROUPS_ENABLED    g_fuse_global_vars.groups_enabled\n#define GROUPS_CACHE_ENABLED         g_fuse_global_vars.groups_cache.enabled\n#define GROUPS_CACHE_TIMEOUT         g_fuse_global_vars.groups_cache.timeout\n#define GROUPS_CACHE_SHARDING_COUNT  g_fuse_global_vars.groups_cache.sharding_count\n#define GROUPS_CACHE_HTABLE_CAPACITY g_fuse_global_vars.groups_cache.htable_capacity\n#define GROUPS_CACHE_ALLOCATOR_COUNT g_fuse_global_vars.groups_cache.allocator_count\n#define GROUPS_CACHE_ELEMENT_LIMIT   g_fuse_global_vars.groups_cache.element_limit\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern FUSEGlobalVars g_fuse_global_vars;\n\n\tint fcfs_fuse_global_init(const char *config_filename);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/fuse/groups_htable.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"groups_htable.h\"\n#include \"global.h\"\n\n#define FCFS_GROUP_FIXED_COUNT  16\n\ntypedef struct {\n    SFShardingHashEntry hentry;  //must be the first\n    time_t expires;\n    struct {\n        short alloc_size;\n        short count;\n        char holder[FCFS_GROUP_FIXED_COUNT * 4];\n        char *list;  //4 bytes network order integer array\n    } groups;\n} FCFSGroupHashEntry;\n\ntypedef struct {\n    int *count;\n    char *list;\n} FCFSGroupOpContext;\n\nstatic SFHtableShardingContext fcfs_group_ctx;\n\nstatic int groups_htable_insert_callback(SFShardingHashEntry *he,\n        void *arg, const bool new_create)\n{\n    FCFSGroupHashEntry *entry;\n    FCFSGroupOpContext *op_ctx;\n\n    entry = (FCFSGroupHashEntry *)he;\n    op_ctx = (FCFSGroupOpContext *)arg;\n\n    if (entry->groups.alloc_size == 0) {\n        entry->groups.alloc_size = FCFS_GROUP_FIXED_COUNT;\n        entry->groups.list = entry->groups.holder;\n    }\n\n    if (*(op_ctx->count) > entry->groups.alloc_size) {\n        int new_size;\n        char *new_list;\n\n        new_size = 2 * entry->groups.alloc_size;\n        while (new_size < *(op_ctx->count)) {\n            new_size *= 2;\n        }\n\n        if ((new_list=fc_malloc(new_size * 4)) == NULL) {\n            return ENOMEM;\n        }\n\n        if (entry->groups.list != entry->groups.holder) {\n            free(entry->groups.list);\n        }\n\n        entry->groups.alloc_size = new_size;\n        entry->groups.list = new_list;\n    }\n\n    entry->groups.count = *(op_ctx->count);\n    if ((*op_ctx->count) > 0) {\n        memcpy(entry->groups.list, op_ctx->list,\n                entry->groups.count * 4);\n    }\n    entry->expires = g_current_time + GROUPS_CACHE_TIMEOUT;\n    return 0;\n}\n\nstatic void *groups_htable_find_callback(SFShardingHashEntry *he, void *arg)\n{\n    FCFSGroupHashEntry *entry;\n    FCFSGroupOpContext *op_ctx;\n\n    entry = (FCFSGroupHashEntry *)he;\n    op_ctx = (FCFSGroupOpContext *)arg;\n    if (entry->expires >= g_current_time) {\n        *(op_ctx->count) = entry->groups.count;\n        if (entry->groups.count > 0) {\n            memcpy(op_ctx->list, entry->groups.list,\n                    entry->groups.count * 4);\n        }\n        return entry;\n    } else {\n        *(op_ctx->count) = 0;\n        return NULL;\n    }\n}\n\nint fcfs_groups_htable_init()\n{\n    int64_t min_ttl_ms;\n    int64_t max_ttl_ms;\n    const double low_water_mark_ratio = 0.10;\n\n    if (GROUPS_CACHE_TIMEOUT <= 30) {\n        min_ttl_ms = 1 * 1000;\n        max_ttl_ms = 30 * 1000;\n    } else if (GROUPS_CACHE_TIMEOUT <= 300) {\n        min_ttl_ms = 3 * 1000;\n        max_ttl_ms = 300 * 1000;\n    } else {\n        min_ttl_ms = 5 * 1000;\n        max_ttl_ms = GROUPS_CACHE_TIMEOUT * 1000LL;\n    }\n    return sf_sharding_htable_init_ex(&fcfs_group_ctx,\n            sf_sharding_htable_key_ids_two, groups_htable_insert_callback,\n            groups_htable_find_callback, NULL, NULL, GROUPS_CACHE_SHARDING_COUNT,\n            GROUPS_CACHE_HTABLE_CAPACITY, GROUPS_CACHE_ALLOCATOR_COUNT,\n            sizeof(FCFSGroupHashEntry), GROUPS_CACHE_ELEMENT_LIMIT,\n            min_ttl_ms, max_ttl_ms, low_water_mark_ratio);\n}\n\n#define FCFS_GROUP_HTABLE_SET_KEY(key, pid, uid, gid) \\\n    key.id1 = ((int64_t)uid << 32) | gid; \\\n    key.id2 = pid\n\nint fcfs_groups_htable_insert(const pid_t pid, const uid_t uid,\n            const gid_t gid, const int count, const char *list)\n{\n    SFTwoIdsHashKey key;\n    FCFSGroupOpContext op_ctx;\n\n    FCFS_GROUP_HTABLE_SET_KEY(key, pid, uid, gid);\n    op_ctx.count = (int *)&count;\n    op_ctx.list = (char *)list;\n    return sf_sharding_htable_insert(&fcfs_group_ctx, &key, &op_ctx);\n}\n\nint fcfs_groups_htable_find(const pid_t pid, const uid_t uid,\n        const gid_t gid, int *count, char *list)\n{\n    SFTwoIdsHashKey key;\n    FCFSGroupOpContext op_ctx;\n\n    FCFS_GROUP_HTABLE_SET_KEY(key, pid, uid, gid);\n    op_ctx.count = count;\n    op_ctx.list = list;\n    if (sf_sharding_htable_find(&fcfs_group_ctx, &key, &op_ctx) != NULL) {\n        return 0;\n    } else {\n        return ENOENT;\n    }\n}\n"
  },
  {
    "path": "src/fuse/groups_htable.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_GROUPS_HTABLE_H\n#define _FCFS_GROUPS_HTABLE_H\n\n#include \"sf/sf_sharding_htable.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    int fcfs_groups_htable_init();\n\n    int fcfs_groups_htable_insert(const pid_t pid, const uid_t uid,\n            const gid_t gid, const int count, const char *list);\n\n    int fcfs_groups_htable_find(const pid_t pid, const uid_t uid,\n            const gid_t gid, int *count, char *list);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/java/jni/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nPREFIX = $(TARGET_PREFIX)\nINC_PATH = -I/usr/local/include $(INCLUDES)\nLIB_PATH = -lfcfsapi -lfdirclient -lfsapi -lfsclient \\\n           -lfcfsauthclient -lfastcommon -lserverframe\n\nSHARED_OBJS = com_fastken_fcfs_FCFSPosixAPI.lo  \\\n              com_fastken_fcfs_FCFSDirectory.lo \\\n              com_fastken_fcfs_FCFSFile.lo      \\\n              global.lo common.lo\n\nALL_OBJS = $(SHARED_OBJS)\n\nALL_PRGS =\nSHARED_LIBS = libfcfsjni.so\nALL_LIBS = $(SHARED_LIBS)\n\nall: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)\nlibfcfsjni.so: $(SHARED_OBJS)\n\t$(COMPILE) -o $@ -shared $(SHARED_OBJS) $(LIB_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n.c.lo:\n\t$(COMPILE) -c -fPIC -o $@ $<  $(INC_PATH)\ninstall:\n\tmkdir -p $(PREFIX)/lib64\n\tmkdir -p $(PREFIX)/lib\n\tinstall -m 755 $(SHARED_LIBS) $(PREFIX)/lib64\n\tinstall -m 755 $(SHARED_LIBS) $(PREFIX)/lib\nclean:\n\trm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)\n"
  },
  {
    "path": "src/java/jni/com_fastken_fcfs_FCFSConstants.h",
    "content": "/* DO NOT EDIT THIS FILE - it is machine generated */\n#include <jni.h>\n/* Header for class com_fastken_fcfs_FCFSConstants */\n\n#ifndef _Included_com_fastken_fcfs_FCFSConstants\n#define _Included_com_fastken_fcfs_FCFSConstants\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#undef com_fastken_fcfs_FCFSConstants_X_OK\n#define com_fastken_fcfs_FCFSConstants_X_OK 1L\n#undef com_fastken_fcfs_FCFSConstants_W_OK\n#define com_fastken_fcfs_FCFSConstants_W_OK 2L\n#undef com_fastken_fcfs_FCFSConstants_R_OK\n#define com_fastken_fcfs_FCFSConstants_R_OK 4L\n#undef com_fastken_fcfs_FCFSConstants_XATTR_CREATE\n#define com_fastken_fcfs_FCFSConstants_XATTR_CREATE 1L\n#undef com_fastken_fcfs_FCFSConstants_XATTR_REPLACE\n#define com_fastken_fcfs_FCFSConstants_XATTR_REPLACE 2L\n#undef com_fastken_fcfs_FCFSConstants_SEEK_SET\n#define com_fastken_fcfs_FCFSConstants_SEEK_SET 0L\n#undef com_fastken_fcfs_FCFSConstants_SEEK_CUR\n#define com_fastken_fcfs_FCFSConstants_SEEK_CUR 1L\n#undef com_fastken_fcfs_FCFSConstants_SEEK_END\n#define com_fastken_fcfs_FCFSConstants_SEEK_END 2L\n#undef com_fastken_fcfs_FCFSConstants_O_RDONLY\n#define com_fastken_fcfs_FCFSConstants_O_RDONLY 0L\n#undef com_fastken_fcfs_FCFSConstants_O_WRONLY\n#define com_fastken_fcfs_FCFSConstants_O_WRONLY 1L\n#undef com_fastken_fcfs_FCFSConstants_O_RDWR\n#define com_fastken_fcfs_FCFSConstants_O_RDWR 2L\n#undef com_fastken_fcfs_FCFSConstants_O_CREAT\n#define com_fastken_fcfs_FCFSConstants_O_CREAT 64L\n#undef com_fastken_fcfs_FCFSConstants_O_EXCL\n#define com_fastken_fcfs_FCFSConstants_O_EXCL 128L\n#undef com_fastken_fcfs_FCFSConstants_O_TRUNC\n#define com_fastken_fcfs_FCFSConstants_O_TRUNC 512L\n#undef com_fastken_fcfs_FCFSConstants_O_APPEND\n#define com_fastken_fcfs_FCFSConstants_O_APPEND 1024L\n#undef com_fastken_fcfs_FCFSConstants_O_NOFOLLOW\n#define com_fastken_fcfs_FCFSConstants_O_NOFOLLOW 131072L\n#undef com_fastken_fcfs_FCFSConstants_O_CLOEXEC\n#define com_fastken_fcfs_FCFSConstants_O_CLOEXEC 524288L\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "src/java/jni/com_fastken_fcfs_FCFSDirectory.c",
    "content": "#include \"fastcfs/api/std/posix_api.h\"\n#include \"global.h\"\n#include \"common.h\"\n#include \"com_fastken_fcfs_FCFSDirectory.h\"\n\njobject JNICALL Java_com_fastken_fcfs_FCFSDirectory_next\n  (JNIEnv *env, jobject obj)\n{\n    long handler;\n    DIR *dir;\n    struct dirent *dirent;\n    jstring name;\n\n    handler = (*env)->CallLongMethod(env, obj,\n            g_fcfs_jni_global_vars.dir.getHandler);\n    if (handler == 0) {\n        fcfs_jni_throw_null_pointer_exception(env);\n        return NULL;\n    }\n\n    dir = (DIR *)handler;\n    if ((dirent=fcfs_readdir(dir)) == NULL) {\n        return NULL;\n    }\n\n    name = (*env)->NewStringUTF(env, dirent->d_name);\n    return (*env)->NewObject(env, g_fcfs_jni_global_vars.dirent.clazz,\n            g_fcfs_jni_global_vars.dirent.constructor2, dirent->d_ino, name);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSDirectory_seek\n  (JNIEnv *env, jobject obj, jlong loc)\n{\n    long handler;\n    DIR *dir;\n\n    handler = (*env)->CallLongMethod(env, obj,\n            g_fcfs_jni_global_vars.dir.getHandler);\n    if (handler == 0) {\n        fcfs_jni_throw_null_pointer_exception(env);\n        return;\n    }\n\n    dir = (DIR *)handler;\n    fcfs_seekdir(dir, loc);\n}\n\njlong JNICALL Java_com_fastken_fcfs_FCFSDirectory_tell\n  (JNIEnv *env, jobject obj)\n{\n    long handler;\n    DIR *dir;\n\n    handler = (*env)->CallLongMethod(env, obj,\n            g_fcfs_jni_global_vars.dir.getHandler);\n    if (handler == 0) {\n        fcfs_jni_throw_null_pointer_exception(env);\n        return -1;\n    }\n\n    dir = (DIR *)handler;\n    return fcfs_telldir(dir);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSDirectory_rewind\n  (JNIEnv *env, jobject obj)\n{\n    long handler;\n    DIR *dir;\n\n    handler = (*env)->CallLongMethod(env, obj,\n            g_fcfs_jni_global_vars.dir.getHandler);\n    if (handler == 0) {\n        fcfs_jni_throw_null_pointer_exception(env);\n        return;\n    }\n\n    dir = (DIR *)handler;\n    fcfs_rewinddir(dir);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSDirectory_close\n  (JNIEnv *env, jobject obj)\n{\n    long handler;\n    DIR *dir;\n\n    handler = (*env)->CallLongMethod(env, obj,\n            g_fcfs_jni_global_vars.dir.getHandler);\n    if (handler == 0) {\n        fcfs_jni_throw_null_pointer_exception(env);\n        return;\n    }\n\n    dir = (DIR *)handler;\n    fcfs_closedir(dir);\n    (*env)->CallVoidMethod(env, obj, g_fcfs_jni_global_vars.\n            dir.setHandler, 0);\n}\n"
  },
  {
    "path": "src/java/jni/com_fastken_fcfs_FCFSDirectory.h",
    "content": "/* DO NOT EDIT THIS FILE - it is machine generated */\n#include <jni.h>\n/* Header for class com_fastken_fcfs_FCFSDirectory */\n\n#ifndef _Included_com_fastken_fcfs_FCFSDirectory\n#define _Included_com_fastken_fcfs_FCFSDirectory\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/*\n * Class:     com_fastken_fcfs_FCFSDirectory\n * Method:    doInit\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_doInit\n  (JNIEnv *, jclass);\n\n/*\n * Class:     com_fastken_fcfs_FCFSDirectory\n * Method:    next\n * Signature: ()Lcom/fastken/fcfs/FCFSDirectory/Entry;\n */\nJNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSDirectory_next\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSDirectory\n * Method:    seek\n * Signature: (J)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_seek\n  (JNIEnv *, jobject, jlong);\n\n/*\n * Class:     com_fastken_fcfs_FCFSDirectory\n * Method:    tell\n * Signature: ()J\n */\nJNIEXPORT jlong JNICALL Java_com_fastken_fcfs_FCFSDirectory_tell\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSDirectory\n * Method:    rewind\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_rewind\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSDirectory\n * Method:    close\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_close\n  (JNIEnv *, jobject);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "src/java/jni/com_fastken_fcfs_FCFSFile.c",
    "content": "\n#include \"fastcfs/api/std/posix_api.h\"\n#include \"global.h\"\n#include \"common.h\"\n#include \"com_fastken_fcfs_FCFSFile.h\"\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_close\n  (JNIEnv *env, jobject obj)\n{\n    int fd;\n\n    fd = (*env)->CallIntMethod(env, obj, g_fcfs_jni_global_vars.file.getFD);\n    if (fd < 0) {\n        return;\n    }\n\n    fcfs_close(fd);\n    (*env)->CallVoidMethod(env, obj, g_fcfs_jni_global_vars.file.setFD, -1);\n}\n\n#define FILE_OBJ_FETCH_FD(retval)  \\\n    int fd;  \\\n    \\\n    fd = (*env)->CallIntMethod(env, obj, g_fcfs_jni_global_vars.file.getFD); \\\n    if (fd < 0) {  \\\n        fcfs_jni_throw_null_pointer_exception(env); \\\n        return retval; \\\n    } \\\n\nstatic inline void throw_file_exception(JNIEnv *env,\n        const int fd, const int err_no)\n{\n    FCFSPosixAPIFileInfo *handle;\n\n    if ((handle=fcfs_fd_manager_get(fd)) == NULL) {\n        fcfs_jni_throw_exception(env, strerror(err_no));\n    } else {\n        fcfs_jni_throw_filesystem_exception(env, handle->filename.str, err_no);\n    }\n}\n\njobject JNICALL Java_com_fastken_fcfs_FCFSFile_stat\n  (JNIEnv *env, jobject obj)\n{\n    struct stat stat;\n\n    FILE_OBJ_FETCH_FD(NULL);\n    if (fcfs_fstat(fd, &stat) != 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : ENOENT);\n        return NULL;\n    }\n\n    return (*env)->NewObject(env, g_fcfs_jni_global_vars.fstat.clazz,\n            g_fcfs_jni_global_vars.fstat.constructor10, stat.st_ino,\n            stat.st_mode, stat.st_nlink, stat.st_uid, stat.st_gid,\n            stat.st_rdev, stat.st_size, (jlong)stat.st_atime * 1000LL,\n            (jlong)stat.st_mtime * 1000LL, (jlong)stat.st_ctime * 1000LL);\n}\n\njobject JNICALL Java_com_fastken_fcfs_FCFSFile_statvfs\n  (JNIEnv *env, jobject obj)\n{\n    struct statvfs stat;\n    SFSpaceStat space;\n\n    FILE_OBJ_FETCH_FD(NULL);\n\n    if (fcfs_fstatvfs(fd, &stat) != 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : ENOENT);\n        return NULL;\n    }\n\n    space.total = (int64_t)stat.f_blocks * stat.f_bsize;\n    space.avail = (int64_t)stat.f_bavail * stat.f_bsize;\n    space.used = space.total - space.avail;\n    return (*env)->NewObject(env, g_fcfs_jni_global_vars.statvfs.clazz,\n            g_fcfs_jni_global_vars.statvfs.constructor6, space.total,\n            space.avail, space.used, stat.f_files, stat.f_favail,\n            stat.f_files - stat.f_favail);\n}\n\njbyteArray JNICALL Java_com_fastken_fcfs_FCFSFile_getxattr\n  (JNIEnv *env, jobject obj, jstring jname)\n{\n    jboolean *isCopy = NULL;\n    const char *name;\n    char holder[4 * 1024];\n    char *buff;\n    int result;\n    int size;\n    int length;\n    jbyteArray value;\n\n    FILE_OBJ_FETCH_FD(NULL);\n    name = (*env)->GetStringUTFChars(env, jname, isCopy);\n\n    buff = holder;\n    size = sizeof(holder);\n    if ((length=fcfs_fgetxattr(fd, name, buff, size)) < 0) {\n        result = errno != 0 ? errno : ENOENT;\n        if (result == EOVERFLOW) {\n            if ((size=fcfs_fgetxattr(fd, name, buff, 0)) < 0) {\n                result = errno != 0 ? errno : ENOENT;\n            } else {\n                if ((buff=malloc(size)) == NULL) {\n                    result = ENOMEM;\n                } else {\n                    if ((length=fcfs_fgetxattr(fd, name, buff, size)) < 0) {\n                        result = errno != 0 ? errno : ENOENT;\n                    } else {\n                        result = 0;\n                    }\n                }\n            }\n        }\n\n        if (result != 0) {\n            throw_file_exception(env, fd, result);\n            (*env)->ReleaseStringUTFChars(env, jname, name);\n            return NULL;\n        }\n    }\n\n    (*env)->ReleaseStringUTFChars(env, jname, name);\n    value = (*env)->NewByteArray(env, length);\n    (*env)->SetByteArrayRegion(env, value, 0, length,\n            (const jbyte *)buff);\n    if (buff != holder) {\n        free(buff);\n    }\n\n    return value;\n}\n\njobject JNICALL Java_com_fastken_fcfs_FCFSFile_listxattr\n  (JNIEnv *env, jobject obj)\n{\n    char holder[4 * 1024];\n    char *buff;\n    int result;\n    int size;\n    int length;\n    jobject list_obj;\n\n    FILE_OBJ_FETCH_FD(NULL);\n    buff = holder;\n    size = sizeof(holder);\n    if ((length=fcfs_flistxattr(fd, buff, size)) < 0) {\n        result = errno != 0 ? errno : ENOENT;\n        if (result == EOVERFLOW) {\n            if ((size=fcfs_flistxattr(fd, buff, 0)) < 0) {\n                result = errno != 0 ? errno : ENOENT;\n            } else {\n                if ((buff=malloc(size)) == NULL) {\n                    result = ENOMEM;\n                } else {\n                    if ((length=fcfs_flistxattr(fd, buff, size)) < 0) {\n                        result = errno != 0 ? errno : ENOENT;\n                    } else {\n                        result = 0;\n                    }\n                }\n            }\n        }\n\n        if (result != 0) {\n            throw_file_exception(env, fd, result);\n            return NULL;\n        }\n    }\n\n    list_obj = fcfs_jni_convert_to_list(env, buff, length);\n    if (buff != holder) {\n        free(buff);\n    }\n\n    return list_obj;\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_sync\n  (JNIEnv *env, jobject obj)\n{\n    FILE_OBJ_FETCH_FD();\n\n    if (fcfs_fsync(fd) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_datasync\n  (JNIEnv *env, jobject obj)\n{\n    FILE_OBJ_FETCH_FD();\n\n    if (fcfs_fdatasync(fd) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n}\n\n#define FILE_CHECK_ARRAY_BOUNDS_EX(b, off, len, size, retval) \\\n    do { \\\n        size = (*env)->GetArrayLength(env, b); \\\n        if (off < 0 || off >= size) { \\\n            fcfs_jni_throw_out_of_bounds_exception(env, off); \\\n            return retval; \\\n        } \\\n        if (off + len > size) { \\\n            fcfs_jni_throw_out_of_bounds_exception(env, off + len); \\\n            return retval; \\\n        } \\\n    } while (0)\n\n#define FILE_CHECK_ARRAY_BOUNDS(b, off, len, size)   \\\n        FILE_CHECK_ARRAY_BOUNDS_EX(b, off, len, size, )\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_write\n  (JNIEnv *env, jobject obj, jbyteArray bs, jint off, jint len)\n{\n    jbyte *ba;\n    jsize size;\n\n    FILE_OBJ_FETCH_FD();\n    FILE_CHECK_ARRAY_BOUNDS(bs, off, len, size);\n    ba = (*env)->GetByteArrayElements(env, bs, NULL);\n    if (fcfs_write(fd, ba + off, len) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseByteArrayElements(env, bs, ba, 0);\n}\n\njint JNICALL Java_com_fastken_fcfs_FCFSFile_read\n  (JNIEnv *env, jobject obj, jbyteArray bs, jint off, jint len)\n{\n    jbyte *ba;\n    jsize size;\n    int bytes;\n\n    FILE_OBJ_FETCH_FD(-1);\n    FILE_CHECK_ARRAY_BOUNDS_EX(bs, off, len, size, -1);\n    ba = (*env)->GetByteArrayElements(env, bs, NULL);\n    if ((bytes=fcfs_read(fd, ba + off, len)) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseByteArrayElements(env, bs, ba, 0);\n    return bytes;\n}\n\njlong JNICALL Java_com_fastken_fcfs_FCFSFile_lseek\n  (JNIEnv *env, jobject obj, jlong offset, jint whence)\n{\n    jlong position;\n\n    FILE_OBJ_FETCH_FD(-1);\n    if ((position=fcfs_lseek(fd, offset, whence)) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n    return position;\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_allocate\n  (JNIEnv *env, jobject obj, jint mode, jlong offset, jlong length)\n{\n    FILE_OBJ_FETCH_FD();\n    if (fcfs_fallocate(fd, mode, offset, length) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_truncate\n  (JNIEnv *env, jobject obj, jlong length)\n{\n    FILE_OBJ_FETCH_FD();\n    if (fcfs_ftruncate(fd, length) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n}\n\njboolean JNICALL Java_com_fastken_fcfs_FCFSFile_lock\n  (JNIEnv *env, jobject obj, jlong position, jlong length,\n   jboolean shared, jboolean blocked)\n{\n    struct flock lock;\n    int result;\n\n    FILE_OBJ_FETCH_FD(false);\n    lock.l_type = (shared ? F_RDLCK : F_WRLCK);\n    lock.l_whence = SEEK_SET;\n    lock.l_start = position;\n    lock.l_len = length;\n    lock.l_pid = getpid();\n    if (fcfs_fcntl(fd, (blocked ? F_SETLKW : F_SETLK), &lock) < 0) {\n        result = errno != 0 ? errno : EIO;\n        if (blocked || result != EWOULDBLOCK) {\n            throw_file_exception(env, fd, result);\n        }\n        return false;\n    }\n    return true;\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_unlock\n  (JNIEnv *env, jobject obj, jlong position, jlong length)\n{\n    struct flock lock;\n\n    FILE_OBJ_FETCH_FD();\n    lock.l_type = F_UNLCK;\n    lock.l_whence = SEEK_SET;\n    lock.l_start = position;\n    lock.l_len = length;\n    lock.l_pid = getpid();\n    if (fcfs_fcntl(fd, F_SETLK, &lock) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_utimes\n  (JNIEnv *env, jobject obj, jlong atime, jlong mtime)\n{\n    struct timeval times[2];\n\n    FILE_OBJ_FETCH_FD();\n    times[0].tv_sec = atime / 1000;\n    times[0].tv_usec = (atime % 1000) * 1000;\n    times[1].tv_sec = mtime / 1000;\n    times[1].tv_usec = (mtime % 1000) * 1000;\n    if (fcfs_futimes(fd, times) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_chown\n  (JNIEnv *env, jobject obj, jint owner, jint group)\n{\n    FILE_OBJ_FETCH_FD();\n    if (fcfs_fchown(fd, owner, group) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_chmod\n  (JNIEnv *env, jobject obj, jint mode)\n{\n    FILE_OBJ_FETCH_FD();\n    if (fcfs_fchmod(fd, mode) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_setxattr\n  (JNIEnv *env, jobject obj, jstring jname, jbyteArray bs,\n   jint off, jint len, jint flags)\n{\n    const char *name;\n    jbyte *ba;\n    jsize size;\n\n    FILE_OBJ_FETCH_FD();\n    FILE_CHECK_ARRAY_BOUNDS(bs, off, len, size);\n    ba = (*env)->GetByteArrayElements(env, bs, NULL);\n    name = (*env)->GetStringUTFChars(env, jname, NULL);\n    if (fcfs_fsetxattr(fd, name, ba + off, len,\n                fcfs_jni_convert_setxattr_flags(flags)) < 0)\n    {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jname, name);\n    (*env)->ReleaseByteArrayElements(env, bs, ba, 0);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_removexattr\n  (JNIEnv *env, jobject obj, jstring jname)\n{\n    const char *name;\n\n    FILE_OBJ_FETCH_FD();\n    name = (*env)->GetStringUTFChars(env, jname, NULL);\n    if (fcfs_fremovexattr(fd, name) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jname, name);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_chdir\n  (JNIEnv *env, jobject obj)\n{\n    FILE_OBJ_FETCH_FD();\n    if (fcfs_fchdir(fd) < 0) {\n        throw_file_exception(env, fd, errno != 0 ? errno : EIO);\n    }\n}\n"
  },
  {
    "path": "src/java/jni/com_fastken_fcfs_FCFSFile.h",
    "content": "/* DO NOT EDIT THIS FILE - it is machine generated */\n#include <jni.h>\n/* Header for class com_fastken_fcfs_FCFSFile */\n\n#ifndef _Included_com_fastken_fcfs_FCFSFile\n#define _Included_com_fastken_fcfs_FCFSFile\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    doInit\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_doInit\n  (JNIEnv *, jclass);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    close\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_close\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    sync\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_sync\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    datasync\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_datasync\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    write\n * Signature: ([BII)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_write\n  (JNIEnv *, jobject, jbyteArray, jint, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    read\n * Signature: ([BII)I\n */\nJNIEXPORT jint JNICALL Java_com_fastken_fcfs_FCFSFile_read\n  (JNIEnv *, jobject, jbyteArray, jint, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    lseek\n * Signature: (JI)J\n */\nJNIEXPORT jlong JNICALL Java_com_fastken_fcfs_FCFSFile_lseek\n  (JNIEnv *, jobject, jlong, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    allocate\n * Signature: (IJJ)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_allocate\n  (JNIEnv *, jobject, jint, jlong, jlong);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    truncate\n * Signature: (J)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_truncate\n  (JNIEnv *, jobject, jlong);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    stat\n * Signature: ()Lcom/fastken/fcfs/FCFSFileStat;\n */\nJNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSFile_stat\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    lock\n * Signature: (JJZZ)Z\n */\nJNIEXPORT jboolean JNICALL Java_com_fastken_fcfs_FCFSFile_lock\n  (JNIEnv *, jobject, jlong, jlong, jboolean, jboolean);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    unlock\n * Signature: (JJ)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_unlock\n  (JNIEnv *, jobject, jlong, jlong);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    utimes\n * Signature: (JJ)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_utimes\n  (JNIEnv *, jobject, jlong, jlong);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    chown\n * Signature: (II)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_chown\n  (JNIEnv *, jobject, jint, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    chmod\n * Signature: (I)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_chmod\n  (JNIEnv *, jobject, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    setxattr\n * Signature: (Ljava/lang/String;[BIII)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_setxattr\n  (JNIEnv *, jobject, jstring, jbyteArray, jint, jint, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    removexattr\n * Signature: (Ljava/lang/String;)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_removexattr\n  (JNIEnv *, jobject, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    getxattr\n * Signature: (Ljava/lang/String;)[B\n */\nJNIEXPORT jbyteArray JNICALL Java_com_fastken_fcfs_FCFSFile_getxattr\n  (JNIEnv *, jobject, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    listxattr\n * Signature: ()Ljava/util/List;\n */\nJNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSFile_listxattr\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    chdir\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_chdir\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    statvfs\n * Signature: ()Lcom/fastken/fcfs/FCFSVFSStat;\n */\nJNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSFile_statvfs\n  (JNIEnv *, jobject);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "src/java/jni/com_fastken_fcfs_FCFSPosixAPI.c",
    "content": "\n#include \"fastcfs/api/std/posix_api.h\"\n#include \"global.h\"\n#include \"common.h\"\n#include \"com_fastken_fcfs_FCFSPosixAPI.h\"\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_init\n  (JNIEnv *env, jobject obj, jstring poolname, jstring filename)\n{\n    const char *log_prefix_name = \"papi\";\n    char *ns;\n    char *config_filename;\n    FCFSPosixAPIContext *ctx;\n    int result;\n\n    ctx = malloc(sizeof(FCFSPosixAPIContext));\n    if (ctx == NULL) {\n        fcfs_jni_throw_exception(env, \"Out of Memory\");\n        return;\n    }\n\n    ns = (char *)((*env)->GetStringUTFChars(env, poolname, NULL));\n    config_filename = (char *)((*env)->GetStringUTFChars(\n                env, filename, NULL));\n    result = fcfs_posix_api_init_start_ex(ctx,\n            log_prefix_name, ns, config_filename);\n    (*env)->ReleaseStringUTFChars(env, poolname, ns);\n    (*env)->ReleaseStringUTFChars(env, filename, config_filename);\n\n    if (result != 0) {\n        free(ctx);\n        fcfs_jni_throw_filesystem_exception(env, \"/\", result);\n        return;\n    }\n\n    (*env)->CallVoidMethod(env, obj, g_fcfs_jni_global_vars.\n            papi.setHandler, (long)ctx);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_destroy\n  (JNIEnv *env, jobject obj)\n{\n    long handler;\n    FCFSPosixAPIContext *ctx;\n\n    handler = (*env)->CallLongMethod(env, obj,\n            g_fcfs_jni_global_vars.papi.getHandler);\n    if (handler == 0) {\n        return;\n    }\n\n    ctx = (FCFSPosixAPIContext *)handler;\n    fcfs_posix_api_stop_ex(ctx);\n    fcfs_posix_api_destroy_ex(ctx);\n    free(ctx);\n\n    (*env)->CallVoidMethod(env, obj, g_fcfs_jni_global_vars.\n            papi.setHandler, 0);\n}\n\n#define PAPI_SET_CTX_AND_PATH_EX(retval, path)  \\\n    long handler;  \\\n    const char *path; \\\n    FCFSPosixAPIContext *ctx; \\\n    \\\n    handler = (*env)->CallLongMethod(env, obj,       \\\n            g_fcfs_jni_global_vars.papi.getHandler); \\\n    if (handler == 0) {  \\\n        fcfs_jni_throw_null_pointer_exception(env); \\\n        return retval; \\\n    } \\\n    \\\n    ctx = (FCFSPosixAPIContext *)handler; \\\n    path = (*env)->GetStringUTFChars(env, j##path, NULL)\n\n#define PAPI_SET_CTX_AND_PATH(retval)  \\\n    PAPI_SET_CTX_AND_PATH_EX(retval, path)\n\njobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_opendir\n  (JNIEnv *env, jobject obj, jstring jpath)\n{\n    DIR *dir;\n\n    PAPI_SET_CTX_AND_PATH(NULL);\n    if ((dir=fcfs_opendir_ex(ctx, path)) == NULL) {\n        fcfs_jni_throw_filesystem_exception(env, path,\n                errno != 0 ? errno : ENOENT);\n        (*env)->ReleaseStringUTFChars(env, jpath, path);\n        return NULL;\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n\n    return (*env)->NewObject(env, g_fcfs_jni_global_vars.dir.clazz,\n            g_fcfs_jni_global_vars.dir.constructor1, (long)dir);\n}\n\njobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_open\n  (JNIEnv *env, jobject obj, jstring jpath, jint flags, jint mode)\n{\n    int fd;\n\n    PAPI_SET_CTX_AND_PATH(NULL);\n    if ((fd=fcfs_open_ex(ctx, path, fcfs_jni_convert_open_flags(flags),\n                    mode, fcfs_papi_tpid_type_tid)) < 0)\n    {\n        fcfs_jni_throw_filesystem_exception(env, path,\n                errno != 0 ? errno : ENOENT);\n        (*env)->ReleaseStringUTFChars(env, jpath, path);\n        return NULL;\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n\n    return (*env)->NewObject(env, g_fcfs_jni_global_vars.file.clazz,\n            g_fcfs_jni_global_vars.file.constructor1, fd);\n}\n\njstring JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_getcwd\n  (JNIEnv *env, jobject obj)\n{\n    long handler;\n    char path[PATH_MAX];\n    FCFSPosixAPIContext *ctx;\n\n    handler = (*env)->CallLongMethod(env, obj,\n            g_fcfs_jni_global_vars.papi.getHandler);\n    if (handler == 0) {\n        fcfs_jni_throw_null_pointer_exception(env);\n        return NULL;\n    }\n\n    ctx = (FCFSPosixAPIContext *)handler;\n    if (fcfs_getcwd_ex(ctx, path, sizeof(path)) == NULL) {\n        fcfs_jni_throw_filesystem_exception(env, \"/\",\n                errno != 0 ? errno : ENAMETOOLONG);\n        return NULL;\n    }\n\n    return (*env)->NewStringUTF(env, path);\n}\n\njobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_stat\n    (JNIEnv *env, jobject obj, jstring jpath, jboolean followlink)\n{\n    int ret;\n    struct stat stat;\n\n    PAPI_SET_CTX_AND_PATH(NULL);\n    if (followlink) {\n        ret = fcfs_stat_ex(ctx, path, &stat);\n    }  else {\n        ret = fcfs_lstat_ex(ctx, path, &stat);\n    }\n    if (ret != 0) {\n        fcfs_jni_throw_filesystem_exception(env, path,\n                errno != 0 ? errno : ENOENT);\n        (*env)->ReleaseStringUTFChars(env, jpath, path);\n        return NULL;\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n\n    return (*env)->NewObject(env, g_fcfs_jni_global_vars.fstat.clazz,\n            g_fcfs_jni_global_vars.fstat.constructor10, stat.st_ino,\n            stat.st_mode, stat.st_nlink, stat.st_uid, stat.st_gid,\n            stat.st_rdev, stat.st_size, (jlong)stat.st_atime * 1000LL,\n            (jlong)stat.st_mtime * 1000LL, (jlong)stat.st_ctime * 1000LL);\n}\n\njstring JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_readlink\n  (JNIEnv *env, jobject obj, jstring jpath)\n{\n    char buff[PATH_MAX];\n\n    PAPI_SET_CTX_AND_PATH(NULL);\n    if (fcfs_readlink_ex(ctx, path, buff, sizeof(buff)) < 0) {\n        fcfs_jni_throw_filesystem_exception(env, path,\n                errno != 0 ? errno : ENOENT);\n        (*env)->ReleaseStringUTFChars(env, jpath, path);\n        return NULL;\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n\n    return (*env)->NewStringUTF(env, buff);\n}\n\njobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_statvfs\n  (JNIEnv *env, jobject obj, jstring jpath)\n{\n    struct statvfs stat;\n    SFSpaceStat space;\n\n    PAPI_SET_CTX_AND_PATH(NULL);\n    if (fcfs_statvfs_ex(ctx, path, &stat) != 0) {\n        fcfs_jni_throw_filesystem_exception(env, path,\n                errno != 0 ? errno : ENOENT);\n        (*env)->ReleaseStringUTFChars(env, jpath, path);\n        return NULL;\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n\n    space.total = (int64_t)stat.f_blocks * stat.f_bsize;\n    space.avail = (int64_t)stat.f_bavail * stat.f_bsize;\n    space.used = space.total - space.avail;\n    return (*env)->NewObject(env, g_fcfs_jni_global_vars.statvfs.clazz,\n            g_fcfs_jni_global_vars.statvfs.constructor6, space.total,\n            space.avail, space.used, stat.f_files, stat.f_favail,\n            stat.f_files - stat.f_favail);\n}\n\njbyteArray JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_getxattr\n  (JNIEnv *env, jobject obj, jstring jpath, jstring jname, jboolean followlink)\n{\n    const char *name;\n    char holder[4 * 1024];\n    char *buff;\n    int result;\n    int size;\n    int length;\n    jbyteArray value;\n\n    PAPI_SET_CTX_AND_PATH(NULL);\n    name = (*env)->GetStringUTFChars(env, jname, NULL);\n\n    buff = holder;\n    size = sizeof(holder);\n    if (followlink) {\n        length = fcfs_getxattr_ex(ctx, path, name, buff, size);\n    }  else {\n        length = fcfs_lgetxattr_ex(ctx, path, name, buff, size);\n    }\n    if (length < 0) {\n        result = errno != 0 ? errno : ENOENT;\n        if (result == EOVERFLOW) {\n            if (followlink) {\n                size = fcfs_getxattr_ex(ctx, path, name, buff, 0);\n            }  else {\n                size = fcfs_lgetxattr_ex(ctx, path, name, buff, 0);\n            }\n            if (size < 0) {\n                result = errno != 0 ? errno : ENOENT;\n            } else {\n                if ((buff=malloc(size)) == NULL) {\n                    result = ENOMEM;\n                } else {\n                    if (followlink) {\n                        length = fcfs_getxattr_ex(ctx, path, name, buff, size);\n                    }  else {\n                        length = fcfs_lgetxattr_ex(ctx, path, name, buff, size);\n                    }\n                    if (length < 0) {\n                        result = errno != 0 ? errno : ENOENT;\n                    } else {\n                        result = 0;\n                    }\n                }\n            }\n        }\n\n        if (result != 0) {\n            fcfs_jni_throw_filesystem_exception(env, path, result);\n            (*env)->ReleaseStringUTFChars(env, jpath, path);\n            (*env)->ReleaseStringUTFChars(env, jname, name);\n            return NULL;\n        }\n    }\n\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n    (*env)->ReleaseStringUTFChars(env, jname, name);\n    value = (*env)->NewByteArray(env, length);\n    (*env)->SetByteArrayRegion(env, value, 0, length,\n            (const jbyte *)buff);\n    if (buff != holder) {\n        free(buff);\n    }\n\n    return value;\n}\n\njobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_listxattr\n  (JNIEnv *env, jobject obj, jstring jpath, jboolean followlink)\n{\n    char holder[4 * 1024];\n    char *buff;\n    int result;\n    int size;\n    int length;\n    jobject list_obj;\n\n    PAPI_SET_CTX_AND_PATH(NULL);\n    buff = holder;\n    size = sizeof(holder);\n    if (followlink) {\n        length = fcfs_listxattr_ex(ctx, path, buff, size);\n    }  else {\n        length = fcfs_llistxattr_ex(ctx, path, buff, size);\n    }\n    if (length < 0) {\n        result = errno != 0 ? errno : ENOENT;\n        if (result == EOVERFLOW) {\n            if (followlink) {\n                size = fcfs_listxattr_ex(ctx, path, buff, 0);\n            }  else {\n                size = fcfs_llistxattr_ex(ctx, path, buff, 0);\n            }\n            if (size < 0) {\n                result = errno != 0 ? errno : ENOENT;\n            } else {\n                if ((buff=malloc(size)) == NULL) {\n                    result = ENOMEM;\n                } else {\n                    if (followlink) {\n                        length = fcfs_listxattr_ex(ctx, path, buff, size);\n                    }  else {\n                        length = fcfs_llistxattr_ex(ctx, path, buff, size);\n                    }\n                    if (length < 0) {\n                        result = errno != 0 ? errno : ENOENT;\n                    } else {\n                        result = 0;\n                    }\n                }\n            }\n        }\n\n        if (result != 0) {\n            fcfs_jni_throw_filesystem_exception(env, path, result);\n            (*env)->ReleaseStringUTFChars(env, jpath, path);\n            return NULL;\n        }\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n    list_obj = fcfs_jni_convert_to_list(env, buff, length);\n    if (buff != holder) {\n        free(buff);\n    }\n\n    return list_obj;\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_truncate\n  (JNIEnv *env, jobject obj, jstring jpath, jlong length)\n{\n    PAPI_SET_CTX_AND_PATH();\n\n    if (fcfs_truncate_ex(ctx, path, length) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_link\n  (JNIEnv *env, jobject obj, jstring jpath1, jstring jpath2)\n{\n    const char *path2;\n    const char *path;\n\n    PAPI_SET_CTX_AND_PATH_EX(, path1);\n    path2 = (*env)->GetStringUTFChars(env, jpath2, NULL);\n    if (fcfs_link_ex(ctx, path1, path2) < 0) {\n        if (errno == ENOENT) {\n            path = path1;\n        } else {\n            path = path2;\n        }\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath1, path1);\n    (*env)->ReleaseStringUTFChars(env, jpath2, path2);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_symlink\n  (JNIEnv *env, jobject obj, jstring jlink, jstring jpath)\n{\n    const char *path;\n\n    PAPI_SET_CTX_AND_PATH_EX(, link);\n    path = (*env)->GetStringUTFChars(env, jpath, NULL);\n    if (fcfs_symlink_ex(ctx, link, path) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jlink, link);\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_rename\n  (JNIEnv *env, jobject obj, jstring jpath1, jstring jpath2)\n{\n    const char *path2;\n    const char *path;\n\n    PAPI_SET_CTX_AND_PATH_EX(, path1);\n    path2 = (*env)->GetStringUTFChars(env, jpath2, NULL);\n    if (fcfs_rename_ex(ctx, path1, path2) < 0) {\n        if (errno == ENOENT) {\n            path = path1;\n        } else {\n            path = path2;\n        }\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath1, path1);\n    (*env)->ReleaseStringUTFChars(env, jpath2, path2);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mknod\n  (JNIEnv *env, jobject obj, jstring jpath, jint mode, jint dev)\n{\n    PAPI_SET_CTX_AND_PATH();\n\n    if (fcfs_mknod_ex(ctx, path, mode, dev) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mkfifo\n  (JNIEnv *env, jobject obj, jstring jpath, jint mode)\n{\n    PAPI_SET_CTX_AND_PATH();\n\n    if (fcfs_mkfifo_ex(ctx, path, mode) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_access\n  (JNIEnv *env, jobject obj, jstring jpath, jint mode)\n{\n    PAPI_SET_CTX_AND_PATH();\n\n    if (fcfs_access_ex(ctx, path, mode) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\njboolean JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_exists\n  (JNIEnv *env, jobject obj, jstring jpath)\n{\n    jboolean ret;\n\n    PAPI_SET_CTX_AND_PATH(false);\n    if (fcfs_access_ex(ctx, path, F_OK) == 0) {\n        ret = true;\n    } else {\n        if (errno != ENOENT) {\n            fcfs_jni_throw_filesystem_exception(env,\n                    path, errno != 0 ? errno : EIO);\n        }\n        ret = false;\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n    return ret;\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_utimes\n  (JNIEnv *env, jobject obj, jstring jpath, jlong atime, jlong mtime)\n{\n    struct timeval times[2];\n\n    PAPI_SET_CTX_AND_PATH();\n    times[0].tv_sec = atime / 1000;\n    times[0].tv_usec = (atime % 1000) * 1000;\n    times[1].tv_sec = mtime / 1000;\n    times[1].tv_usec = (mtime % 1000) * 1000;\n    if (fcfs_utimes_ex(ctx, path, times) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_unlink\n  (JNIEnv *env, jobject obj, jstring jpath)\n{\n    PAPI_SET_CTX_AND_PATH();\n\n    if (fcfs_unlink_ex(ctx, path) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mkdir\n  (JNIEnv *env, jobject obj, jstring jpath, jint mode)\n{\n    PAPI_SET_CTX_AND_PATH();\n\n    if (fcfs_mkdir_ex(ctx, path, mode) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_rmdir\n  (JNIEnv *env, jobject obj, jstring jpath)\n{\n    PAPI_SET_CTX_AND_PATH();\n\n    if (fcfs_rmdir_ex(ctx, path) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chown\n  (JNIEnv *env, jobject obj, jstring jpath, jint owner,\n   jint group, jboolean followlink)\n{\n    int ret;\n    PAPI_SET_CTX_AND_PATH();\n\n    if (followlink) {\n        ret = fcfs_chown_ex(ctx, path, owner, group);\n    }  else {\n        ret = fcfs_lchown_ex(ctx, path, owner, group);\n    }\n    if (ret != 0) {\n        fcfs_jni_throw_filesystem_exception(env, path,\n                errno != 0 ? errno : ENOENT);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chmod\n  (JNIEnv *env, jobject obj, jstring jpath, jint mode)\n{\n    PAPI_SET_CTX_AND_PATH();\n\n    if (fcfs_chmod_ex(ctx, path, mode) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chdir\n  (JNIEnv *env, jobject obj, jstring jpath)\n{\n    PAPI_SET_CTX_AND_PATH();\n\n    if (fcfs_chdir_ex(ctx, path) < 0) {\n        fcfs_jni_throw_filesystem_exception(env,\n                path, errno != 0 ? errno : EIO);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_setxattr\n  (JNIEnv *env, jobject obj, jstring jpath, jstring jname,\n   jbyteArray b, jint off, jint len,\n   jint flags, jboolean followlink)\n{\n    const char *name;\n    int ret;\n    jbyte *ba;\n    jsize size;\n\n    PAPI_SET_CTX_AND_PATH();\n    size = (*env)->GetArrayLength(env, b);\n    if (off < 0 || off >= size) {\n        fcfs_jni_throw_out_of_bounds_exception(env, off);\n        (*env)->ReleaseStringUTFChars(env, jpath, path);\n        return;\n    }\n    if (off + len > size) {\n        fcfs_jni_throw_out_of_bounds_exception(env, off + len);\n        (*env)->ReleaseStringUTFChars(env, jpath, path);\n        return;\n    }\n\n    name = (*env)->GetStringUTFChars(env, jname, NULL);\n    ba = (*env)->GetByteArrayElements(env, b, NULL);\n    if (followlink) {\n        ret = fcfs_setxattr_ex(ctx, path, name, ba + off, len,\n                fcfs_jni_convert_setxattr_flags(flags));\n    }  else {\n        ret = fcfs_lsetxattr_ex(ctx, path, name, ba + off, len,\n                fcfs_jni_convert_setxattr_flags(flags));\n    }\n    if (ret != 0) {\n        fcfs_jni_throw_filesystem_exception(env, path,\n                errno != 0 ? errno : ENOENT);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n    (*env)->ReleaseStringUTFChars(env, jname, name);\n    (*env)->ReleaseByteArrayElements(env, b, ba, 0);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_removexattr\n  (JNIEnv *env, jobject obj, jstring jpath,\n   jstring jname, jboolean followlink)\n{\n    const char *name;\n    int ret;\n    PAPI_SET_CTX_AND_PATH();\n\n    name = (*env)->GetStringUTFChars(env, jname, NULL);\n    if (followlink) {\n        ret = fcfs_removexattr_ex(ctx, path, name);\n    }  else {\n        ret = fcfs_lremovexattr_ex(ctx, path, name);\n    }\n    if (ret != 0) {\n        fcfs_jni_throw_filesystem_exception(env, path,\n                errno != 0 ? errno : ENOENT);\n    }\n    (*env)->ReleaseStringUTFChars(env, jpath, path);\n    (*env)->ReleaseStringUTFChars(env, jname, name);\n}\n"
  },
  {
    "path": "src/java/jni/com_fastken_fcfs_FCFSPosixAPI.h",
    "content": "/* DO NOT EDIT THIS FILE - it is machine generated */\n#include <jni.h>\n/* Header for class com_fastken_fcfs_FCFSPosixAPI */\n\n#ifndef _Included_com_fastken_fcfs_FCFSPosixAPI\n#define _Included_com_fastken_fcfs_FCFSPosixAPI\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    doInit\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_doInit\n  (JNIEnv *, jclass);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    init\n * Signature: (Ljava/lang/String;Ljava/lang/String;)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_init\n  (JNIEnv *, jobject, jstring, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    destroy\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_destroy\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    opendir\n * Signature: (Ljava/lang/String;)Lcom/fastken/fcfs/FCFSDirectory;\n */\nJNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_opendir\n  (JNIEnv *, jobject, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    open\n * Signature: (Ljava/lang/String;II)Lcom/fastken/fcfs/FCFSFile;\n */\nJNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_open\n  (JNIEnv *, jobject, jstring, jint, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    getcwd\n * Signature: ()Ljava/lang/String;\n */\nJNIEXPORT jstring JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_getcwd\n  (JNIEnv *, jobject);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    truncate\n * Signature: (Ljava/lang/String;J)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_truncate\n  (JNIEnv *, jobject, jstring, jlong);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    stat\n * Signature: (Ljava/lang/String;Z)Lcom/fastken/fcfs/FCFSFileStat;\n */\nJNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_stat\n  (JNIEnv *, jobject, jstring, jboolean);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    link\n * Signature: (Ljava/lang/String;Ljava/lang/String;)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_link\n  (JNIEnv *, jobject, jstring, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    symlink\n * Signature: (Ljava/lang/String;Ljava/lang/String;)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_symlink\n  (JNIEnv *, jobject, jstring, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    readlink\n * Signature: (Ljava/lang/String;)Ljava/lang/String;\n */\nJNIEXPORT jstring JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_readlink\n  (JNIEnv *, jobject, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    mknod\n * Signature: (Ljava/lang/String;II)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mknod\n  (JNIEnv *, jobject, jstring, jint, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    mkfifo\n * Signature: (Ljava/lang/String;I)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mkfifo\n  (JNIEnv *, jobject, jstring, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    access\n * Signature: (Ljava/lang/String;I)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_access\n  (JNIEnv *, jobject, jstring, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    exists\n * Signature: (Ljava/lang/String;)Z\n */\nJNIEXPORT jboolean JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_exists\n  (JNIEnv *, jobject, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    utimes\n * Signature: (Ljava/lang/String;JJ)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_utimes\n  (JNIEnv *, jobject, jstring, jlong, jlong);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    unlink\n * Signature: (Ljava/lang/String;)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_unlink\n  (JNIEnv *, jobject, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    rename\n * Signature: (Ljava/lang/String;Ljava/lang/String;)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_rename\n  (JNIEnv *, jobject, jstring, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    mkdir\n * Signature: (Ljava/lang/String;I)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_mkdir\n  (JNIEnv *, jobject, jstring, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    rmdir\n * Signature: (Ljava/lang/String;)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_rmdir\n  (JNIEnv *, jobject, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    chown\n * Signature: (Ljava/lang/String;IIZ)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chown\n  (JNIEnv *, jobject, jstring, jint, jint, jboolean);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    chmod\n * Signature: (Ljava/lang/String;I)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chmod\n  (JNIEnv *, jobject, jstring, jint);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    statvfs\n * Signature: (Ljava/lang/String;)Lcom/fastken/fcfs/FCFSVFSStat;\n */\nJNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_statvfs\n  (JNIEnv *, jobject, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    chdir\n * Signature: (Ljava/lang/String;)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_chdir\n  (JNIEnv *, jobject, jstring);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    setxattr\n * Signature: (Ljava/lang/String;Ljava/lang/String;[BIIIZ)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_setxattr\n  (JNIEnv *, jobject, jstring, jstring, jbyteArray, jint, jint, jint, jboolean);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    removexattr\n * Signature: (Ljava/lang/String;Ljava/lang/String;Z)V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_removexattr\n  (JNIEnv *, jobject, jstring, jstring, jboolean);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    getxattr\n * Signature: (Ljava/lang/String;Ljava/lang/String;Z)[B\n */\nJNIEXPORT jbyteArray JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_getxattr\n  (JNIEnv *, jobject, jstring, jstring, jboolean);\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    listxattr\n * Signature: (Ljava/lang/String;Z)Ljava/util/List;\n */\nJNIEXPORT jobject JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_listxattr\n  (JNIEnv *, jobject, jstring, jboolean);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "src/java/jni/common.c",
    "content": "/*\n * Copyright (c) 2022 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <errno.h>\n#include <sys/types.h>\n#include <sys/xattr.h>\n#include \"fastcommon/common_define.h\"\n#include \"com_fastken_fcfs_FCFSConstants.h\"\n#include \"common.h\"\n\nvoid fcfs_jni_throw_exception(JNIEnv *env, const char *message)\n{\n    jclass clazz;\n    clazz = (*env)->FindClass(env, \"java/lang/Exception\");\n    if (clazz == NULL) {\n        return;\n    }\n\n    (*env)->ThrowNew(env, clazz, message);\n}\n\nvoid fcfs_jni_throw_null_pointer_exception(JNIEnv *env)\n{\n    jclass clazz;\n    clazz = (*env)->FindClass(env, \"java/lang/NullPointerException\");\n    if (clazz == NULL) {\n        return;\n    }\n\n    (*env)->ThrowNew(env, clazz, \"null pointer\");\n}\n\nvoid fcfs_jni_throw_out_of_bounds_exception(JNIEnv *env, const int index)\n{\n    jclass clazz;\n    jmethodID constructor1;\n\n    clazz = (*env)->FindClass(env, \"java/lang/ArrayIndexOutOfBoundsException\");\n    if (clazz == NULL) {\n        return;\n    }\n\n    constructor1 = (*env)->GetMethodID(env, clazz, \"<init>\", \"(I)V\");\n    (*env)->Throw(env, (*env)->NewObject(env, clazz, constructor1, index));\n}\n\nvoid fcfs_jni_throw_filesystem_exception(JNIEnv *env,\n        const char *file, const int err_no)\n{\n    const char *cname;\n    bool need_error;\n    jclass clazz;\n\n    switch (err_no) {\n        case ENOENT:\n            cname = \"java/nio/file/NoSuchFileException\";\n            need_error = false;\n            break;\n        case EEXIST:\n            cname = \"java/nio/file/FileAlreadyExistsException\";\n            need_error = false;\n            break;\n        case EPERM:\n            cname = \"java/nio/file/AccessDeniedException\";\n            need_error = false;\n            break;\n        case ENOTEMPTY:\n            cname = \"java/nio/file/DirectoryNotEmptyException\";\n            need_error = false;\n            break;\n        case ELOOP:\n            cname = \"java/nio/file/FileSystemLoopException\";\n            need_error = false;\n            break;\n        case ENOTDIR:\n            cname = \"java/nio/file/NotDirectoryException\";\n            need_error = false;\n            break;\n        case ENOLINK:\n            cname = \"java/nio/file/NotLinkException\";\n            need_error = false;\n            break;\n        default:\n            cname = \"java/nio/file/FileSystemException\";\n            need_error = true;\n            break;\n    }\n\n    clazz = (*env)->FindClass(env, cname);\n    if (clazz == NULL) {\n        return;\n    }\n\n    if (need_error) {\n        jmethodID constructor3;\n        jobject obj;\n\n        constructor3 = (*env)->GetMethodID(env, clazz, \"<init>\",\n                \"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V\");\n        obj = (*env)->NewObject(env, clazz, constructor3,\n                (*env)->NewStringUTF(env, file), NULL,\n                (*env)->NewStringUTF(env, strerror(err_no)));\n        (*env)->Throw(env, obj);\n    } else {\n        (*env)->ThrowNew(env, clazz, file);\n    }\n}\n\njobject fcfs_jni_convert_to_list(JNIEnv *env,\n        const char *buff, const int length)\n{\n    jclass lclazz;\n    jobject lobj;\n    jmethodID lconstructor;\n    jmethodID ladd;\n    const char *name;\n    const char *end;\n\n    lclazz = (*env)->FindClass(env, \"java/util/ArrayList\");\n    lconstructor = (*env)->GetMethodID(env, lclazz, \"<init>\", \"()V\");\n    lobj = (*env)->NewObject(env, lclazz, lconstructor);\n    if (length == 0) {\n        return lobj;\n    }\n\n    ladd = (*env)->GetMethodID(env, lclazz,\n            \"add\", \"(Ljava/lang/Object;)Z\");\n    name = buff;\n    end = buff + length;\n    do {\n        (*env)->CallBooleanMethod(env, lobj, ladd,\n                (*env)->NewStringUTF(env, name));\n        name += strlen(name) + 1;\n    } while (name < end);\n\n    return lobj;\n}\n\nint fcfs_jni_convert_open_flags(const int flags)\n{\n    int new_flags;\n\n    new_flags = 0;\n    if ((flags & com_fastken_fcfs_FCFSConstants_O_WRONLY)) {\n        new_flags |= O_WRONLY;\n    }\n    if ((flags & com_fastken_fcfs_FCFSConstants_O_RDWR)) {\n        new_flags |= O_RDWR;\n    }\n    if ((flags & com_fastken_fcfs_FCFSConstants_O_CREAT)) {\n        new_flags |= O_CREAT;\n    }\n    if ((flags & com_fastken_fcfs_FCFSConstants_O_EXCL)) {\n        new_flags |= O_EXCL;\n    }\n    if ((flags & com_fastken_fcfs_FCFSConstants_O_TRUNC)) {\n        new_flags |= O_TRUNC;\n    }\n    if ((flags & com_fastken_fcfs_FCFSConstants_O_APPEND)) {\n        new_flags |= O_APPEND;\n    }\n    if ((flags & com_fastken_fcfs_FCFSConstants_O_NOFOLLOW)) {\n        new_flags |= O_NOFOLLOW;\n    }\n    if ((flags & com_fastken_fcfs_FCFSConstants_O_CLOEXEC)) {\n        new_flags |= O_CLOEXEC;\n    }\n\n    return new_flags;\n}\n\nint fcfs_jni_convert_setxattr_flags(const int flags)\n{\n    int new_flags;\n\n    new_flags = 0;\n    if ((flags & com_fastken_fcfs_FCFSConstants_XATTR_CREATE)) {\n        new_flags |= XATTR_CREATE;\n    }\n    if ((flags & com_fastken_fcfs_FCFSConstants_XATTR_REPLACE)) {\n        new_flags |= XATTR_REPLACE;\n    }\n\n    return new_flags;\n}\n"
  },
  {
    "path": "src/java/jni/common.h",
    "content": "/*\n * Copyright (c) 2022 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_JNI_COMMON_H\n#define _FCFS_JNI_COMMON_H\n\n#include <jni.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n    void fcfs_jni_throw_exception(JNIEnv *env, const char *message);\n\n    void fcfs_jni_throw_null_pointer_exception(JNIEnv *env);\n\n    void fcfs_jni_throw_out_of_bounds_exception(JNIEnv *env, const int index);\n\n    void fcfs_jni_throw_filesystem_exception(JNIEnv *env,\n            const char *file, const int err_no);\n\n    jobject fcfs_jni_convert_to_list(JNIEnv *env,\n            const char *buff, const int length);\n\n    int fcfs_jni_convert_open_flags(const int flags);\n\n    int fcfs_jni_convert_setxattr_flags(const int flags);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/java/jni/global.c",
    "content": "/*\n * Copyright (c) 2022 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <errno.h>\n#include \"fastcommon/common_define.h\"\n#include \"global.h\"\n\nFCFSJNIGlobalVars g_fcfs_jni_global_vars = {{NULL, NULL}};\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_doInit\n  (JNIEnv *env, jclass clazz)\n{\n    g_fcfs_jni_global_vars.papi.setHandler = (*env)->GetMethodID(\n            env, clazz, \"setHandler\", \"(J)V\");\n    g_fcfs_jni_global_vars.papi.getHandler = (*env)->GetMethodID(\n            env, clazz, \"getHandler\", \"()J\");\n\n    g_fcfs_jni_global_vars.dir.clazz = (*env)->FindClass(env,\n            \"com/fastken/fcfs/FCFSDirectory\");\n    g_fcfs_jni_global_vars.dir.clazz = (*env)->NewGlobalRef(env,\n            g_fcfs_jni_global_vars.dir.clazz);\n\n    g_fcfs_jni_global_vars.file.clazz = (*env)->FindClass(env,\n            \"com/fastken/fcfs/FCFSFile\");\n    g_fcfs_jni_global_vars.file.clazz = (*env)->NewGlobalRef(env,\n            g_fcfs_jni_global_vars.file.clazz);\n\n    g_fcfs_jni_global_vars.statvfs.clazz = (*env)->FindClass(env,\n            \"com/fastken/fcfs/FCFSVFSStat\");\n    g_fcfs_jni_global_vars.statvfs.clazz = (*env)->NewGlobalRef(env,\n            g_fcfs_jni_global_vars.statvfs.clazz);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSDirectory_doInit\n  (JNIEnv *env, jclass clazz)\n{\n    g_fcfs_jni_global_vars.dir.constructor1 = (*env)->GetMethodID(\n            env, clazz, \"<init>\", \"(J)V\");\n    g_fcfs_jni_global_vars.dir.setHandler = (*env)->GetMethodID(\n            env, clazz, \"setHandler\", \"(J)V\");\n    g_fcfs_jni_global_vars.dir.getHandler = (*env)->GetMethodID(\n            env, clazz, \"getHandler\", \"()J\");\n\n    g_fcfs_jni_global_vars.dirent.clazz = (*env)->FindClass(env,\n            \"com/fastken/fcfs/FCFSDirectory$Entry\");\n    g_fcfs_jni_global_vars.dirent.clazz = (*env)->NewGlobalRef(env,\n            g_fcfs_jni_global_vars.dirent.clazz);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSDirectory_00024Entry_doInit\n  (JNIEnv *env, jclass clazz)\n{\n    g_fcfs_jni_global_vars.dirent.constructor2 = (*env)->GetMethodID(\n            env, clazz, \"<init>\", \"(JLjava/lang/String;)V\");\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFile_doInit\n  (JNIEnv *env, jclass clazz)\n{\n    g_fcfs_jni_global_vars.file.constructor1 = (*env)->GetMethodID(\n            env, clazz, \"<init>\", \"(I)V\");\n    g_fcfs_jni_global_vars.file.setFD = (*env)->GetMethodID(\n            env, clazz, \"setFD\", \"(I)V\");\n    g_fcfs_jni_global_vars.file.getFD = (*env)->GetMethodID(\n            env, clazz, \"getFD\", \"()I\");\n\n    g_fcfs_jni_global_vars.fstat.clazz = (*env)->FindClass(env,\n            \"com/fastken/fcfs/FCFSFileStat\");\n    g_fcfs_jni_global_vars.fstat.clazz = (*env)->NewGlobalRef(env,\n            g_fcfs_jni_global_vars.fstat.clazz);\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSFileStat_doInit\n  (JNIEnv *env, jclass clazz)\n{\n    g_fcfs_jni_global_vars.fstat.constructor10 = (*env)->GetMethodID(\n            env, clazz, \"<init>\", \"(JIIIIIJJJJ)V\");\n}\n\nvoid JNICALL Java_com_fastken_fcfs_FCFSVFSStat_doInit\n  (JNIEnv *env, jclass clazz)\n{\n    g_fcfs_jni_global_vars.statvfs.constructor6 = (*env)->GetMethodID(\n            env, clazz, \"<init>\", \"(JJJJJJ)V\");\n}\n"
  },
  {
    "path": "src/java/jni/global.h",
    "content": "/*\n * Copyright (c) 2022 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_JNI_GLOBAL_H\n#define _FCFS_JNI_GLOBAL_H\n\n#include <jni.h>\n\ntypedef struct {\n    struct {\n        jmethodID setHandler;\n        jmethodID getHandler;\n    } papi;\n\n    struct {\n        jclass clazz;\n        jmethodID constructor1;\n        jmethodID setHandler;\n        jmethodID getHandler;\n    } dir;\n\n    struct {\n        jclass clazz;\n        jmethodID constructor1;\n        jmethodID setFD;\n        jmethodID getFD;\n    } file;\n\n    struct {\n        jclass clazz;\n        jmethodID constructor2;\n    } dirent;\n\n    struct {\n        jclass clazz;\n        jmethodID constructor10;\n    } fstat;\n\n    struct {\n        jclass clazz;\n        jmethodID constructor6;\n    } statvfs;\n} FCFSJNIGlobalVars;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern FCFSJNIGlobalVars g_fcfs_jni_global_vars;\n\n/*\n * Class:     com_fastken_fcfs_FCFSPosixAPI\n * Method:    doInit\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSPosixAPI_doInit\n  (JNIEnv *, jclass);\n\n/*\n * Class:     com_fastken_fcfs_FCFSDirectory\n * Method:    doInit\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_doInit\n  (JNIEnv *, jclass);\n\n/*\n * Class:     com_fastken_fcfs_FCFSDirectory_Entry\n * Method:    doInit\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSDirectory_00024Entry_doInit\n  (JNIEnv *, jclass);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFile\n * Method:    doInit\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFile_doInit\n  (JNIEnv *, jclass);\n\n/*\n * Class:     com_fastken_fcfs_FCFSFileStat\n * Method:    doInit\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSFileStat_doInit\n  (JNIEnv *, jclass);\n\n/*\n * Class:     com_fastken_fcfs_FCFSVFSStat\n * Method:    doInit\n * Signature: ()V\n */\nJNIEXPORT void JNICALL Java_com_fastken_fcfs_FCFSVFSStat_doInit\n  (JNIEnv *, jclass);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/java/src/main/java/com/fastken/fcfs/FCFSConstants.java",
    "content": "package com.fastken.fcfs;\n\npublic class FCFSConstants {\n    //for access mode\n    public static final int F_OK = 0;  //check file existence\n    public static final int X_OK = 1;  //execute permission\n    public static final int W_OK = 2;  //write permission\n    public static final int R_OK = 4;  //read permission\n\n\n    //for setxattr flags\n    /* perform a pure create operation, which fails if the named\n       attribute exists already. */\n    public static final int XATTR_CREATE = (1 << 0);\n\n    /*Perform a pure replace operation, which fails if the named\n      attribute does not already exist. */\n    public static final int XATTR_REPLACE = (1 << 1);\n\n\n    //for lseek whence\n    /* The file offset is set to offset bytes. */\n    public static final int SEEK_SET = 0;\n\n    /* The file offset is set to its current location plus offset bytes. */\n    public static final int SEEK_CUR = 1;\n\n    /* The file offset is set to the size of the file plus offset bytes. */\n    public static final int SEEK_END = 2;\n\n\n    //for file open flags\n    public static final int O_RDONLY   = 00;\n    public static final int O_WRONLY   = 01;\n    public static final int O_RDWR     = 02;\n    public static final int O_CREAT    = 0100;\n    public static final int O_EXCL     = 0200;\n    public static final int O_TRUNC    = 01000;\n    public static final int O_APPEND   = 02000;\n    public static final int O_NOFOLLOW = 0400000;\n    public static final int O_CLOEXEC  = 02000000;\n}\n"
  },
  {
    "path": "src/java/src/main/java/com/fastken/fcfs/FCFSDirectory.java",
    "content": "package com.fastken.fcfs;\n  \nimport java.nio.file.FileSystemException;\n\npublic class FCFSDirectory {\n    public static class Entry {\n        private long inode;\n        private String name;\n\n        private static native void doInit();\n        static {\n            doInit();\n        }\n\n        public Entry(long inode, String name) {\n            this.inode = inode;\n            this.name = name;\n        }\n\n        public long getInode() {\n            return this.inode;\n        }\n\n        public String getName() {\n            return this.name;\n        }\n    }\n\n    private static native void doInit();\n\n    static {\n        doInit();\n    }\n\n    public native Entry next();\n    public native void seek(long loc);\n    public native long tell();\n    public native void rewind();\n    public native void close();\n\n    private long handler;\n\n    public FCFSDirectory(long handler) {\n        this.handler = handler;\n    }\n\n    public void setHandler(long handler) {\n        this.handler = handler;\n    }\n\n    public long getHandler() {\n        return this.handler;\n    }\n}\n"
  },
  {
    "path": "src/java/src/main/java/com/fastken/fcfs/FCFSFile.java",
    "content": "package com.fastken.fcfs;\n\nimport java.util.List;\n\npublic class FCFSFile {\n\n    private static native void doInit();\n\n    static {\n        doInit();\n    }\n\n    public native void close();\n    public native void sync();\n    public native void datasync();\n    public native void write(byte[] bs, int off, int len);\n    public native int read(byte[] bs, int off, int len);\n    public native long lseek(long offset, int whence);\n    public native void allocate(int mode, long offset, long length);\n    public native void truncate(long length);\n    public native FCFSFileStat stat();\n    public native boolean lock(long position, long length,\n            boolean shared, boolean blocked);\n    public native void unlock(long position, long length);\n    public native void utimes(long atime, long mtime);\n    public native void chown(int owner, int group);\n    public native void chmod(int mode);\n    public native void setxattr(String name, byte[] bs,\n            int off, int len, int flags);\n    public native void removexattr(String name);\n    public native byte[] getxattr(String name);\n    public native List<String> listxattr();\n    public native void chdir();\n    public native FCFSVFSStat statvfs();\n\n    private int fd;\n\n    public FCFSFile(int fd) {\n        this.fd = fd;\n    }\n\n    public void setFD(int fd) {\n        this.fd = fd;\n    }\n\n    public int getFD() {\n        return this.fd;\n    }\n\n    public void write(byte[] bs) {\n        this.write(bs, 0, bs.length);\n    }\n\n    public int read(byte[] bs) {\n        return this.read(bs, 0, bs.length);\n    }\n\n    public boolean lock(long position, long length, boolean shared)\n    {\n        final boolean blocked = true;\n        return this.lock(position, length, shared, blocked);\n    }\n\n    public boolean tryLock(long position, long length, boolean shared)\n    {\n        final boolean blocked = false;\n        return this.lock(position, length, shared, blocked);\n    }\n\n     public void setxattr(String name, byte[] bs, int flags)\n     {\n         this.setxattr(name, bs, 0, bs.length, flags);\n     }\n\n     public void setxattr(String name, byte[] bs)\n     {\n         final int flags = 0;\n         this.setxattr(name, bs, 0, bs.length, flags);\n     }\n}\n"
  },
  {
    "path": "src/java/src/main/java/com/fastken/fcfs/FCFSFileStat.java",
    "content": "package com.fastken.fcfs;\n\npublic class FCFSFileStat {\n    private static native void doInit();\n\n    static {\n        doInit();\n    }\n\n    private long inode;  /* Inode number */\n    private int mode;    /* File type and mode */\n    private int links;   /* Number of hard links */\n    private int uid;     /* User ID of owner */\n    private int gid;     /* Group ID of owner */\n    private int rdev;    /* Device ID (if special file) */\n    private long size;   /* Total size, in bytes */\n    private long atime;  /* Time of last access */\n    private long mtime;  /* Time of last modification */\n    private long ctime;  /* Time of last status change */\n\n    public FCFSFileStat(long inode, int mode, int links, int uid, int gid,\n            int rdev, long size, long atime, long mtime, long ctime)\n    {\n        this.inode = inode;\n        this.mode = mode;\n        this.links = links;\n        this.uid = uid;\n        this.gid = gid;\n        this.rdev = rdev;\n        this.size = size;\n        this.atime = atime;\n        this.mtime = mtime;\n        this.ctime = ctime;\n    }\n\n    public long getInode() {\n        return this.inode;\n    }\n\n    public long getMode() {\n        return this.mode;\n    }\n\n    public long getLinks() {\n        return this.links;\n    }\n\n    public long getUid() {\n        return this.uid;\n    }\n\n    public long getGid() {\n        return this.gid;\n    }\n\n    public long getRdev() {\n        return this.rdev;\n    }\n\n    public long getSize() {\n        return this.size;\n    }\n\n    public long getAtime() {\n        return this.atime;\n    }\n\n    public long getMtime() {\n        return this.mtime;\n    }\n\n    public long getCtime() {\n        return this.ctime;\n    }\n\n    public String toString() {\n        return \"inode: \" + this.inode\n            + \", mode: \" + this.mode\n            + \", links: \" + this.links\n            + \", uid: \" + this.uid\n            + \", gid: \" + this.gid\n            + \", rdev: \" + this.rdev\n            + \", size: \" + this.size\n            + \", atime: \" + this.atime\n            + \", mtime: \" + this.mtime\n            + \", ctime: \" + this.ctime;\n    }\n}\n"
  },
  {
    "path": "src/java/src/main/java/com/fastken/fcfs/FCFSPosixAPI.java",
    "content": "package com.fastken.fcfs;\n  \nimport java.util.List;\nimport java.util.Map;\nimport java.util.HashMap;\nimport java.io.File;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.file.FileSystemException;\n\npublic class FCFSPosixAPI {\n    private static HashMap<String, FCFSPosixAPI> instances = new HashMap<String, FCFSPosixAPI>();\n    private static final String charset = \"UTF-8\";\n    private static String libraryFilename = null;\n\n    private static native void doInit();\n\n    private native void init(String ns, String configFilename);\n    private native void destroy();\n\n    public native FCFSDirectory opendir(String path);\n    public native FCFSFile open(String path, int flags, int mode);\n\n    public native String getcwd();\n    public native void truncate(String path, long length);\n    public native FCFSFileStat stat(String path, boolean followlink);\n    public native void link(String path1, String path2);\n    public native void symlink(String link, String path);\n    public native String readlink(String path);\n    public native void mknod(String path, int mode, int dev);\n    public native void mkfifo(String path, int mode);\n    public native void access(String path, int mode);\n    public native boolean exists(String path);\n    public native void utimes(String path, long atime, long mtime);\n    public native void unlink(String path);\n    public native void rename(String path1, String path2);\n    public native void mkdir(String path, int mode);\n    public native void rmdir(String path);\n    public native void chown(String path, int owner, int group, boolean followlink);\n    public native void chmod(String path, int mode);\n    public native FCFSVFSStat statvfs(String path);\n    public native void chdir(String path);\n    public native void setxattr(String path, String name, byte[] b,\n            int off, int len, int flags, boolean followlink);\n    public native void removexattr(String path, String name, boolean followlink);\n    public native byte[] getxattr(String path, String name, boolean followlink);\n    public native List<String> listxattr(String path, boolean followlink);\n\n    private long handler;\n\n    public static String getLibraryFilename() {\n        return libraryFilename;\n    }\n\n    /**\n      * set libfcfsjni.so filename to load\n      * @param filename the full filename\n      * @return none\n      */\n    public static void setLibraryFilename(String filename) {\n        if (libraryFilename == null) {\n            System.load(filename);  //load the library\n            libraryFilename = filename;\n            doInit();\n        } else if (libraryFilename.equals(filename)) {\n            System.err.println(\"[WARNING] library \" + libraryFilename + \" already loaded\");\n        } else {\n            throw new RuntimeException(\"library \" + libraryFilename\n                    + \" already loaded, can't change to \" + filename);\n        }\n    }\n\n    // private for singleton\n    private FCFSPosixAPI(String ns, String configFilename) {\n        init(ns, configFilename);\n    }\n\n    public void setHandler(long handler) {\n        this.handler = handler;\n    }\n\n    public long getHandler() {\n        return this.handler;\n    }\n\n    protected void finalize() throws Throwable {\n        super.finalize();\n        this.close();\n    }\n\n    private void close() {\n        System.out.println(\"close\");\n        destroy();\n    }\n\n\n    /**\n      * get FCFSPosixAPI instance\n      * @param ns the namespace / poolname\n      * @param configFilename the config filename such as /etc/fastcfs/fcfs/fuse.conf\n      * @return FCFSPosixAPI object\n     */\n    public synchronized static FCFSPosixAPI getInstance(String ns, String configFilename) {\n        String key = ns + \"@\" + configFilename;\n        FCFSPosixAPI obj = instances.get(key);\n        if (obj == null) {\n            obj = new FCFSPosixAPI(ns, configFilename);\n            instances.put(key, obj);\n        }\n\n        return obj;\n    }\n\n    /**\n      * clear FCFSPosixAPI instances\n      * @return none\n     */\n    public synchronized static void clearInstances() {\n        instances.clear();\n    }\n\n    /**\n      * open file for read\n      * @param path the filename to open\n      * @return FCFSFile instance\n     */\n    public FCFSFile open(String path)\n    {\n        final int mode = 0;\n        return this.open(path, FCFSConstants.O_RDONLY, mode);\n    }\n\n    public void setxattr(String path, String name, byte[] b, int flags) {\n        final boolean followlink = true;\n        this.setxattr(path, name, b, 0, b.length, flags, followlink);\n    }\n\n    public void setxattr(String path, String name, byte[] b) {\n        final int flags = 0;\n        final boolean followlink = true;\n        this.setxattr(path, name, b, 0, b.length, flags, followlink);\n    }\n\n    public void lsetxattr(String path, String name, byte[] b, int flags) {\n        final boolean followlink = false;\n        this.setxattr(path, name, b, 0, b.length, flags, followlink);\n    }\n\n    public void lsetxattr(String path, String name, byte[] b) {\n        final int flags = 0;\n        final boolean followlink = false;\n        this.setxattr(path, name, b, 0, b.length, flags, followlink);\n    }\n\n    public FCFSFileStat stat(String path) {\n        final boolean followlink = true;\n        return this.stat(path, followlink);\n    }\n\n    public FCFSFileStat lstat(String path) {\n        final boolean followlink = false;\n        return this.stat(path, followlink);\n    }\n\n    public static void main(String[] args) throws Exception {\n        final String ns = \"fs\";\n        final String configFilename = \"/etc/fastcfs/fcfs/fuse.conf\";\n        final boolean followlink = false;\n        String path;\n        FCFSPosixAPI papi;\n\n        File f = new File(\"/usr/lib/libfcfsjni.so\");\n        if (f.exists()) {\n            FCFSPosixAPI.setLibraryFilename(f.getAbsolutePath());\n        } else {\n            FCFSPosixAPI.setLibraryFilename(\"/usr/local/lib/libfcfsjni.so\");\n        }\n\n        papi = FCFSPosixAPI.getInstance(ns, configFilename);\n\n        path = \"/opt/fastcfs/fuse/\";\n        String filename = path + \"test.txt\";\n        String filename1 = filename;\n        String filename2 = path + \"test2.txt\";\n\n        FCFSDirectory dir = papi.opendir(path);\n        FCFSDirectory.Entry dirent;\n        FCFSFile file;\n\n        System.out.println(\"cwd: \" + papi.getcwd());\n        while ((dirent=dir.next()) != null) {\n            //System.out.println(\"inode: \" + dirent.getInode() + \", name: \" + dirent.getName());\n        }\n        dir.close();\n\n        try {\n            papi.unlink(filename1);\n        } catch(Exception ex) {\n        }\n\n        if (!papi.exists(filename1)) {\n            file = papi.open(filename1, FCFSConstants.O_WRONLY |\n                    FCFSConstants.O_CREAT, 0755);\n            file.write(new String(\"hello world!\").getBytes(charset));\n            file.close();\n\n            System.out.println(filename1 + \" created\");\n        }\n\n        try {\n            papi.unlink(filename2);\n        } catch(Exception ex) {\n        }\n        papi.symlink(\"./test.txt\", filename2);\n        //papi.symlink(filename1, filename2);\n        System.out.println(\"readlink: \" + papi.readlink(filename2));\n\n        byte[] bs = new byte[1024];\n        file = papi.open(filename2);\n        int length = file.read(bs);\n        file.close();\n        System.out.println(\"content: \" + new String(bs, 0, length, charset));\n\n        System.out.println(\"fstat: \" + papi.stat(filename));\n        papi.truncate(filename1, 4 * 1024);\n        System.out.println(\"fstat: \" + papi.stat(filename2));\n\n        //System.out.println(\"statvfs: \" + papi.statvfs(path));\n\n        papi.setxattr(path, \"myxattr\", new String(\"myvalue\").getBytes(charset));\n\n        List<String> list;\n        list = papi.listxattr(path, followlink);\n        for (String name : list) {\n            System.out.println(\"name: \" + name + \", value: \"\n                    + new String(papi.getxattr(path, name, followlink), charset));\n        }\n\n\n        file = papi.open(filename);\n        System.out.println(\"fstat: \" + file.stat());\n        //System.out.println(\"fstatvfs: \" + file.statvfs());\n\n        list = file.listxattr();\n        for (String name : list) {\n            System.out.println(\"name: \" + name + \", value: \"\n                    + new String(file.getxattr(name), charset));\n        }\n\n        file.close();\n\n        papi.close();\n    }\n}\n"
  },
  {
    "path": "src/java/src/main/java/com/fastken/fcfs/FCFSVFSStat.java",
    "content": "package com.fastken.fcfs;\n  \npublic class FCFSVFSStat {\n    public static class Stat {\n        private long total;\n        private long avail;\n        private long used;\n\n        public Stat(long total, long avail, long used) {\n            this.total = total;\n            this.avail = avail;\n            this.used = used;\n        }\n\n        public long getTotal() {\n            return this.total;\n        }\n\n        public long getAvail() {\n            return this.avail;\n        }\n\n        public long getUsed() {\n            return this.used;\n        }\n\n        public String toString() {\n            return \"total: \" + this.total\n                + \", avail: \" + this.avail\n                + \", used: \" + this.used;\n        }\n    }\n\n\n    private Stat space;\n    private Stat inode;\n\n    static {\n        doInit();\n    }\n\n    private static native void doInit();\n\n    public FCFSVFSStat(long spaceTotal, long spaceAvail, long spaceUsed,\n            long inodeTotal, long inodeAvail, long inodeUsed)\n    {\n        this.space = new Stat(spaceTotal, spaceAvail, spaceUsed);\n        this.inode = new Stat(inodeTotal, inodeAvail, inodeUsed);\n    }\n\n    public Stat getSpaceStat() {\n        return this.space;\n    }\n\n    public Stat getInodeStat() {\n        return this.inode;\n    }\n\n    public String toString() {\n        return \"space { \" + this.space.toString() + \" }\"\n            + \", inode { \" + this.inode.toString() + \" }\";\n    }\n}\n"
  },
  {
    "path": "src/preload/Makefile.in",
    "content": ".SUFFIXES: .c .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I../common -I../include\nLIB_PATH = $(LIBS) -L../api -ldl -lfcfsapi -lfsclient -lfsapi -lfdirclient -lfastcommon -lserverframe -lfcfsauthclient\nTARGET_LIB = $(TARGET_PREFIX)/$(LIB_VERSION)\n\nSHARED_OBJS =  global.lo api.lo\n\nALL_OBJS = $(SHARED_OBJS)\n\nALL_PRGS = \nSHARED_LIBS = libfcfspreload.so\nALL_LIBS = $(SHARED_LIBS)\n\nall: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)\n\nlibfcfspreload.so: $(SHARED_OBJS)\n\t$(COMPILE) -o $@ -shared $(SHARED_OBJS) $(LIB_PATH)\n\n.c.lo:\n\t$(COMPILE) -c -fPIC -o $@ $<  $(INC_PATH)\n\ninstall:\n\tmkdir -p $(TARGET_LIB)\n\tmkdir -p $(TARGET_PREFIX)/lib\n\n\tinstall -m 755 $(SHARED_LIBS) $(TARGET_LIB)\n\t@BUILDROOT=$$(echo \"$(TARGET_PREFIX)\" | grep BUILDROOT); \\\n\tif [ -z \"$$BUILDROOT\" ] && [ ! -e $(TARGET_PREFIX)/lib/libfcfspreload.so ]; then ln -s $(TARGET_LIB)/libfcfspreload.so $(TARGET_PREFIX)/lib/libfcfspreload.so; fi\nclean:\n\trm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)\n\n"
  },
  {
    "path": "src/preload/api.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdarg.h>\n#include <dlfcn.h>\n#include <sys/syscall.h>\n#include \"fastcommon/logger.h\"\n#include \"global.h\"\n#include \"api.h\"\n\n#ifdef FCFS_PRELOAD_WITH_PAPI\nstatic int counter = 0;\n#endif\n\n#define FCFS_LOG_DEBUG(format, ...)  \\\n    if (g_fcfs_preload_global_vars.inited && g_log_context. \\\n            log_level >= LOG_DEBUG) logDebug(format, ##__VA_ARGS__)\n\n//            log_level >= LOG_DEBUG) fprintf(stderr, format, ##__VA_ARGS__)\n\n\n__attribute__ ((constructor)) static void preload_global_init(void)\n{\n    int result;\n    pid_t pid;\n    char *ns = \"fs\";\n    char *config_filename;\n\n    config_filename = getenv(\"FCFS_PRELOAD_CONFIG_FILENAME\");\n    if (config_filename == NULL) {\n        config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME;\n    }\n\n    log_init();\n    pid = getpid();\n    fprintf(stderr, \"pid: %d, file: \"__FILE__\", line: %d, inited: %d, \"\n            \"constructor, config_filename: %s\\n\", pid, __LINE__,\n            g_fcfs_preload_global_vars.inited, config_filename);\n\n    if ((result=fcfs_preload_global_init()) != 0) {\n        return;\n    }\n\n#ifdef FCFS_PRELOAD_WITH_CAPI\n    if ((result=fcfs_capi_init()) != 0) {\n        return;\n    }\n#endif\n\n    FCFS_LOG_DEBUG(\"file: \"__FILE__\", line: %d, pid: %d, \"\n            \"constructor\\n\", __LINE__, pid);\n\n    log_set_fd_flags(&g_log_context, O_CLOEXEC);\n    if ((result=fcfs_posix_api_init(\"fcfs_preload\",\n                    ns, config_filename)) != 0)\n    {\n        return;\n    }\n\n    FCFS_LOG_DEBUG(\"pid: %d, file: \"__FILE__\", line: %d, \"\n            \"constructor\\n\", pid, __LINE__);\n    if ((result=fcfs_posix_api_start()) != 0) {\n        return;\n    }\n\n    g_fcfs_preload_global_vars.cwd_call_type =\n        FCFS_PRELOAD_CALL_SYSTEM;\n    g_fcfs_preload_global_vars.inited = true;\n\n    FCFS_LOG_DEBUG(\"pid: %d, file: \"__FILE__\", line: %d, inited: %d, \"\n            \"log_fd: %d\\n\", pid, __LINE__, g_fcfs_preload_global_vars.inited,\n            g_log_context.log_fd);\n\n    fprintf(stderr, \"pid: %d, parent pid: %d, base path: %s, \"\n            \"log_level: %d, log_fd: %d\\n\", getpid(), getppid(),\n            SF_G_BASE_PATH_STR, g_log_context.log_level,\n            g_log_context.log_fd);\n}\n\n__attribute__ ((destructor)) static void preload_global_destroy(void)\n{\n    FCFS_LOG_DEBUG(\"pid: %d, file: \"__FILE__\", line: %d, \"\n            \"destructor\\n\", getpid(), __LINE__);\n    if (g_fcfs_preload_global_vars.inited) {\n        fcfs_posix_api_stop();\n    }\n}\n\nstatic inline void *fcfs_dlsym1(const char *fname)\n{\n    void *func;\n\n    if ((func=dlsym(RTLD_NEXT, fname)) == NULL) {\n        FCFS_LOG_DEBUG(\"file: \"__FILE__\", line: %d, \"\n                \"function %s not exist!\", __LINE__, fname);\n    }\n    return func;\n}\n\nstatic inline void *fcfs_dlsym2(const char *fname1, const char *fname2)\n{\n    void *func;\n\n    if ((func=dlsym(RTLD_NEXT, fname1)) != NULL) {\n        return func;\n    }\n\n    if ((func=dlsym(RTLD_NEXT, fname2)) == NULL) {\n        FCFS_LOG_DEBUG(\"file: \"__FILE__\", line: %d, \"\n                \"function %s | %s not exist!\", __LINE__,\n                fname1, fname2);\n    }\n    return func;\n}\n\n#ifdef FCFS_PRELOAD_WITH_PAPI\nstatic inline int do_open(const char *path, int flags, int mode)\n{\n    int fd;\n\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        fd = fcfs_open(path, flags, mode);\n    } else {\n        fd = syscall(SYS_open, path, flags, mode);\n    }\n\n    FCFS_LOG_DEBUG(\"%d. @func: %s, path: %s, mode: %o, fd: %d\\n\",\n            ++counter, __FUNCTION__, path, mode, fd);\n    return fd;\n}\n\nint _open_(const char *path, int flags, ...)\n{\n    va_list ap;\n    int mode;\n\n    va_start(ap, flags);\n    mode = va_arg(ap, int);\n    va_end(ap);\n\n    FCFS_LOG_DEBUG(\"#func: %s, path: %s, mode: %o\\n\", __FUNCTION__, path, mode);\n    return do_open(path, flags, mode);\n}\n\nint __open_nocancel(const char *path, int flags, ...)\n{\n    va_list ap;\n    int mode;\n\n    va_start(ap, flags);\n    mode = va_arg(ap, int);\n    va_end(ap);\n\n    FCFS_LOG_DEBUG(\"#func: %s, path: %s, mode: %o\\n\", __FUNCTION__, path, mode);\n    return do_open(path, flags, mode);\n}\n\nint open64(const char *path, int flags, ...)\n{\n    va_list ap;\n    int mode;\n\n    va_start(ap, flags);\n    mode = va_arg(ap, int);\n    va_end(ap);\n\n    FCFS_LOG_DEBUG(\"#func: %s, path: %s, mode: %o\\n\", __FUNCTION__, path, mode);\n    return do_open(path, flags, mode);\n}\n\nint __open(const char *path, int flags, int mode)\n{\n    FCFS_LOG_DEBUG(\"#func: %s, path: %s, mode: %o\\n\", __FUNCTION__, path, mode);\n    return do_open(path, flags, mode);\n}\n\nint __open_2_(const char *path, int flags)\n{\n    FCFS_LOG_DEBUG(\"#func: %s, path: %s\\n\", __FUNCTION__, path);\n    return do_open(path, flags, 0666);\n}\n\nint __open64_2_(const char *path, int flags)\n{\n    FCFS_LOG_DEBUG(\"#func: %s, path: %s\\n\", __FUNCTION__, path);\n    return do_open(path, flags, 0666);\n}\n\nstatic inline int do_creat(const char *path, mode_t mode)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_creat(path, mode);\n    } else {\n        return syscall(SYS_creat, path, mode);\n    }\n}\n\nint _creat_(const char *path, mode_t mode)\n{\n    return do_creat(path, mode);\n}\n\nint creat64(const char *path, mode_t mode)\n{\n    return do_creat(path, mode);\n}\n\nstatic inline int do_truncate(const char *path, off_t length)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_truncate(path, length);\n    } else {\n        return syscall(SYS_truncate, path, length);\n    }\n}\n\nint _truncate_(const char *path, off_t length)\n{\n    return do_truncate(path, length);\n}\n\nint truncate64(const char *path, off_t length)\n{\n    return do_truncate(path, length);\n}\n\nint lstat(const char *path, struct stat *buf)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, line: %d\\n\", ++counter, __FUNCTION__, __LINE__);\n\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_lstat(path, buf);\n    } else {\n        return syscall(SYS_lstat, path, buf);\n    }\n}\n\nstatic inline int do_lxstat(int ver, const char *path, struct stat *buf)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, ver: %d, path: %s\\n\", ++counter, __FUNCTION__, ver, path);\n\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_lstat(path, buf);\n    } else {\n        if (g_fcfs_preload_global_vars.__lxstat == NULL) {\n            g_fcfs_preload_global_vars.__lxstat = fcfs_dlsym1(\"__lxstat\");\n        }\n        return g_fcfs_preload_global_vars.__lxstat(ver, path, buf);\n    }\n}\n\nint __lxstat_(int ver, const char *path, struct stat *buf)\n{\n    return do_lxstat(ver, path, buf);\n}\n\nint __lxstat64(int ver, const char *path, struct stat64 *buf)\n{\n    return do_lxstat(ver, path, (struct stat *)buf);\n}\n\nint stat(const char *path, struct stat *buf)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, line: %d, path: %s\\n\", ++counter, __FUNCTION__, __LINE__, path);\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_stat(path, buf);\n    } else {\n        return syscall(SYS_stat, path, buf);\n    }\n}\n\nstatic inline int do_xstat(int ver, const char *path, struct stat *buf)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, ver: %d, path: %s\\n\", ++counter, __FUNCTION__, ver, path);\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_stat(path, buf);\n    } else {\n        if (g_fcfs_preload_global_vars.__xstat == NULL) {\n            g_fcfs_preload_global_vars.__xstat = fcfs_dlsym1(\"__xstat\");\n        }\n        return g_fcfs_preload_global_vars.__xstat(ver, path, buf);\n    }\n}\n\nint __xstat_(int ver, const char *path, struct stat *buf)\n{\n    return do_xstat(ver, path, buf);\n}\n\nint __xstat64(int ver, const char *path, struct stat64 *buf)\n{\n    return do_xstat(ver, path, (struct stat *)buf);\n}\n\nint link(const char *path1, const char *path2)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path1) ||\n            FCFS_PRELOAD_IS_MY_MOUNTPOINT(path2))\n    {\n        return fcfs_link(path1, path2);\n    } else {\n        return syscall(SYS_link, path1, path2);\n    }\n}\n\nint symlink(const char *link, const char *path)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_symlink(link, path);\n    } else {\n        return syscall(SYS_symlink, link, path);\n    }\n}\n\nssize_t readlink(const char *path, char *buff, size_t size)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_readlink(path, buff, size);\n    } else {\n        return syscall(SYS_readlink, path, buff, size);\n    }\n}\n\nint mknod(const char *path, mode_t mode, dev_t dev)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_mknod(path, mode, dev);\n    } else {\n        return syscall(SYS_mknod, path, mode, dev);\n    }\n}\n\nint __xmknod(int ver, const char *path, mode_t mode, dev_t *dev)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_mknod(path, mode, *dev);\n    } else {\n        if (g_fcfs_preload_global_vars.__xmknod == NULL) {\n            g_fcfs_preload_global_vars.__xmknod = fcfs_dlsym1(\"__xmknod\");\n        }\n        return g_fcfs_preload_global_vars.__xmknod(ver, path, mode, dev);\n    }\n}\n\nint mkfifo(const char *path, mode_t mode)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_mkfifo(path, mode);\n    } else {\n        if (g_fcfs_preload_global_vars.mkfifo == NULL) {\n            g_fcfs_preload_global_vars.mkfifo = fcfs_dlsym1(\"mkfifo\");\n        }\n        return g_fcfs_preload_global_vars.mkfifo(path, mode);\n    }\n}\n\nint access(const char *path, int mode)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, path: %s, mode: %o\\n\",\n            ++counter, __FUNCTION__, path, mode);\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_access(path, mode);\n    } else {\n        return syscall(SYS_access, path, mode);\n    }\n}\n\nint eaccess(const char *path, int mode)\n{\n    FCFS_LOG_DEBUG(\"func: %s, path: %s, mode: %o\\n\", __FUNCTION__, path, mode);\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_eaccess(path, mode);\n    } else {\n        if (g_fcfs_preload_global_vars.eaccess == NULL) {\n            g_fcfs_preload_global_vars.eaccess = fcfs_dlsym1(\"eaccess\");\n        }\n        return g_fcfs_preload_global_vars.eaccess(path, mode);\n    }\n}\n\nint euidaccess(const char *path, int mode)\n{\n    FCFS_LOG_DEBUG(\"func: %s, path: %s, mode: %o\\n\", __FUNCTION__, path, mode);\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_euidaccess(path, mode);\n    } else {\n        if (g_fcfs_preload_global_vars.euidaccess == NULL) {\n            g_fcfs_preload_global_vars.euidaccess = fcfs_dlsym1(\"euidaccess\");\n        }\n        return g_fcfs_preload_global_vars.euidaccess(path, mode);\n    }\n}\n\nint utime(const char *path, const struct utimbuf *times)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_utime(path, times);\n    } else {\n        return syscall(SYS_utime, path, times);\n    }\n}\n\nint utimes(const char *path, const struct timeval times[2])\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_utimes(path, times);\n    } else {\n        return syscall(SYS_utimes, path, times);\n    }\n}\n\nint unlink(const char *path)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_unlink(path);\n    } else {\n        return syscall(SYS_unlink, path);\n    }\n}\n\nint rename(const char *path1, const char *path2)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path1) ||\n            FCFS_PRELOAD_IS_MY_MOUNTPOINT(path2))\n    {\n        return fcfs_rename(path1, path2);\n    } else {\n        return syscall(SYS_rename, path1, path2);\n    }\n}\n\nint mkdir(const char *path, mode_t mode)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_mkdir(path, mode);\n    } else {\n        return syscall(SYS_mkdir, path, mode);\n    }\n}\n\nint rmdir(const char *path)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_rmdir(path);\n    } else {\n        return syscall(SYS_rmdir, path);\n    }\n}\n\nint chown(const char *path, uid_t owner, gid_t group)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_chown(path, owner, group);\n    } else {\n        return syscall(SYS_chown, path, owner, group);\n    }\n}\n\nint lchown(const char *path, uid_t owner, gid_t group)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_lchown(path, owner, group);\n    } else {\n        return syscall(SYS_lchown, path, owner, group);\n    }\n}\n\nint chmod(const char *path, mode_t mode)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_chmod(path, mode);\n    } else {\n        return syscall(SYS_chmod, path, mode);\n    }\n}\n\nstatic inline int do_statvfs(const char *path, struct statvfs *buf)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_statvfs(path, buf);\n    } else {\n        if (g_fcfs_preload_global_vars.statvfs == NULL) {\n            g_fcfs_preload_global_vars.statvfs = fcfs_dlsym2(\n                    \"statvfs\", \"statvfs64\");\n        }\n        return g_fcfs_preload_global_vars.statvfs(path, buf);\n    }\n}\n\nint _statvfs_(const char *path, struct statvfs *buf)\n{\n    return do_statvfs(path, buf);\n}\n\nint statvfs64(const char *path, struct statvfs64 *buf)\n{\n    return do_statvfs(path, (struct statvfs *)buf);\n}\n\nint setxattr(const char *path, const char *name,\n        const void *value, size_t size, int flags)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_setxattr(path, name, value, size, flags);\n    } else {\n        return syscall(SYS_setxattr, path, name, value, size, flags);\n    }\n}\n\nint lsetxattr(const char *path, const char *name,\n        const void *value, size_t size, int flags)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_lsetxattr(path, name, value, size, flags);\n    } else {\n        return syscall(SYS_lsetxattr, path, name, value, size, flags);\n    }\n}\n\nssize_t getxattr(const char *path, const char *name, void *value, size_t size)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_getxattr(path, name, value, size);\n    } else {\n        return syscall(SYS_getxattr, path, name, value, size);\n    }\n}\n\nssize_t lgetxattr(const char *path, const char *name, void *value, size_t size)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_lgetxattr(path, name, value, size);\n    } else {\n        return syscall(SYS_lgetxattr, path, name, value, size);\n    }\n}\n\nssize_t listxattr(const char *path, char *list, size_t size)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_listxattr(path, list, size);\n    } else {\n        return syscall(SYS_listxattr, path, list, size);\n    }\n}\n\nssize_t llistxattr(const char *path, char *list, size_t size)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_llistxattr(path, list, size);\n    } else {\n        return syscall(SYS_llistxattr, path, list, size);\n    }\n}\n\nint removexattr(const char *path, const char *name)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_removexattr(path, name);\n    } else {\n        return syscall(SYS_removexattr, path, name);\n    }\n}\n\nint lremovexattr(const char *path, const char *name)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_lremovexattr(path, name);\n    } else {\n        return syscall(SYS_lremovexattr, path, name);\n    }\n}\n\nint chdir(const char *path)\n{\n    int result;\n\n    FCFS_LOG_DEBUG(\"%d. func: %s, line: %d, path: %s\\n\",\n            ++counter, __FUNCTION__, __LINE__, path);\n\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        if ((result=fcfs_chdir(path)) == 0) {\n            g_fcfs_preload_global_vars.cwd_call_type =\n                FCFS_PRELOAD_CALL_FASTCFS;\n        }\n    } else {\n        if ((result=syscall(SYS_chdir, path)) == 0) {\n            g_fcfs_preload_global_vars.cwd_call_type =\n                FCFS_PRELOAD_CALL_SYSTEM;\n        }\n    }\n\n    return result;\n}\n\nint chroot(const char *path)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_chroot(path);\n    } else {\n        return syscall(SYS_chroot, path);\n    }\n}\n\nDIR *opendir(const char *path)\n{\n    FCFSPreloadDIRWrapper *wapper;\n    DIR *dirp;\n    int call_type;\n\n    FCFS_LOG_DEBUG(\"%d. func: %s, path: %s\\n\", ++counter, __FUNCTION__, path);\n\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        call_type = FCFS_PRELOAD_CALL_FASTCFS;\n        dirp = (DIR *)fcfs_opendir(path);\n    } else {\n        call_type = FCFS_PRELOAD_CALL_SYSTEM;\n        if (g_fcfs_preload_global_vars.opendir == NULL) {\n            g_fcfs_preload_global_vars.opendir = fcfs_dlsym1(\"opendir\");\n        }\n        dirp = g_fcfs_preload_global_vars.opendir(path);\n    }\n\n    if (dirp != NULL) {\n        wapper = fc_malloc(sizeof(FCFSPreloadDIRWrapper));\n        wapper->call_type = call_type;\n        wapper->dirp = dirp;\n        return (DIR *)wapper;\n    } else {\n        return NULL;\n    }\n}\n\nstatic inline int do_scandir(const char *path, struct dirent ***namelist,\n        fcfs_dir_filter_func filter, fcfs_dir_compare_func compar)\n{\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        return fcfs_scandir(path, namelist, filter, compar);\n    } else {\n        if (g_fcfs_preload_global_vars.scandir == NULL) {\n            g_fcfs_preload_global_vars.scandir = fcfs_dlsym1(\"scandir\");\n        }\n        return g_fcfs_preload_global_vars.scandir(path, namelist, filter, compar);\n    }\n}\n\nint _scandir_(const char *path, struct dirent ***namelist,\n        fcfs_dir_filter_func filter, fcfs_dir_compare_func compar)\n{\n    return do_scandir(path, namelist, filter, compar);\n}\n\nint scandir64(const char * path, struct dirent64 ***namelist,\n        int (*filter) (const struct dirent64 *),\n        int (*compar) (const struct dirent64 **,\n            const struct dirent64 **))\n{\n    return do_scandir(path, (struct dirent ***)namelist,\n            (fcfs_dir_filter_func)filter,\n            (fcfs_dir_compare_func)compar);\n}\n\nint close(int fd)\n{\n    FCFS_LOG_DEBUG(\"%d. pid: %d, func: %s, line: %d, fd: %d\\n\", ++counter,\n            getpid(), __FUNCTION__, __LINE__, fd);\n\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_close(fd);\n    } else {\n        return syscall(SYS_close, fd);\n    }\n}\n\nint fsync(int fd)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fsync(fd);\n    } else {\n        return syscall(SYS_fsync, fd);\n    }\n}\n\nint fdatasync(int fd)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fdatasync(fd);\n    } else {\n        return syscall(SYS_fdatasync, fd);\n    }\n}\n\nssize_t write(int fd, const void *buff, size_t count)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_write(fd, buff, count);\n    } else {\n        return syscall(SYS_write, fd, buff, count);\n    }\n}\n\nstatic inline ssize_t do_pwrite(int fd, const void *buff,\n        size_t count, off_t offset)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_pwrite(fd, buff, count, offset);\n    } else {\n        return syscall(SYS_pwrite64, fd, buff, count, offset);\n    }\n}\n\nssize_t _pwrite_(int fd, const void *buff, size_t count, off_t offset)\n{\n    return do_pwrite(fd, buff, count, offset);\n}\n\nssize_t pwrite64(int fd, const void *buff, size_t count, off_t offset)\n{\n    return do_pwrite(fd, buff, count, offset);\n}\n\nssize_t writev(int fd, const struct iovec *iov, int iovcnt)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_writev(fd, iov, iovcnt);\n    } else {\n        return syscall(SYS_writev, fd, iov, iovcnt);\n    }\n}\n\nstatic inline ssize_t do_pwritev(int fd, const struct iovec *iov,\n        int iovcnt, off_t offset)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_pwritev(fd, iov, iovcnt, offset);\n    } else {\n        return syscall(SYS_writev, fd, iov, iovcnt, offset);\n    }\n}\n\nssize_t _pwritev_(int fd, const struct iovec *iov, int iovcnt, off_t offset)\n{\n    return do_pwritev(fd, iov, iovcnt, offset);\n}\n\nssize_t pwritev64(int fd, const struct iovec *iov, int iovcnt, off_t offset)\n{\n    return do_pwritev(fd, iov, iovcnt, offset);\n}\n\nssize_t read(int fd, void *buff, size_t count)\n{\n    FCFS_LOG_DEBUG(\"%d. line: %d, func: %s, fd: %d, count: %d\\n\",\n            ++counter, __LINE__, __FUNCTION__, fd, (int)count);\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_read(fd, buff, count);\n    } else {\n        return syscall(SYS_read, fd, buff, count);\n    }\n}\n\nssize_t __read_chk(int fd, void *buff, size_t count, size_t size)\n{\n    /*\n    fprintf(stderr, \"==== line: %d, func: %s, fd: %d, count: %d, size: %d ====\\n\",\n            __LINE__, __FUNCTION__, fd, (int)count, (int)size);\n            */\n    return read(fd, buff, size > 0 ? FC_MIN(count, size) : count);\n}\n\nssize_t readahead(int fd, off64_t offset, size_t count)\n{\n    FCFS_LOG_DEBUG(\"func: %s, fd: %d\\n\", __FUNCTION__, fd);\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_readahead(fd, offset, count);\n    } else {\n        return syscall(SYS_readahead, fd, offset, count);\n    }\n}\n\nstatic inline ssize_t do_pread(int fd, void *buff, size_t count, off_t offset)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d\\n\", ++counter, __FUNCTION__, fd);\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_pread(fd, buff, count, offset);\n    } else {\n        return syscall(SYS_pread64, fd, buff, count, offset);\n    }\n}\n\nssize_t _pread_(int fd, void *buff, size_t count, off_t offset)\n{\n    return do_pread(fd, buff, count, offset);\n}\n\nssize_t pread64(int fd, void *buff, size_t count, off_t offset)\n{\n    return do_pread(fd, buff, count, offset);\n}\n\nssize_t __pread_chk(int fd, void *buff, size_t count,\n        off_t offset, size_t size)\n{\n    /*\n    fprintf(stderr, \"==== line: %d, func: %s, fd: %d, count: %d, size: %d ====\\n\",\n            __LINE__, __FUNCTION__, fd, (int)count, (int)size);\n            */\n    return do_pread(fd, buff, size > 0 ? FC_MIN(count, size) : count, offset);\n}\n\nssize_t __pread64_chk(int fd, void *buff, size_t count,\n        off_t offset, size_t size)\n{\n    /*\n    fprintf(stderr, \"==== line: %d, func: %s, fd: %d, count: %d, size: %d ====\\n\",\n            __LINE__, __FUNCTION__, fd, (int)count, (int)size);\n            */\n    return do_pread(fd, buff, size > 0 ? FC_MIN(count, size) : count, offset);\n}\n\nssize_t readv(int fd, const struct iovec *iov, int iovcnt)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d\\n\", ++counter, __FUNCTION__, fd);\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_readv(fd, iov, iovcnt);\n    } else {\n        return syscall(SYS_readv, fd, iov, iovcnt);\n    }\n}\n\nstatic inline ssize_t do_preadv(int fd, const struct iovec *iov,\n        int iovcnt, off_t offset)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_preadv(fd, iov, iovcnt, offset);\n    } else {\n        return syscall(SYS_preadv, fd, iov, iovcnt, offset);\n    }\n}\n\nssize_t _preadv_(int fd, const struct iovec *iov, int iovcnt, off_t offset)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d\\n\", ++counter, __FUNCTION__, fd);\n    return do_preadv(fd, iov, iovcnt, offset);\n}\n\nssize_t preadv64(int fd, const struct iovec *iov, int iovcnt, off_t offset)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d\\n\", ++counter, __FUNCTION__, fd);\n    return do_preadv(fd, iov, iovcnt, offset);\n}\n\nstatic inline off_t do_lseek(int fd, off_t offset, int whence)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_lseek(fd, offset, whence);\n    } else {\n        return syscall(SYS_lseek, fd, offset, whence);\n    }\n}\n\noff_t _lseek_(int fd, off_t offset, int whence)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d\\n\", ++counter, __FUNCTION__, fd);\n    return do_lseek(fd, offset, whence);\n}\n\noff_t __lseek(int fd, off_t offset, int whence)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d\\n\", ++counter, __FUNCTION__, fd);\n    return do_lseek(fd, offset, whence);\n}\n\noff_t lseek64(int fd, off_t offset, int whence)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d\\n\", ++counter, __FUNCTION__, fd);\n    return do_lseek(fd, offset, whence);\n}\n\nstatic inline int do_fallocate(int fd, int mode, off_t offset, off_t length)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fallocate(fd, mode, offset, length);\n    } else {\n        return syscall(SYS_fallocate, fd, mode, offset, length);\n    }\n}\n\nint _fallocate_(int fd, int mode, off_t offset, off_t length)\n{\n    return do_fallocate(fd, mode, offset, length);\n}\n\nint fallocate64(int fd, int mode, off_t offset, off_t length)\n{\n    return do_fallocate(fd, mode, offset, length);\n}\n\nstatic inline int do_ftruncate(int fd, off_t length)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_ftruncate(fd, length);\n    } else {\n        return syscall(SYS_ftruncate, fd, length);\n    }\n}\n\nint _ftruncate_(int fd, off_t length)\n{\n    return do_ftruncate(fd, length);\n}\n\nint ftruncate64(int fd, off_t length)\n{\n    return do_ftruncate(fd, length);\n}\n\nint fstat(int fd, struct stat *buf)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d\\n\", ++counter, __FUNCTION__, fd);\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fstat(fd, buf);\n    } else {\n        return syscall(SYS_fstat, fd, buf);\n    }\n}\n\nstatic inline int do_fxstat(int ver, int fd, struct stat *buf)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, ver: %d, fd: %d\\n\", ++counter, __FUNCTION__, ver, fd);\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fstat(fd, buf);\n    } else {\n        if (g_fcfs_preload_global_vars.__fxstat == NULL) {\n            g_fcfs_preload_global_vars.__fxstat = fcfs_dlsym1(\"__fxstat\");\n        }\n        return g_fcfs_preload_global_vars.__fxstat(ver, fd, buf);\n    }\n}\n\nint __fxstat_(int ver, int fd, struct stat *buf)\n{\n    FCFS_LOG_DEBUG(\"func: %s, ver: %d, fd: %d\\n\", __FUNCTION__, ver, fd);\n    return do_fxstat(ver, fd, buf);\n}\n\nint __fxstat64(int ver, int fd, struct stat64 *buf)\n{\n    FCFS_LOG_DEBUG(\"func: %s, ver: %d, fd: %d\\n\", __FUNCTION__, ver, fd);\n    return do_fxstat(ver, fd, (struct stat *)buf);\n}\n\nint flock(int fd, int operation)\n{\n    FCFS_LOG_DEBUG(\"func: %s, fd: %d, operation: %d\\n\", __FUNCTION__, fd, operation);\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_flock(fd, operation);\n    } else {\n        return syscall(SYS_flock, fd, operation);\n    }\n}\n\nstatic int do_fcntl(int fd, int cmd, void *arg)\n{\n    struct flock *lock;\n    int flags;\n\n    switch (cmd) {\n        case F_GETLK:\n        case F_SETLK:\n        case F_SETLKW:\n            lock = (struct flock *)arg;\n            if (FCFS_PAPI_IS_MY_FD(fd)) {\n                return fcfs_fcntl(fd, cmd, lock); \n            } else {\n                return syscall(SYS_fcntl, fd, cmd, lock);\n            }\n        default:\n            flags = (long)arg;\n\n            FCFS_LOG_DEBUG(\"func: %s, fd: %d, cmd: %d, flags: %d\\n\",\n                    __FUNCTION__, fd, cmd, flags);\n\n            if (FCFS_PAPI_IS_MY_FD(fd)) {\n                return fcfs_fcntl(fd, cmd, flags); \n            } else {\n                return syscall(SYS_fcntl, fd, cmd, flags);\n            }\n    }\n}\n\nint _fcntl_(int fd, int cmd, ...)\n{\n    va_list ap;\n    void *arg;\n\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d, cmd: %d\\n\", ++counter, __FUNCTION__, fd, cmd);\n    va_start(ap, cmd);\n    arg = va_arg(ap, void *);\n    va_end(ap);\n\n    return do_fcntl(fd, cmd, arg);\n}\n\nint fcntl64(int fd, int cmd, ...)\n{\n    va_list ap;\n    void *arg;\n\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d, cmd: %d\\n\", ++counter, __FUNCTION__, fd, cmd);\n    va_start(ap, cmd);\n    arg = va_arg(ap, void *);\n    va_end(ap);\n\n    return do_fcntl(fd, cmd, arg);\n}\n\nint futimes(int fd, const struct timeval times[2])\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_futimes(fd, times);\n    } else {\n        if (g_fcfs_preload_global_vars.futimes == NULL) {\n            g_fcfs_preload_global_vars.futimes = fcfs_dlsym1(\"futimes\");\n        }\n        return g_fcfs_preload_global_vars.futimes(fd, times);\n    }\n}\n\nint futimens(int fd, const struct timespec times[2])\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_futimens(fd, times);\n    } else {\n        if (g_fcfs_preload_global_vars.futimens == NULL) {\n            g_fcfs_preload_global_vars.futimens = fcfs_dlsym1(\"futimens\");\n        }\n        return g_fcfs_preload_global_vars.futimens(fd, times);\n    }\n}\n\nint fchown(int fd, uid_t owner, gid_t group)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fchown(fd, owner, group);\n    } else {\n        return syscall(SYS_fchown, fd, owner, group);\n    }\n}\n\nint fchmod(int fd, mode_t mode)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fchmod(fd, mode);\n    } else {\n        return syscall(SYS_fchmod, fd, mode);\n    }\n}\n\nint fsetxattr(int fd, const char *name, const\n        void *value, size_t size, int flags)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fsetxattr(fd, name, value, size, flags);\n    } else {\n        return syscall(SYS_fsetxattr, fd, name, value, size, flags);\n    }\n}\n\nssize_t fgetxattr(int fd, const char *name, void *value, size_t size)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fgetxattr(fd, name, value, size);\n    } else {\n        return syscall(SYS_fgetxattr, fd, name, value, size);\n    }\n}\n\nssize_t flistxattr(int fd, char *list, size_t size)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_flistxattr(fd, list, size);\n    } else {\n        return syscall(SYS_flistxattr, fd, list, size);\n    }\n}\n\nint fremovexattr(int fd, const char *name)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fremovexattr(fd, name);\n    } else {\n        return syscall(SYS_fremovexattr, fd, name);\n    }\n}\n\nint fchdir(int fd)\n{\n    int result;\n\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        if ((result=fcfs_fchdir(fd)) == 0) {\n            g_fcfs_preload_global_vars.cwd_call_type =\n                FCFS_PRELOAD_CALL_FASTCFS;\n        }\n    } else {\n        if ((result=syscall(SYS_fchdir, fd)) == 0) {\n            g_fcfs_preload_global_vars.cwd_call_type =\n                FCFS_PRELOAD_CALL_SYSTEM;\n        }\n    }\n\n    return result;\n}\n\nstatic inline int do_fstatvfs(int fd, struct statvfs *buf)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_fstatvfs(fd, buf);\n    } else {\n        if (g_fcfs_preload_global_vars.fstatvfs == NULL) {\n            g_fcfs_preload_global_vars.fstatvfs = fcfs_dlsym2(\n                    \"fstatvfs\", \"fstatvfs64\");\n        }\n        return g_fcfs_preload_global_vars.fstatvfs(fd, buf);\n    }\n}\n\nint _fstatvfs_(int fd, struct statvfs *buf)\n{\n    return do_fstatvfs(fd, buf);\n}\n\nint fstatvfs64(int fd, struct statvfs64 *buf)\n{\n    return do_fstatvfs(fd, (struct statvfs *)buf);\n}\n\nint dup(int fd)\n{\n    FCFS_LOG_DEBUG(\"#func: %s, fd: %d\\n\", __FUNCTION__, fd);\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_dup(fd);\n    } else {\n        return syscall(SYS_dup, fd);\n    }\n}\n\nint dup2(int fd1, int fd2)\n{\n    FCFS_LOG_DEBUG(\"#func: %s, fd1: %d, fd2: %d\\n\", __FUNCTION__, fd1, fd2);\n    if (FCFS_PAPI_IS_MY_FD(fd1) && FCFS_PAPI_IS_MY_FD(fd2)) {\n        return fcfs_dup2(fd1, fd2);\n    } else {\n        return syscall(SYS_dup2, fd1, fd2);\n    }\n}\n\nstatic inline void *do_mmap(void *addr, size_t length, int prot,\n        int flags, int fd, off_t offset)\n{\n    FCFS_LOG_DEBUG(\"func: %s, fd: %d, offset: %\"PRId64\"\\n\",\n            __FUNCTION__, fd, (int64_t)offset);\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_mmap(addr, length, prot, flags, fd, offset);\n    } else {\n        return (void *)syscall(SYS_mmap, addr, length,\n                prot, flags, fd, offset);\n    }\n}\n\nvoid *_mmap_(void *addr, size_t length, int prot,\n        int flags, int fd, off_t offset)\n{\n    return do_mmap(addr, length, prot, flags, fd, offset);\n}\n\nvoid *mmap64(void *addr, size_t length, int prot,\n        int flags, int fd, off_t offset)\n{\n    return do_mmap(addr, length, prot, flags, fd, offset);\n}\n\nDIR *fdopendir(int fd)\n{\n    FCFSPreloadDIRWrapper *wapper;\n    DIR *dirp;\n    int call_type;\n\n    FCFS_LOG_DEBUG(\"%d. func: %s, fd: %d\\n\", ++counter, __FUNCTION__, fd);\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        call_type = FCFS_PRELOAD_CALL_FASTCFS;\n        dirp = (DIR *)fcfs_fdopendir(fd);\n    } else {\n        call_type = FCFS_PRELOAD_CALL_SYSTEM;\n        if (g_fcfs_preload_global_vars.fdopendir == NULL) {\n            g_fcfs_preload_global_vars.fdopendir = fcfs_dlsym1(\"fdopendir\");\n        }\n        dirp = g_fcfs_preload_global_vars.fdopendir(fd);\n    }\n\n    if (dirp != NULL) {\n        wapper = fc_malloc(sizeof(FCFSPreloadDIRWrapper));\n        wapper->call_type = call_type;\n        wapper->dirp = dirp;\n        return (DIR *)wapper;\n    } else {\n        return NULL;\n    }\n}\n\nint symlinkat(const char *link, int fd, const char *path)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_symlinkat(link, fd, path);\n    } else {\n        return syscall(SYS_symlinkat, link, fd, path);\n    }\n}\n\nstatic inline int do_openat(int fd, const char *path, int flags, int mode)\n{\n    FCFS_LOG_DEBUG(\"func: %s, path: %s, mode: %o\\n\", __FUNCTION__, path, mode);\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_openat(fd, path, flags, mode);\n    } else {\n        return syscall(SYS_openat, fd, path, flags, mode);\n    }\n}\n\nint _openat_(int fd, const char *path, int flags, ...)\n{\n    va_list ap;\n    int mode;\n\n    va_start(ap, flags);\n    mode = va_arg(ap, int);\n    va_end(ap);\n\n    FCFS_LOG_DEBUG(\"func: %s, path: %s, mode: %o\\n\", __FUNCTION__, path, mode);\n    return do_openat(fd, path, flags, mode);\n}\n\nint openat64(int fd, const char *path, int flags, ...)\n{\n    va_list ap;\n    int mode;\n\n    va_start(ap, flags);\n    mode = va_arg(ap, int);\n    va_end(ap);\n\n    FCFS_LOG_DEBUG(\"func: %s, path: %s, mode: %o\\n\", __FUNCTION__, path, mode);\n    return do_openat(fd, path, flags, mode);\n}\n\nint __openat_2_(int fd, const char *path, int flags)\n{\n    FCFS_LOG_DEBUG(\"func: %s, path: %s\\n\", __FUNCTION__, path);\n    return do_openat(fd, path, flags, 0666);\n}\n\nint __openat64_2(int fd, const char *path, int flags)\n{\n    FCFS_LOG_DEBUG(\"func: %s, path: %s\\n\", __FUNCTION__, path);\n    return do_openat(fd, path, flags, 0666);\n}\n\nint fstatat(int fd, const char *path, struct stat *buf, int flags)\n{\n    FCFS_LOG_DEBUG(\"func: %s, fd: %d, path: %s\\n\", __FUNCTION__, fd, path);\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_fstatat(fd, path, buf, flags);\n    } else {\n        if (g_fcfs_preload_global_vars.fstatat == NULL) {\n            g_fcfs_preload_global_vars.fstatat = fcfs_dlsym1(\"fstatat\");\n        }\n        return g_fcfs_preload_global_vars.fstatat(fd, path, buf, flags);\n    }\n}\n\nstatic inline int do_fxstatat(int ver, int fd,\n        const char *path, struct stat *buf, int flags)\n{\n    FCFS_LOG_DEBUG(\"%d. line: %d, func: %s, fd: %d, path: %s\\n\", ++counter, __LINE__, __FUNCTION__, fd, path);\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_fstatat(fd, path, buf, flags);\n    } else {\n        if (g_fcfs_preload_global_vars.__fxstatat == NULL) {\n            g_fcfs_preload_global_vars.__fxstatat = fcfs_dlsym1(\"__fxstatat\");\n        }\n        return g_fcfs_preload_global_vars.__fxstatat(ver, fd, path, buf, flags);\n    }\n}\n\nint __fxstatat_(int ver, int fd, const char *path,\n        struct stat *buf, int flags)\n{\n    return do_fxstatat(ver, fd, path, buf, flags);\n}\n\nint __fxstatat64(int ver, int fd, const char *path,\n        struct stat64 *buf, int flags)\n{\n    return do_fxstatat(ver, fd, path, (struct stat *)buf, flags);\n}\n\nssize_t readlinkat(int fd, const char *path, char *buff, size_t size)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_readlinkat(fd, path, buff, size);\n    } else {\n        return syscall(SYS_readlinkat, fd, path, buff, size);\n    }\n}\n\nint mknodat(int fd, const char *path, mode_t mode, dev_t dev)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_mknodat(fd, path, mode, dev);\n    } else {\n        return syscall(SYS_mknodat, fd, path, mode, dev);\n    }\n}\n\nint __xmknodat(int ver, int fd, const char *path, mode_t mode, dev_t *dev)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_mknodat(fd, path, mode, *dev);\n    } else {\n        if (g_fcfs_preload_global_vars.__xmknodat == NULL) {\n            g_fcfs_preload_global_vars.__xmknodat = fcfs_dlsym1(\"__xmknodat\");\n        }\n        return g_fcfs_preload_global_vars.__xmknodat(ver, fd, path, mode, dev);\n    }\n}\n\nint mkfifoat(int fd, const char *path, mode_t mode)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_mkfifoat(fd, path, mode);\n    } else {\n        if (g_fcfs_preload_global_vars.mkfifoat == NULL) {\n            g_fcfs_preload_global_vars.mkfifoat = fcfs_dlsym1(\"mkfifoat\");\n        }\n        return g_fcfs_preload_global_vars.mkfifoat(fd, path, mode);\n    }\n}\n\nint faccessat(int fd, const char *path, int mode, int flags)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, path: %s, mode: %o\\n\", ++counter, __FUNCTION__, path, mode);\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_faccessat(fd, path, mode, flags);\n    } else {\n        return syscall(SYS_faccessat, fd, path, mode, flags);\n    }\n}\n\nint futimesat(int fd, const char *path, const struct timeval times[2])\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_futimesat(fd, path, times);\n    } else {\n        return syscall(SYS_futimesat, fd, path, times);\n    }\n}\n\nint utimensat(int fd, const char *path, const struct timespec times[2], int flags)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_utimensat(fd, path, times, flags);\n    } else {\n        return syscall(SYS_utimensat, fd, path, times, flags);\n    }\n}\n\nint unlinkat(int fd, const char *path, int flags)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_unlinkat(fd, path, flags);\n    } else {\n        return syscall(SYS_unlinkat, fd, path, flags);\n    }\n}\n\nint mkdirat(int fd, const char *path, mode_t mode)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_mkdirat(fd, path, mode);\n    } else {\n        return syscall(SYS_futimesat, fd, path, mode);\n    }\n}\n\nint fchownat(int fd, const char *path, uid_t owner, gid_t group, int flags)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_fchownat(fd, path, owner, group, flags);\n    } else {\n        return syscall(SYS_fchownat, fd, path, owner, group, flags);\n    }\n}\n\nint fchmodat(int fd, const char *path, mode_t mode, int flags)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_fchmodat(fd, path, mode, flags);\n    } else {\n        return syscall(SYS_fchmodat, fd, path, mode, flags);\n    }\n}\n\nint linkat(int fd1, const char *path1, int fd2, const char *path2, int flags)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd1, path1) ||\n            FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd2, path2))\n    {\n        return fcfs_linkat(fd1, path1, fd2, path2, flags);\n    } else {\n        return syscall(SYS_linkat, fd1, path1, fd2, path2, flags);\n    }\n}\n\nint renameat(int fd1, const char *path1, int fd2, const char *path2)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd1, path1) ||\n            FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd2, path2))\n    {\n        return fcfs_renameat(fd1, path1, fd2, path2);\n    } else {\n        return syscall(SYS_renameat, fd1, path1, fd2, path2);\n    }\n}\n\nint renameat2(int fd1, const char *path1, int fd2,\n        const char *path2, unsigned int flags)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd1, path1) ||\n            FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd2, path2))\n    {\n        return fcfs_renameat2(fd1, path1, fd2, path2, flags);\n    } else {\n        return syscall(SYS_renameat2, fd1, path1, fd2, path2, flags);\n    }\n}\n\nstatic inline int do_scandirat(int fd, const char *path,\n        struct dirent ***namelist, fcfs_dir_filter_func filter,\n        fcfs_dir_compare_func compar)\n{\n    if (FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path)) {\n        return fcfs_scandirat(fd, path, namelist, filter, compar);\n    } else {\n        if (g_fcfs_preload_global_vars.scandirat == NULL) {\n            g_fcfs_preload_global_vars.scandirat = fcfs_dlsym1(\"scandirat\");\n        }\n        return g_fcfs_preload_global_vars.scandirat(\n                fd, path, namelist, filter, compar);\n    }\n}\n\nint _scandirat_(int fd, const char *path, struct dirent ***namelist,\n        fcfs_dir_filter_func filter, fcfs_dir_compare_func compar)\n{\n    return do_scandirat(fd, path, namelist, filter, compar);\n}\n\nint scandirat64(int fd, const char *path, struct dirent64 ***namelist,\n        int (*filter) (const struct dirent64 *),\n        int (*compar) (const struct dirent64 **,\n            const struct dirent64 **))\n{\n    return do_scandirat(fd, path, (struct dirent ***)namelist,\n            (fcfs_dir_filter_func)filter,\n            (fcfs_dir_compare_func)compar);\n}\n\nchar *getcwd(char *buf, size_t size)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, line: %d, size: %d\\n\",\n            ++counter, __FUNCTION__, __LINE__, (int)size);\n    if (g_fcfs_preload_global_vars.cwd_call_type ==\n            FCFS_PRELOAD_CALL_FASTCFS)\n    {\n        return fcfs_getcwd(buf, size);\n    } else {\n        return g_fcfs_preload_global_vars.getcwd(buf, size);\n    }\n}\n\nchar *getwd(char *buf)\n{\n    if (g_fcfs_preload_global_vars.cwd_call_type ==\n            FCFS_PRELOAD_CALL_FASTCFS)\n    {\n        return fcfs_getwd(buf);\n    } else {\n        return g_fcfs_preload_global_vars.getwd(buf);\n    }\n}\n\nint closedir(DIR *dirp)\n{\n    FCFSPreloadDIRWrapper *wapper;\n    int result;\n\n    FCFS_LOG_DEBUG(\"%d. func: %s, line: %d\\n\", ++counter, __FUNCTION__, __LINE__);\n    wapper = (FCFSPreloadDIRWrapper *)dirp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        result = fcfs_closedir(wapper->dirp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.closedir == NULL) {\n            g_fcfs_preload_global_vars.closedir = fcfs_dlsym1(\"closedir\");\n        }\n        result = g_fcfs_preload_global_vars.closedir(wapper->dirp);\n    } else {\n        errno = EBADF;\n        return -1;\n    }\n\n    free(wapper);\n    return result;\n}\n\nstatic inline struct dirent *do_readdir(DIR *dirp)\n{\n    FCFSPreloadDIRWrapper *wapper;\n\n    wapper = (FCFSPreloadDIRWrapper *)dirp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_readdir(wapper->dirp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.readdir == NULL) {\n            g_fcfs_preload_global_vars.readdir = fcfs_dlsym1(\"readdir\");\n        }\n        return g_fcfs_preload_global_vars.readdir(wapper->dirp);\n    } else {\n        errno = EBADF;\n        return NULL;\n    }\n}\n\nstruct dirent *_readdir_(DIR *dirp)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, line: %d\\n\", ++counter, __FUNCTION__, __LINE__);\n    return do_readdir(dirp);\n}\n\nstruct dirent64 *readdir64(DIR *dirp)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, line: %d\\n\", ++counter, __FUNCTION__, __LINE__);\n    return (struct dirent64 *)do_readdir(dirp);\n}\n\nstatic inline int do_readdir_r(DIR *dirp, struct dirent *entry,\n        struct dirent **result)\n{\n    FCFSPreloadDIRWrapper *wapper;\n\n    wapper = (FCFSPreloadDIRWrapper *)dirp;\n\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_readdir_r(wapper->dirp, entry, result);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.readdir_r == NULL) {\n            g_fcfs_preload_global_vars.readdir_r = fcfs_dlsym1(\"readdir_r\");\n        }\n        return g_fcfs_preload_global_vars.readdir_r(\n                wapper->dirp, entry, result);\n    } else {\n        return EBADF;\n    }\n}\n\nint _readdir_r_(DIR *dirp, struct dirent *entry, struct dirent **result)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, line: %d\\n\", ++counter, __FUNCTION__, __LINE__);\n    return do_readdir_r(dirp, entry, result);\n}\n\nint readdir64_r(DIR *dirp, struct dirent64 *entry, struct dirent64 **result)\n{\n    FCFS_LOG_DEBUG(\"%d. func: %s, line: %d\\n\", ++counter, __FUNCTION__, __LINE__);\n    return do_readdir_r(dirp, (struct dirent *)entry, (struct dirent **)result);\n}\n\nvoid seekdir(DIR *dirp, long loc)\n{\n    FCFSPreloadDIRWrapper *wapper;\n\n    wapper = (FCFSPreloadDIRWrapper *)dirp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_seekdir(wapper->dirp, loc);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.seekdir == NULL) {\n            g_fcfs_preload_global_vars.seekdir = fcfs_dlsym1(\"seekdir\");\n        }\n        return g_fcfs_preload_global_vars.seekdir(wapper->dirp, loc);\n    }\n}\n\nlong telldir(DIR *dirp)\n{\n    FCFSPreloadDIRWrapper *wapper;\n\n    wapper = (FCFSPreloadDIRWrapper *)dirp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_telldir(wapper->dirp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.telldir == NULL) {\n            g_fcfs_preload_global_vars.telldir = fcfs_dlsym1(\"telldir\");\n        }\n        return g_fcfs_preload_global_vars.telldir(wapper->dirp);\n    } else {\n        errno = EBADF;\n        return -1;\n    }\n}\n\nvoid rewinddir(DIR *dirp)\n{\n    FCFSPreloadDIRWrapper *wapper;\n\n    wapper = (FCFSPreloadDIRWrapper *)dirp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_rewinddir(wapper->dirp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.rewinddir == NULL) {\n            g_fcfs_preload_global_vars.rewinddir = fcfs_dlsym1(\"rewinddir\");\n        }\n        return g_fcfs_preload_global_vars.rewinddir(wapper->dirp);\n    }\n}\n\nint dirfd(DIR *dirp)\n{\n    FCFSPreloadDIRWrapper *wapper;\n\n    wapper = (FCFSPreloadDIRWrapper *)dirp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_dirfd(wapper->dirp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.dirfd == NULL) {\n            g_fcfs_preload_global_vars.dirfd = fcfs_dlsym1(\"dirfd\");\n        }\n        return g_fcfs_preload_global_vars.dirfd(wapper->dirp);\n    } else {\n        errno = EBADF;\n        return -1;\n    }\n}\n\nint vdprintf(int fd, const char *format, va_list ap)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_vdprintf(fd, format, ap);\n    } else {\n        if (g_fcfs_preload_global_vars.vdprintf == NULL) {\n            g_fcfs_preload_global_vars.vdprintf = fcfs_dlsym1(\"vdprintf\");\n        }\n        return g_fcfs_preload_global_vars.vdprintf(fd, format, ap);\n    }\n}\n\nint dprintf(int fd, const char *format, ...)\n{\n    va_list ap;\n    int bytes;\n\n    va_start(ap, format);\n    bytes = vdprintf(fd, format, ap);\n    va_end(ap);\n    return bytes;\n}\n\nstatic inline int do_lockf(int fd, int cmd, off_t len)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_lockf(fd, cmd, len);\n    } else {\n        if (g_fcfs_preload_global_vars.lockf == NULL) {\n            g_fcfs_preload_global_vars.lockf = fcfs_dlsym2(\n                    \"lockf\", \"lockf64\");\n        }\n        return g_fcfs_preload_global_vars.lockf(fd, cmd, len);\n    }\n}\n\nint _lockf_(int fd, int cmd, off_t len)\n{\n    return do_lockf(fd, cmd, len);\n}\n\nint lockf64(int fd, int cmd, off_t len)\n{\n    return do_lockf(fd, cmd, len);\n}\n\nstatic inline int do_posix_fallocate(int fd, off_t offset, off_t len)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_posix_fallocate(fd, offset, len);\n    } else {\n        if (g_fcfs_preload_global_vars.posix_fallocate == NULL) {\n            g_fcfs_preload_global_vars.posix_fallocate = fcfs_dlsym2(\n                    \"posix_fallocate\", \"posix_fallocate64\");\n        }\n        return g_fcfs_preload_global_vars.posix_fallocate(fd, offset, len);\n    }\n}\n\nint _posix_fallocate_(int fd, off_t offset, off_t len)\n{\n    return do_posix_fallocate(fd, offset, len);\n}\n\nint posix_fallocate64(int fd, off_t offset, off_t len)\n{\n    return do_posix_fallocate(fd, offset, len);\n}\n\nint _posix_fadvise_(int fd, off_t offset, off_t len, int advice)\n{\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        return fcfs_posix_fadvise(fd, offset, len, advice);\n    } else {\n        if (g_fcfs_preload_global_vars.posix_fadvise == NULL) {\n            g_fcfs_preload_global_vars.posix_fadvise = fcfs_dlsym2(\n                    \"posix_fadvise\", \"posix_fadvise64\");\n        }\n        return g_fcfs_preload_global_vars.posix_fadvise(\n                fd, offset, len, advice);\n    }\n}\n\nint posix_fadvise64(int fd, off_t offset, off_t len, int advice)\n{\n    return _posix_fadvise_(fd, offset, len, advice);\n}\n\nint unsetenv(const char *name)\n{\n    FCFS_LOG_DEBUG(\"pid: %d, func: %s, line: %d, name: %s\\n\",\n            getpid(), __FUNCTION__, __LINE__, name);\n\n    if (g_fcfs_preload_global_vars.unsetenv == NULL) {\n        g_fcfs_preload_global_vars.unsetenv = fcfs_dlsym1(\"unsetenv\");\n    }\n    return g_fcfs_preload_global_vars.unsetenv(name);\n}\n\nint clearenv(void)\n{\n    FCFS_LOG_DEBUG(\"pid: %d, func: %s, line: %d\\n\",\n            getpid(), __FUNCTION__, __LINE__);\n\n    if (g_fcfs_preload_global_vars.clearenv == NULL) {\n        g_fcfs_preload_global_vars.clearenv = fcfs_dlsym1(\"clearenv\");\n    }\n    return g_fcfs_preload_global_vars.clearenv();\n}\n#endif\n\n#ifdef FCFS_PRELOAD_WITH_CAPI\nstatic inline FILE *do_fopen(const char *path, const char *mode)\n{\n    FCFSPreloadFILEWrapper *wapper;\n    FILE *fp;\n    int call_type;\n\n    if (FCFS_PRELOAD_IS_MY_MOUNTPOINT(path)) {\n        call_type = FCFS_PRELOAD_CALL_FASTCFS;\n        fp = fcfs_fopen(path, mode);\n    } else {\n        call_type = FCFS_PRELOAD_CALL_SYSTEM;\n        if (g_fcfs_preload_global_vars.fopen == NULL) {\n            g_fcfs_preload_global_vars.fopen = fcfs_dlsym2(\"fopen\", \"fopen64\");\n        }\n        fp = g_fcfs_preload_global_vars.fopen(path, mode);\n\n        FCFS_LOG_DEBUG(\"func: %s, line: %d, fp ========== %p\\n\",  __FUNCTION__, __LINE__, fp);\n    }\n\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, fp: %p\\n\",  __FUNCTION__, __LINE__, fp);\n\n    if (fp != NULL) {\n        wapper = fc_malloc(sizeof(FCFSPreloadFILEWrapper));\n        wapper->call_type = call_type;\n        wapper->fp = fp;\n        return (FILE *)wapper;\n    } else {\n        return NULL;\n    }\n}\n\nFILE *_fopen_(const char *path, const char *mode)\n{\n    FCFS_LOG_DEBUG(\"pid: %d, func: %s, line: %d, path: %s, mode: %s, inited: %d\\n\",\n            getpid(), __FUNCTION__, __LINE__, path, mode, g_fcfs_preload_global_vars.inited);\n\n    return do_fopen(path, mode);\n}\n\nFILE *fopen64(const char *path, const char *mode)\n{\n    FCFS_LOG_DEBUG(\"pid: %d, func: %s, line: %d, path: %s, mode: %s\\n\",\n            getpid(), __FUNCTION__, __LINE__, path, mode);\n    return do_fopen(path, mode);\n}\n\nFILE *_IO_fdopen(int fd, const char *mode)\n{\n    FCFSPreloadFILEWrapper *wapper;\n    FILE *fp;\n    int call_type;\n\n    FCFS_LOG_DEBUG(\"====== func: %s, line: %d, fd: %d, mode: %s\\n\",\n            __FUNCTION__, __LINE__, fd, mode);\n\n    if (FCFS_PAPI_IS_MY_FD(fd)) {\n        call_type = FCFS_PRELOAD_CALL_FASTCFS;\n        fp = fcfs_fdopen(fd, mode);\n    } else {\n        call_type = FCFS_PRELOAD_CALL_SYSTEM;\n        if (g_fcfs_preload_global_vars.fdopen == NULL) {\n            g_fcfs_preload_global_vars.fdopen = fcfs_dlsym1(\"fdopen\");\n        }\n        fp = g_fcfs_preload_global_vars.fdopen(fd, mode);\n    }\n\n    if (fp != NULL) {\n        wapper = fc_malloc(sizeof(FCFSPreloadFILEWrapper));\n        wapper->call_type = call_type;\n        wapper->fp = fp;\n        return (FILE *)wapper;\n    } else {\n        return NULL;\n    }\n}\n\nFILE *fdopen(int fd, const char *mode)\n{\n    return _IO_fdopen(fd, mode);\n}\n\nFILE *_freopen_(const char *path, const char *mode, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"====== func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        fp = fcfs_freopen(path, mode, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.freopen == NULL) {\n            g_fcfs_preload_global_vars.freopen = fcfs_dlsym1(\"freopen\");\n        }\n        fp = g_fcfs_preload_global_vars.freopen(path, mode, wapper->fp);\n    } else {\n        errno = EBADF;\n        return NULL;\n    }\n\n    if (fp != NULL) {\n        wapper->fp = fp;\n        return (FILE *)wapper;\n    } else {\n        return NULL;\n    }\n}\n\nFILE *freopen64(const char *path, const char *mode, FILE *fp)\n{\n    return _freopen_(path, mode, fp);\n}\n\n#define CHECK_DEAL_STD_STREAM(funcname, ...) \\\n    if (fp == stdin || fp == stderr || fp == stdout) {  \\\n        if (g_fcfs_preload_global_vars.funcname == NULL) { \\\n            g_fcfs_preload_global_vars.funcname = fcfs_dlsym1(#funcname); \\\n        } \\\n        return g_fcfs_preload_global_vars.funcname(__VA_ARGS__); \\\n    }\n\n#define CHECK_DEAL_STDIO_VOID(funcname, ...) \\\n    if (fp == stdin || fp == stderr || fp == stdout) {  \\\n        if (g_fcfs_preload_global_vars.funcname == NULL) { \\\n            g_fcfs_preload_global_vars.funcname = fcfs_dlsym1(#funcname); \\\n        } \\\n        g_fcfs_preload_global_vars.funcname(__VA_ARGS__); \\\n    }\n\nint fclose(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fclose, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fclose(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fclose == NULL) {\n            g_fcfs_preload_global_vars.fclose = fcfs_dlsym1(\"fclose\");\n        }\n        return g_fcfs_preload_global_vars.fclose(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fcloseall()\n{\n    int r1;\n    int r2;\n\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\\n\", __FUNCTION__, __LINE__);\n\n    r1 = fcfs_fcloseall();\n    if (g_fcfs_preload_global_vars.fcloseall == NULL) {\n        g_fcfs_preload_global_vars.fcloseall = fcfs_dlsym1(\"fcloseall\");\n    }\n    r2 = g_fcfs_preload_global_vars.fcloseall();\n    return (r1 == 0 ? r2 : r1);\n}\n\nvoid flockfile(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STDIO_VOID(flockfile, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        fcfs_flockfile(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.flockfile == NULL) {\n            g_fcfs_preload_global_vars.flockfile = fcfs_dlsym1(\"flockfile\");\n        }\n        g_fcfs_preload_global_vars.flockfile(wapper->fp);\n    }\n}\n\nint ftrylockfile(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(ftrylockfile, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_ftrylockfile(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.ftrylockfile == NULL) {\n            g_fcfs_preload_global_vars.ftrylockfile = fcfs_dlsym1(\"ftrylockfile\");\n        }\n        return g_fcfs_preload_global_vars.ftrylockfile(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nvoid funlockfile(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STDIO_VOID(funlockfile, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        fcfs_funlockfile(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.funlockfile == NULL) {\n            g_fcfs_preload_global_vars.funlockfile = fcfs_dlsym1(\"funlockfile\");\n        }\n        g_fcfs_preload_global_vars.funlockfile(wapper->fp);\n    }\n}\n\nint fseek(FILE *fp, long offset, int whence)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fseek, fp, offset, whence);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fseek(wapper->fp, offset, whence);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fseek == NULL) {\n            g_fcfs_preload_global_vars.fseek = fcfs_dlsym1(\"fseek\");\n        }\n        return g_fcfs_preload_global_vars.fseek(wapper->fp, offset, whence);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint _fseeko_(FILE *fp, off_t offset, int whence)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fseeko, fp, offset, whence);\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fseeko(wapper->fp, offset, whence);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fseeko == NULL) {\n            g_fcfs_preload_global_vars.fseeko = fcfs_dlsym1(\"fseeko\");\n        }\n        return g_fcfs_preload_global_vars.fseeko(wapper->fp, offset, whence);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fseeko64(FILE *fp, off_t offset, int whence)\n{\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\\n\", __FUNCTION__, __LINE__);\n    return _fseeko_(fp, offset, whence);\n}\n\nint __fseeko64(FILE *fp, off_t offset, int whence)\n{\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\\n\", __FUNCTION__, __LINE__);\n    return _fseeko_(fp, offset, whence);\n}\n\nlong ftell(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(ftell, fp);\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_ftell(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.ftell == NULL) {\n            g_fcfs_preload_global_vars.ftell = fcfs_dlsym1(\"ftell\");\n        }\n        return g_fcfs_preload_global_vars.ftell(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nlong _IO_ftell(FILE *fp)\n{\n    return ftell(fp);\n}\n\nstatic inline off_t do_ftello(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(ftello, fp);\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\\n\", __FUNCTION__, __LINE__);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_ftello(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.ftello == NULL) {\n            g_fcfs_preload_global_vars.ftello = fcfs_dlsym1(\"ftello\");\n        }\n        return g_fcfs_preload_global_vars.ftello(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\noff_t _ftello_(FILE *fp)\n{\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\\n\", __FUNCTION__, __LINE__);\n    return do_ftello(fp);\n}\n\noff_t ftello64(FILE *fp)\n{\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\\n\", __FUNCTION__, __LINE__);\n    return do_ftello(fp);\n}\n\noff_t __ftello64(FILE *fp)\n{\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\\n\", __FUNCTION__, __LINE__);\n    return do_ftello(fp);\n}\n\nvoid rewind(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STDIO_VOID(rewind, fp);\n\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\\n\", __FUNCTION__, __LINE__);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        fcfs_rewind(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.rewind == NULL) {\n            g_fcfs_preload_global_vars.rewind = fcfs_dlsym1(\"rewind\");\n        }\n        g_fcfs_preload_global_vars.rewind(wapper->fp);\n    }\n}\n\nint _fgetpos_(FILE *fp, fpos_t *pos)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fgetpos, fp, pos);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fgetpos(wapper->fp, pos);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fgetpos == NULL) {\n            g_fcfs_preload_global_vars.fgetpos = fcfs_dlsym1(\"fgetpos\");\n        }\n        return g_fcfs_preload_global_vars.fgetpos(wapper->fp, pos);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fgetpos64(FILE *fp, fpos_t *pos)\n{\n    return _fgetpos_(fp, pos);\n}\n\nint _IO_fgetpos(FILE *fp, fpos_t *pos)\n{\n    return _fgetpos_(fp, pos);\n}\n\nint _IO_fgetpos64(FILE *fp, fpos_t *pos)\n{\n    return _fgetpos_(fp, pos);\n}\n\nint _fsetpos_(FILE *fp, const fpos_t *pos)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fsetpos, fp, pos);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fsetpos(wapper->fp, pos);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fsetpos == NULL) {\n            g_fcfs_preload_global_vars.fsetpos = fcfs_dlsym1(\"fsetpos\");\n        }\n        return g_fcfs_preload_global_vars.fsetpos(wapper->fp, pos);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fsetpos64(FILE *fp, const fpos_t *pos)\n{\n    return _fsetpos_(fp, pos);\n}\n\nint _IO_fsetpos(FILE *fp, const fpos_t *pos)\n{\n    return _fsetpos_(fp, pos);\n}\n\nint _IO_fsetpos64(FILE *fp, const fpos_t *pos)\n{\n    return _fsetpos_(fp, pos);\n}\n\nint fgetc_unlocked(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fgetc_unlocked, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fgetc_unlocked(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fgetc_unlocked == NULL) {\n            g_fcfs_preload_global_vars.fgetc_unlocked = fcfs_dlsym1(\"fgetc_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.fgetc_unlocked(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fputc_unlocked(int c, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fputc_unlocked, c, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fputc_unlocked(c, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fputc_unlocked == NULL) {\n            g_fcfs_preload_global_vars.fputc_unlocked = fcfs_dlsym1(\"fputc_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.fputc_unlocked(c, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint getc_unlocked(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(getc_unlocked, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_getc_unlocked(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.getc_unlocked == NULL) {\n            g_fcfs_preload_global_vars.getc_unlocked = fcfs_dlsym1(\"getc_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.getc_unlocked(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint putc_unlocked(int c, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(putc_unlocked, c, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_putc_unlocked(c, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.putc_unlocked == NULL) {\n            g_fcfs_preload_global_vars.putc_unlocked = fcfs_dlsym1(\"putc_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.putc_unlocked(c, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nvoid clearerr_unlocked(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STDIO_VOID(clearerr_unlocked, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        fcfs_clearerr_unlocked(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.clearerr_unlocked == NULL) {\n            g_fcfs_preload_global_vars.clearerr_unlocked = fcfs_dlsym1(\"clearerr_unlocked\");\n        }\n        g_fcfs_preload_global_vars.clearerr_unlocked(wapper->fp);\n    }\n}\n\nint feof_unlocked(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(feof_unlocked, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\", __FUNCTION__, __LINE__);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_feof_unlocked(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.feof_unlocked == NULL) {\n            g_fcfs_preload_global_vars.feof_unlocked = fcfs_dlsym1(\"feof_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.feof_unlocked(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\n#ifdef _IO_feof_unlocked\n#undef _IO_feof_unlocked\n#endif\n\nint _IO_feof_unlocked(FILE *fp)\n{\n    return feof_unlocked(fp);\n}\n\nint ferror_unlocked(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(ferror_unlocked, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\", __FUNCTION__, __LINE__);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_ferror_unlocked(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.ferror_unlocked == NULL) {\n            g_fcfs_preload_global_vars.ferror_unlocked = fcfs_dlsym1(\"ferror_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.ferror_unlocked(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\n#ifdef _IO_ferror_unlocked\n#undef _IO_ferror_unlocked\n#endif\n\nint _IO_ferror_unlocked(FILE *fp)\n{\n    return ferror_unlocked(fp);\n}\n\nint fileno_unlocked(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fileno_unlocked, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\", __FUNCTION__, __LINE__);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fileno_unlocked(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fileno_unlocked == NULL) {\n            g_fcfs_preload_global_vars.fileno_unlocked =\n                fcfs_dlsym1(\"fileno_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.fileno_unlocked(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fflush_unlocked(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    if (fp == NULL)  {\n        return g_fcfs_preload_global_vars.fflush_unlocked(fp);\n    }\n\n    CHECK_DEAL_STD_STREAM(fflush_unlocked, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fflush_unlocked(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fflush_unlocked == NULL) {\n            g_fcfs_preload_global_vars.fflush_unlocked = fcfs_dlsym1(\"fflush_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.fflush_unlocked(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nsize_t fread_unlocked(void *buff, size_t size, size_t n, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fread_unlocked, buff, size, n, fp);\n\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, size: %d, nmemb: %d\\n\", __FUNCTION__,\n            __LINE__, (int)size, (int)n);\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fread_unlocked(buff, size, n, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fread_unlocked == NULL) {\n            g_fcfs_preload_global_vars.fread_unlocked = fcfs_dlsym1(\"fread_unlocked\");\n        }\n        //return g_fcfs_preload_global_vars.fread_unlocked(buff, size, n, wapper->fp);\n        int bytes = g_fcfs_preload_global_vars.fread_unlocked(buff, size, n, wapper->fp);\n\n        FCFS_LOG_DEBUG(\"func: %s, line: %d, size: %d, nmemb: %d, bytes: %d\\n\",\n                __FUNCTION__, __LINE__, (int)size, (int)n, bytes);\n        return bytes;\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nsize_t fwrite_unlocked(const void *buff, size_t size, size_t n, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fwrite_unlocked, buff, size, n, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fwrite_unlocked(buff, size, n, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fwrite_unlocked == NULL) {\n            g_fcfs_preload_global_vars.fwrite_unlocked = fcfs_dlsym1(\"fwrite_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.fwrite_unlocked(buff, size, n, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nchar *fgets_unlocked(char *s, int size, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fgets_unlocked, s, size, fp);\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fgets_unlocked(s, size, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fgets_unlocked == NULL) {\n            g_fcfs_preload_global_vars.fgets_unlocked = fcfs_dlsym1(\"fgets_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.fgets_unlocked(s, size, wapper->fp);\n    } else {\n        errno = EBADF;\n        return NULL;\n    }\n}\n\nssize_t __libc_readline_unlocked (FILE *fp, char *buff, size_t size)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(__libc_readline_unlocked, fp, buff, size);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_readline_unlocked(wapper->fp, buff, size);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.__libc_readline_unlocked == NULL) {\n            g_fcfs_preload_global_vars.__libc_readline_unlocked =\n                fcfs_dlsym1(\"__libc_readline_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.__libc_readline_unlocked(\n                wapper->fp, buff, size);\n    } else {\n        errno = EBADF;\n        return -1;\n    }\n}\n\nint fputs_unlocked(const char *s, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fputs_unlocked, s, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fputs_unlocked(s, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fputs_unlocked == NULL) {\n            g_fcfs_preload_global_vars.fputs_unlocked = fcfs_dlsym1(\"fputs_unlocked\");\n        }\n        return g_fcfs_preload_global_vars.fputs_unlocked(s, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nvoid clearerr(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STDIO_VOID(clearerr, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        fcfs_clearerr(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.clearerr == NULL) {\n            g_fcfs_preload_global_vars.clearerr = fcfs_dlsym1(\"clearerr\");\n        }\n        g_fcfs_preload_global_vars.clearerr(wapper->fp);\n    }\n}\n\nint _IO_feof(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(feof, fp);\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_feof(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.feof == NULL) {\n            g_fcfs_preload_global_vars.feof = fcfs_dlsym1(\"feof\");\n        }\n        return g_fcfs_preload_global_vars.feof(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint _feof_(FILE *fp)\n{\n    return _IO_feof(fp);\n}\n\nint _ferror_(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(feof, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_ferror(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.ferror == NULL) {\n            g_fcfs_preload_global_vars.ferror = fcfs_dlsym1(\"ferror\");\n        }\n        return g_fcfs_preload_global_vars.ferror(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint _IO_ferror(FILE *fp)\n{\n    return _ferror_(fp);\n}\n\nint fileno(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fileno, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fileno(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fileno == NULL) {\n            g_fcfs_preload_global_vars.fileno = fcfs_dlsym1(\"fileno\");\n        }\n        return g_fcfs_preload_global_vars.fileno(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fgetc(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fgetc, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fgetc(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fgetc == NULL) {\n            g_fcfs_preload_global_vars.fgetc = fcfs_dlsym1(\"fgetc\");\n        }\n        return g_fcfs_preload_global_vars.fgetc(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nchar *fgets(char *s, int size, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fgets, s, size, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fgets(s, size, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fgets == NULL) {\n            g_fcfs_preload_global_vars.fgets = fcfs_dlsym1(\"fgets\");\n        }\n        return g_fcfs_preload_global_vars.fgets(s, size, wapper->fp);\n    } else {\n        errno = EBADF;\n        return NULL;\n    }\n}\n\n#ifdef getc\n#undef getc\n#endif\n\nint getc(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(getc, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_getc(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.getc == NULL) {\n            g_fcfs_preload_global_vars.getc = fcfs_dlsym1(\"getc\");\n        }\n        return g_fcfs_preload_global_vars.getc(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint ungetc(int c, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(ungetc, c, fp);\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_ungetc(c, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.ungetc == NULL) {\n            g_fcfs_preload_global_vars.ungetc = fcfs_dlsym1(\"ungetc\");\n        }\n        return g_fcfs_preload_global_vars.ungetc(c, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fputc(int c, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fputc, c, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fputc(c, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fputc == NULL) {\n            g_fcfs_preload_global_vars.fputc = fcfs_dlsym1(\"fputc\");\n        }\n        return g_fcfs_preload_global_vars.fputc(c, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fputs(const char *s, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fputs, s, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fputs(s, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fputs == NULL) {\n            g_fcfs_preload_global_vars.fputs = fcfs_dlsym1(\"fputs\");\n        }\n        return g_fcfs_preload_global_vars.fputs(s, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\n#ifdef putc\n#undef putc\n#endif\n\nint putc(int c, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(putc, c, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_putc(c, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.putc == NULL) {\n            g_fcfs_preload_global_vars.putc = fcfs_dlsym1(\"putc\");\n        }\n        return g_fcfs_preload_global_vars.putc(c, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nsize_t fread(void *buff, size_t size, size_t nmemb, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fread, buff, size, nmemb, fp);\n\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, size: %d, nmemb: %d\\n\", __FUNCTION__,\n            __LINE__, (int)size, (int)nmemb);\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fread(buff, size, nmemb, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fread == NULL) {\n            g_fcfs_preload_global_vars.fread = fcfs_dlsym1(\"fread\");\n        }\n        return g_fcfs_preload_global_vars.fread(buff, size, nmemb, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nsize_t fwrite(const void *buff, size_t size, size_t nmemb, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(fwrite, buff, size, nmemb, fp);\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fwrite(buff, size, nmemb, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fwrite == NULL) {\n            g_fcfs_preload_global_vars.fwrite = fcfs_dlsym1(\"fwrite\");\n        }\n        return g_fcfs_preload_global_vars.fwrite(buff, size, nmemb, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint vfprintf(FILE *fp, const char *format, va_list ap)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(vfprintf, fp, format, ap);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_vfprintf(wapper->fp, format, ap);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.vfprintf == NULL) {\n            g_fcfs_preload_global_vars.vfprintf = fcfs_dlsym1(\"vfprintf\");\n        }\n        return g_fcfs_preload_global_vars.vfprintf(wapper->fp, format, ap);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fprintf(FILE *fp, const char *format, ...)\n{\n    va_list ap;\n    int result;\n\n    va_start(ap, format);\n    result = vfprintf(fp, format, ap);\n    va_end(ap);\n\n    return result;\n}\n\nint __vfprintf_chk(FILE *fp, int flag, const char *format, va_list ap)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(__vfprintf_chk, fp, flag, format, ap);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_vfprintf(wapper->fp, format, ap);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.__vfprintf_chk == NULL) {\n            g_fcfs_preload_global_vars.__vfprintf_chk =\n                fcfs_dlsym1(\"__vfprintf_chk\");\n        }\n        return g_fcfs_preload_global_vars.__vfprintf_chk(\n                wapper->fp, flag, format, ap);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint __fprintf_chk(FILE *fp, int flag, const char *format, ...)\n{\n    va_list ap;\n    int result;\n\n    va_start(ap, format);\n    result = __vfprintf_chk(fp, flag, format, ap);\n    va_end(ap);\n\n    return result;\n}\n\nssize_t getdelim(char **line, size_t *size, int delim, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(getdelim, line, size, delim, fp);\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_getdelim(line, size, delim, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.getdelim == NULL) {\n            g_fcfs_preload_global_vars.getdelim = fcfs_dlsym1(\"getdelim\");\n        }\n        return g_fcfs_preload_global_vars.getdelim(line, size, delim, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nssize_t __getdelim(char **line, size_t *size, int delim, FILE *fp)\n{\n    FCFS_LOG_DEBUG(\"func: %s, line: %d\\n\", __FUNCTION__, __LINE__);\n    return getdelim(line, size, delim, fp);\n}\n\nssize_t getline(char **line, size_t *size, FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    FCFS_LOG_DEBUG(\"func: %s, line: %d, wapper: %p, fp: %p\\n\",\n            __FUNCTION__, __LINE__, wapper, wapper->fp);\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_getline(line, size, wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.getline == NULL) {\n            g_fcfs_preload_global_vars.getline = fcfs_dlsym1(\"getline\");\n        }\n        return g_fcfs_preload_global_vars.getline(line, size, wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nssize_t _IO_getline(char **line, size_t *size, FILE *fp)\n{\n    return getline(line, size, fp);\n}\n\nint vfscanf(FILE *fp, const char *format, va_list ap)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(vfscanf, fp, format, ap);\n\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        //TODO\n        errno = EOPNOTSUPP;\n        return EOF;\n        //return fcfs_vfscanf(wapper->fp, format, ap);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.vfscanf == NULL) {\n            g_fcfs_preload_global_vars.vfscanf = fcfs_dlsym1(\"vfscanf\");\n        }\n        return g_fcfs_preload_global_vars.vfscanf(wapper->fp, format, ap);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint fscanf(FILE *fp, const char *format, ...)\n{\n    va_list ap;\n    int count;\n\n    va_start(ap, format);\n    count = vfscanf(fp, format, ap);\n    va_end(ap);\n\n    return count;\n}\n\nint setvbuf(FILE *fp, char *buf, int mode, size_t size)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(setvbuf, fp, buf, mode, size);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_setvbuf(wapper->fp, buf, mode, size);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.setvbuf == NULL) {\n            g_fcfs_preload_global_vars.setvbuf = fcfs_dlsym1(\"setvbuf\");\n        }\n        return g_fcfs_preload_global_vars.setvbuf(wapper->fp, buf, mode, size);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\n\nint _IO_setvbuf(FILE *fp, char *buf, int mode, size_t size)\n{\n    return setvbuf(fp, buf, mode, size);\n}\n\nvoid setbuf(FILE *fp, char *buf)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(setbuf, fp, buf);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        fcfs_setbuf(wapper->fp, buf);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.setbuf == NULL) {\n            g_fcfs_preload_global_vars.setbuf = fcfs_dlsym1(\"setbuf\");\n        }\n        g_fcfs_preload_global_vars.setbuf(wapper->fp, buf);\n    }\n}\n\nvoid setbuffer(FILE *fp, char *buf, size_t size)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(setbuffer, fp, buf, size);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        fcfs_setbuffer(wapper->fp, buf, size);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.setbuffer == NULL) {\n            g_fcfs_preload_global_vars.setbuffer = fcfs_dlsym1(\"setbuffer\");\n        }\n        g_fcfs_preload_global_vars.setbuffer(wapper->fp, buf, size);\n    }\n}\n\nvoid _IO_setbuffer(FILE *fp, char *buf, size_t size)\n{\n    setbuffer(fp, buf, size);\n}\n\nvoid setlinebuf(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(setlinebuf, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        fcfs_setlinebuf(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.setlinebuf == NULL) {\n            g_fcfs_preload_global_vars.setlinebuf = fcfs_dlsym1(\"setlinebuf\");\n        }\n        g_fcfs_preload_global_vars.setlinebuf(wapper->fp);\n    }\n}\n\nint fflush(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    if (fp == NULL)  {\n        return g_fcfs_preload_global_vars.fflush(fp);\n    }\n\n    CHECK_DEAL_STD_STREAM(fflush, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return fcfs_fflush(wapper->fp);\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.fflush == NULL) {\n            g_fcfs_preload_global_vars.fflush = fcfs_dlsym1(\"fflush\");\n        }\n        return g_fcfs_preload_global_vars.fflush(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint _IO_fflush(FILE *fp)\n{\n    return fflush(fp);\n}\n\nint __uflow(FILE *fp)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(__uflow, fp);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return 0;\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.__uflow == NULL) {\n            g_fcfs_preload_global_vars.__uflow = fcfs_dlsym1(\"__uflow\");\n        }\n        return g_fcfs_preload_global_vars.__uflow(wapper->fp);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n\nint __overflow(FILE *fp, int ch)\n{\n    FCFSPreloadFILEWrapper *wapper;\n\n    CHECK_DEAL_STD_STREAM(__overflow, fp, ch);\n    wapper = (FCFSPreloadFILEWrapper *)fp;\n    if (wapper->call_type == FCFS_PRELOAD_CALL_FASTCFS) {\n        return 0;\n    } else if (wapper->call_type == FCFS_PRELOAD_CALL_SYSTEM) {\n        if (g_fcfs_preload_global_vars.__overflow == NULL) {\n            g_fcfs_preload_global_vars.__overflow = fcfs_dlsym1(\"__overflow\");\n        }\n        return g_fcfs_preload_global_vars.__overflow(wapper->fp, ch);\n    } else {\n        errno = EBADF;\n        return EOF;\n    }\n}\n#endif\n"
  },
  {
    "path": "src/preload/api.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_PRELOAD_API_H\n#define _FCFS_PRELOAD_API_H\n\n#include \"types.h\"\n\n#define FCFS_PRELOAD_IS_MY_MOUNTPOINT(path) \\\n    FCFS_API_IS_MY_MOUNTPOINT(path)\n\n#define FCFS_PRELOAD_IS_MY_FD_MOUNTPOINT(fd, path) \\\n    (FCFS_PAPI_IS_MY_FD(fd) || FCFS_PRELOAD_IS_MY_MOUNTPOINT(path))\n\n#ifdef fread_unlocked\n#undef fread_unlocked\n#endif\n\n#ifdef fwrite_unlocked\n#undef fwrite_unlocked\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint closedir(DIR *dirp);\n\nstruct dirent *_readdir_(DIR *dirp) __asm__ (\"\" \"readdir\");\n\nstruct dirent64 *readdir64(DIR *dirp);\n\nint _readdir_r_(DIR *dirp, struct dirent *entry, struct dirent **result)\n     __asm__ (\"\" \"readdir_r\");\n\nint readdir64_r(DIR *dirp, struct dirent64 *entry, struct dirent64 **result);\n\nvoid seekdir(DIR *dirp, long loc);\n\nlong telldir(DIR *dirp);\n\nvoid rewinddir(DIR *dirp);\n\nint dirfd(DIR *dirp);\n\nint close(int fd);\n\nint fsync(int fd);\n\nint fdatasync(int fd);\n\nssize_t write(int fd, const void *buff, size_t count);\n\nssize_t _pwrite_(int fd, const void *buff, size_t count, off_t offset)\n    __asm__ (\"\" \"pwrite\");\n\nssize_t pwrite64(int fd, const void *buff, size_t count, off_t offset);\n\nssize_t writev(int fd, const struct iovec *iov, int iovcnt);\n\nssize_t _pwritev_(int fd, const struct iovec *iov, int iovcnt, off_t offset)\n    __asm__ (\"\" \"pwritev\");\n\nssize_t pwritev64(int fd, const struct iovec *iov, int iovcnt, off_t offset);\n\nssize_t read(int fd, void *buff, size_t count);\n\nssize_t __read_chk(int fd, void *buff, size_t count, size_t size);\n\nssize_t _pread_(int fd, void *buff, size_t count, off_t offset)\n    __asm__ (\"\" \"pread\");\n\nssize_t pread64(int fd, void *buff, size_t count, off_t offset);\n\nssize_t __pread_chk(int fd, void *buff, size_t count,\n        off_t offset, size_t size);\n\nssize_t __pread64_chk(int fd, void *buff, size_t count,\n        off_t offset, size_t size);\n\nssize_t readv(int fd, const struct iovec *iov, int iovcnt);\n\nssize_t _preadv_(int fd, const struct iovec *iov, int iovcnt, off_t offset)\n    __asm__ (\"\" \"preadv\");\n\nssize_t preadv64(int fd, const struct iovec *iov, int iovcnt, off_t offset);\n\nssize_t readahead(int fd, off64_t offset, size_t count);\n\noff_t _lseek_(int fd, off_t offset, int whence) __asm__ (\"\" \"lseek\");\n\noff_t __lseek(int fd, off_t offset, int whence);\n\noff_t lseek64(int fd, off_t offset, int whence);\n\nint _fallocate_(int fd, int mode, off_t offset, off_t length)\n    __asm__ (\"\" \"fallocate\");\n\nint fallocate64(int fd, int mode, off_t offset, off_t length);\n\nint _ftruncate_(int fd, off_t length) __asm__ (\"\" \"ftruncate\");\n\nint ftruncate64(int fd, off_t length);\n\nint __fxstat_(int ver, int fd, struct stat *buf) __asm__ (\"\" \"__fxstat\");\n\nint __fxstat64(int ver, int fd, struct stat64 *buf);\n\nint fstat(int fd, struct stat *buf);\n\nint flock(int fd, int operation);\n\nint _fcntl_(int fd, int cmd, ...)  __asm__ (\"\" \"fcntl\");\n\nint fcntl64(int fd, int cmd, ...);\n\nint futimes(int fd, const struct timeval times[2]);\n\nint futimens(int fd, const struct timespec times[2]);\n\nint fchown(int fd, uid_t owner, gid_t group);\n\nint fchmod(int fd, mode_t mode);\n\nint fsetxattr(int fd, const char *name, const void *value,\n        size_t size, int flags);\n\nssize_t fgetxattr(int fd, const char *name, void *value, size_t size);\n\nssize_t flistxattr(int fd, char *list, size_t size);\n\nint fremovexattr(int fd, const char *name);\n\nint fchdir(int fd);\n\nint _fstatvfs_(int fd, struct statvfs *buf) __asm__ (\"\" \"fstatvfs\");\n\nint fstatvfs64(int fd, struct statvfs64 *buf);\n\nint dup(int fd);\n\nint dup2(int fd1, int fd2);\n\nvoid *_mmap_(void *addr, size_t length, int prot, int flags,\n        int fd, off_t offset) __asm__ (\"\" \"mmap\");\n\nvoid *mmap64(void *addr, size_t length, int prot,\n        int flags, int fd, off_t offset);\n\nDIR *fdopendir(int fd);\n\nint symlinkat(const char *link, int fd, const char *path);\n\nint _openat_(int fd, const char *path, int flags, ...) __asm__ (\"\" \"openat\");\n\nint openat64(int fd, const char *path, int flags, ...);\n\nint __openat_2_(int fd, const char *path, int flags) __asm__ (\"\" \"__openat_2\");\n\nint __openat64_2(int fd, const char *path, int flags);\n\nint __fxstatat_(int ver, int fd, const char *path, struct stat *buf,\n        int flags) __asm__ (\"\" \"__fxstatat\");\n\nint __fxstatat64(int ver, int fd, const char *path,\n        struct stat64 *buf, int flags);\n\nint fstatat(int fd, const char *path, struct stat *buf, int flags);\n\nssize_t readlinkat(int fd, const char *path, char *buff, size_t size);\n\nint __xmknodat(int ver, int fd, const char *path, mode_t mode, dev_t *dev);\n\nint mknodat(int fd, const char *path, mode_t mode, dev_t dev);\n\nint mkfifoat(int fd, const char *path, mode_t mode);\n\nint faccessat(int fd, const char *path, int mode, int flags);\n\nint futimesat(int fd, const char *path, const struct timeval times[2]);\n\nint utimensat(int fd, const char *path, const\n        struct timespec times[2], int flags);\n\nint unlinkat(int fd, const char *path, int flags);\n\nint mkdirat(int fd, const char *path, mode_t mode);\n\nint fchownat(int fd, const char *path, uid_t owner, gid_t group, int flags);\n\nint fchmodat(int fd, const char *path, mode_t mode, int flags);\n\nint linkat(int fd1, const char *path1, int fd2, const char *path2, int flags);\n\nint renameat(int fd1, const char *path1, int fd2, const char *path2);\n\nint renameat2(int fd1, const char *path1, int fd2,\n        const char *path2, unsigned int flags);\n\nint _scandirat_(int fd, const char *path, struct dirent ***namelist,\n        fcfs_dir_filter_func filter, fcfs_dir_compare_func compar)\n        __asm__ (\"\" \"scandirat\");\n\nint scandirat64(int fd, const char *path, struct dirent64 ***namelist,\n        int (*filter) (const struct dirent64 *),\n        int (*compar) (const struct dirent64 **,\n            const struct dirent64 **));\n\nchar *getcwd(char *buf, size_t size);\n\nchar *getwd(char *buf);\n\nint _open_(const char *path, int flags, ...) __asm__ (\"\" \"open\");\n\nint open64(const char *path, int flags, ...);\n\nint __open(const char *path, int flags, int mode);\n\nint __open_2_(const char *path, int flags) __asm__ (\"\" \"__open_2\");\n\nint __open64_2_(const char *path, int flags);\n\nint _creat_(const char *path, mode_t mode) __asm__ (\"\" \"creat\");\n\nint creat64(const char *path, mode_t mode);\n\nint _truncate_(const char *path, off_t length) __asm__ (\"\" \"truncate\");\n\nint truncate64(const char *path, off_t length);\n\nint __lxstat_(int ver, const char *path, struct stat *buf)\n    __asm__ (\"\" \"__lxstat\");\n\nint __lxstat64(int ver, const char *path, struct stat64 *buf);\n\nint lstat(const char *path, struct stat *buf);\n\nint __xstat_(int ver, const char *path, struct stat *buf)\n     __asm__ (\"\" \"__xstat\");\n\nint __xstat64(int ver, const char *path, struct stat64 *buf);\n\nint stat(const char *path, struct stat *buf);\n\nint link(const char *path1, const char *path2);\n\nint symlink(const char *link, const char *path);\n\nssize_t readlink(const char *path, char *buff, size_t size);\n\nint __xmknod(int ver, const char *path, mode_t mode, dev_t *dev);\n\nint mknod(const char *path, mode_t mode, dev_t dev);\n\nint mkfifo(const char *path, mode_t mode);\n\nint access(const char *path, int mode);\n\nint euidaccess(const char *path, int mode);\n\nint eaccess(const char *path, int mode);\n\nint utime(const char *path, const struct utimbuf *times);\n\nint utimes(const char *path, const struct timeval times[2]);\n\nint unlink(const char *path);\n\nint rename(const char *path1, const char *path2);\n\nint mkdir(const char *path, mode_t mode);\n\nint rmdir(const char *path);\n\nint chown(const char *path, uid_t owner, gid_t group);\n\nint lchown(const char *path, uid_t owner, gid_t group);\n\nint chmod(const char *path, mode_t mode);\n\nint _statvfs_(const char *path, struct statvfs *buf) __asm__ (\"\" \"statvfs\");\n\nint statvfs64(const char *path, struct statvfs64 *buf);\n\nint setxattr(const char *path, const char *name,\n        const void *value, size_t size, int flags);\n\nint lsetxattr(const char *path, const char *name,\n        const void *value, size_t size, int flags);\n\nssize_t getxattr(const char *path, const char *name,\n        void *value, size_t size);\n\nssize_t lgetxattr(const char *path, const char *name,\n        void *value, size_t size);\n\nssize_t listxattr(const char *path, char *list, size_t size);\n\nssize_t llistxattr(const char *path, char *list, size_t size);\n\nint removexattr(const char *path, const char *name);\n\nint lremovexattr(const char *path, const char *name);\n\nint chdir(const char *path);\n\nint chroot(const char *path);\n\nint unsetenv(const char *name);\n\nint clearenv(void);\n\nDIR *opendir(const char *path);\n\nint _scandir_(const char *path, struct dirent ***namelist,\n        fcfs_dir_filter_func filter, fcfs_dir_compare_func compar)\n    __asm__ (\"\" \"scandir\");\n\nint scandir64(const char *path, struct dirent64 ***namelist,\n        int (*filter) (const struct dirent64 *),\n        int (*compar) (const struct dirent64 **,\n            const struct dirent64 **));\n\nint dprintf(int fd, const char *format, ...);\n\nint vdprintf(int fd, const char *format, va_list ap);\n\nint _lockf_(int fd, int cmd, off_t len) __asm__ (\"\" \"lockf\");\n\nint lockf64(int fd, int cmd, off_t len);\n\nint _posix_fallocate_(int fd, off_t offset, off_t len)\n    __asm__ (\"\" \"posix_fallocate\");\n\nint posix_fallocate64(int fd, off_t offset, off_t len);\n\nint _posix_fadvise_(int fd, off_t offset, off_t len, int advice)\n    __asm__ (\"\" \"posix_fadvise\");\n\nint posix_fadvise64(int fd, off_t offset, off_t len, int advice);\n\nFILE *_fopen_(const char *path, const char *mode) __asm__ (\"\" \"fopen\");\n\nFILE *fopen64(const char *path, const char *mode);\n\nFILE *fdopen(int fd, const char *mode);\n\nFILE *_freopen_(const char *path, const char *mode, FILE *fp)\n    __asm__ (\"\" \"freopen\");\n\nFILE *freopen64(const char *path, const char *mode, FILE *fp);\n\nint fclose(FILE *fp);\n\nint fcloseall();\n\nvoid flockfile(FILE *fp);\n\nint ftrylockfile(FILE *fp);\n\nvoid funlockfile(FILE *fp);\n\nint fseek(FILE *fp, long offset, int whence);\n\nint _fseeko_(FILE *fp, off_t offset, int whence) __asm__ (\"\" \"fseeko\");\n\nint fseeko64(FILE *fp, off_t offset, int whence);\n\nlong ftell(FILE *fp);\n\noff_t _ftello_(FILE *fp) __asm__ (\"\" \"ftello\");\n\noff_t ftello64(FILE *fp);\n\nvoid rewind(FILE *fp);\n\nint _fgetpos_(FILE *fp, fpos_t *pos) __asm__ (\"\" \"fgetpos\");\n\nint fgetpos64(FILE *fp, fpos_t *pos);\n\nint _fsetpos_(FILE *fp, const fpos_t *pos) __asm__ (\"\" \"fsetpos\");\n\nint fsetpos64(FILE *fp, const fpos_t *pos);\n\nint fgetc_unlocked(FILE *fp);\n\nint fputc_unlocked(int c, FILE *fp);\n\nint getc_unlocked(FILE *fp);\n\nint putc_unlocked(int c, FILE *fp);\n\nvoid clearerr_unlocked(FILE *fp);\n\nint feof_unlocked(FILE *fp);\n\nint ferror_unlocked(FILE *fp);\n\nint fileno_unlocked(FILE *fp);\n\nint fflush_unlocked(FILE *fp);\n\nsize_t fread_unlocked(void *buff, size_t size, size_t n, FILE *fp);\n\nsize_t fwrite_unlocked(const void *buff, size_t size, size_t n, FILE *fp);\n\nchar *fgets_unlocked(char *s, int size, FILE *fp);\n\nint fputs_unlocked(const char *s, FILE *fp);\n\nvoid clearerr(FILE *fp);\n\nint _feof_(FILE *fp) __asm__ (\"\" \"feof\");\n\nint _ferror_(FILE *fp) __asm__ (\"\" \"ferror\");\n\nint fileno(FILE *fp);\n\nint fgetc(FILE *fp);\n\nchar *fgets(char *s, int size, FILE *fp);\n\nint getc(FILE *fp);\n\nint ungetc(int c, FILE *fp);\n\nint fputc(int c, FILE *fp);\n\nint fputs(const char *s, FILE *fp);\n\nint putc(int c, FILE *fp);\n\nsize_t fread(void *buff, size_t size, size_t nmemb, FILE *fp);\n\nsize_t fwrite(const void *buff, size_t size, size_t nmemb, FILE *fp);\n\nint fprintf(FILE *fp, const char *format, ...);\n\nint vfprintf(FILE *fp, const char *format, va_list ap);\n\nint __fprintf_chk (FILE *fp, int flag, const char *format, ...);\n\nint __vfprintf_chk (FILE *fp, int flag, const char *format, va_list ap);\n\nssize_t getdelim(char **line, size_t *size, int delim, FILE *fp);\n\nssize_t getline(char **line, size_t *size, FILE *fp);\n\nint fscanf(FILE *fp, const char *format, ...);\n\nint vfscanf(FILE *fp, const char *format, va_list ap);\n\nint setvbuf(FILE *fp, char *buf, int mode, size_t size);\n\nvoid setbuf(FILE *fp, char *buf);\n\nvoid setbuffer(FILE *fp, char *buf, size_t size);\n\nvoid setlinebuf(FILE *fp);\n\nint fflush(FILE *fp);\n\nlong syscall(long number, ...);\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/preload/global.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <dlfcn.h>\n#include \"global.h\"\n\nFCFSPreloadGlobalVars g_fcfs_preload_global_vars;\n\nstatic inline void *dlsym_one(const char *fname, const bool required)\n{\n    int log_level;\n    void *func;\n\n    if ((func=dlsym(RTLD_NEXT, fname)) == NULL) {\n        log_level = (required ? LOG_ERR : LOG_DEBUG);\n        log_it_ex(&g_log_context, log_level, \"file: \"__FILE__\", line: %d, \"\n                \"function %s not exist!\", __LINE__, fname);\n    }\n    return func;\n}\n\nstatic inline void *dlsym_two(const char *fname1,\n        const char *fname2, const bool required)\n{\n    int log_level;\n    void *func;\n\n    if ((func=dlsym(RTLD_NEXT, fname1)) != NULL) {\n        return func;\n    }\n\n    if ((func=dlsym(RTLD_NEXT, fname2)) == NULL) {\n        log_level = (required ? LOG_ERR : LOG_DEBUG);\n        log_it_ex(&g_log_context, log_level, \"file: \"__FILE__\", line: %d, \"\n                \"function %s | %s not exist!\", __LINE__, fname1, fname2);\n    }\n\n    return func;\n}\n\nstatic int dlsym_papi()\n{\n    bool required;\n\n    g_fcfs_preload_global_vars.unsetenv = dlsym_one(\"unsetenv\", true);\n    g_fcfs_preload_global_vars.clearenv = dlsym_one(\"clearenv\", true);\n\n    g_fcfs_preload_global_vars.fstatat = dlsym_one(\"fstatat\", false);\n    required = (g_fcfs_preload_global_vars.fstatat == NULL);\n    g_fcfs_preload_global_vars.__xstat = dlsym_two(\n            \"__xstat\", \"__xstat64\", required);\n    g_fcfs_preload_global_vars.__lxstat = dlsym_two(\n            \"__lxstat\", \"__lxstat64\", required);\n    g_fcfs_preload_global_vars.__fxstat = dlsym_two(\n            \"__fxstat\", \"__fxstat64\", required);\n    g_fcfs_preload_global_vars.__fxstatat = dlsym_two(\n            \"__fxstatat\", \"__fxstatat64\", required);\n\n    g_fcfs_preload_global_vars.__xmknod = dlsym_one(\"__xmknod\", false);\n    g_fcfs_preload_global_vars.__xmknodat = dlsym_one(\"__xmknodat\", false);\n\n    g_fcfs_preload_global_vars.mkfifo = dlsym_one(\"mkfifo\", true);\n    g_fcfs_preload_global_vars.mkfifoat = dlsym_one(\"mkfifoat\", false);\n    g_fcfs_preload_global_vars.euidaccess = dlsym_one(\"euidaccess\", false);\n    g_fcfs_preload_global_vars.eaccess = dlsym_one(\"eaccess\", false);\n    g_fcfs_preload_global_vars.futimes = dlsym_one(\"futimes\", true);\n    g_fcfs_preload_global_vars.futimens = dlsym_one(\"futimens\", true);\n    g_fcfs_preload_global_vars.statvfs = dlsym_two(\n            \"statvfs\", \"statvfs64\", true);\n    g_fcfs_preload_global_vars.fstatvfs = dlsym_two(\n            \"fstatvfs\", \"fstatvfs64\", true);\n\n    g_fcfs_preload_global_vars.lockf = dlsym_two(\"lockf\", \"lockf64\", true);\n    g_fcfs_preload_global_vars.posix_fallocate = dlsym_two(\n            \"posix_fallocate\", \"posix_fallocate64\", true);\n    g_fcfs_preload_global_vars.posix_fadvise = dlsym_two(\n            \"posix_fadvise\", \"posix_fadvise64\", true);\n    g_fcfs_preload_global_vars.vdprintf = dlsym_one(\"vdprintf\", true);\n\n    g_fcfs_preload_global_vars.opendir = dlsym_one(\"opendir\", true);\n    g_fcfs_preload_global_vars.fdopendir = dlsym_one(\"fdopendir\", true);\n    g_fcfs_preload_global_vars.closedir = dlsym_one(\"closedir\", true);\n    g_fcfs_preload_global_vars.readdir = dlsym_two(\n            \"readdir\", \"readdir64\", true);\n    g_fcfs_preload_global_vars.readdir_r = dlsym_two(\n            \"readdir_r\", \"readdir64_r\", true);\n    g_fcfs_preload_global_vars.seekdir = dlsym_one(\"seekdir\", true);\n    g_fcfs_preload_global_vars.telldir = dlsym_one(\"telldir\", true);\n    g_fcfs_preload_global_vars.rewinddir = dlsym_one(\"rewinddir\", true);\n    g_fcfs_preload_global_vars.dirfd = dlsym_one(\"dirfd\", true);\n    g_fcfs_preload_global_vars.scandir = dlsym_one(\"scandir\", true);\n    g_fcfs_preload_global_vars.scandirat = dlsym_one(\"scandirat\", false);\n    g_fcfs_preload_global_vars.getcwd = dlsym_one(\"getcwd\", true);\n    g_fcfs_preload_global_vars.getwd = dlsym_one(\"getwd\", true);\n\n    return 0;\n}\n\nstatic int dlsym_capi()\n{\n    g_fcfs_preload_global_vars.fopen = dlsym_two(\"fopen\", \"fopen64\", true);\n    g_fcfs_preload_global_vars.fdopen = dlsym_one(\"fdopen\", true);\n    g_fcfs_preload_global_vars.freopen = dlsym_two(\"freopen\", \"freopen64\", true);\n    g_fcfs_preload_global_vars.fclose = dlsym_one(\"fclose\", true);\n    g_fcfs_preload_global_vars.fcloseall = dlsym_one(\"fcloseall\", true);\n    g_fcfs_preload_global_vars.flockfile = dlsym_one(\"flockfile\", true);\n    g_fcfs_preload_global_vars.ftrylockfile = dlsym_one(\"ftrylockfile\", true);\n    g_fcfs_preload_global_vars.funlockfile = dlsym_one(\"funlockfile\", true);\n    g_fcfs_preload_global_vars.fseek = dlsym_one(\"fseek\", true);\n    g_fcfs_preload_global_vars.fseeko = dlsym_two(\"fseeko\", \"fseeko64\", true);\n    g_fcfs_preload_global_vars.ftell = dlsym_one(\"ftell\", true);\n    g_fcfs_preload_global_vars.ftello = dlsym_two(\"ftello\", \"ftello64\", true);\n    g_fcfs_preload_global_vars.rewind = dlsym_one(\"rewind\", true);\n    g_fcfs_preload_global_vars.fgetpos = dlsym_one(\"fgetpos\", true);\n    g_fcfs_preload_global_vars.fsetpos = dlsym_one(\"fsetpos\", true);\n    g_fcfs_preload_global_vars.fgetc_unlocked = dlsym_one(\"fgetc_unlocked\", true);\n    g_fcfs_preload_global_vars.fputc_unlocked = dlsym_one(\"fputc_unlocked\", true);\n    g_fcfs_preload_global_vars.getc_unlocked = dlsym_one(\"getc_unlocked\", true);\n    g_fcfs_preload_global_vars.putc_unlocked = dlsym_one(\"putc_unlocked\", true);\n    g_fcfs_preload_global_vars.clearerr_unlocked = dlsym_one(\"clearerr_unlocked\", true);\n    g_fcfs_preload_global_vars.feof_unlocked = dlsym_one(\"feof_unlocked\", true);\n    g_fcfs_preload_global_vars.ferror_unlocked = dlsym_one(\"ferror_unlocked\", true);\n    g_fcfs_preload_global_vars.fileno_unlocked = dlsym_one(\"fileno_unlocked\", true);\n    g_fcfs_preload_global_vars.fflush_unlocked = dlsym_one(\"fflush_unlocked\", true);\n    g_fcfs_preload_global_vars.fread_unlocked = dlsym_one(\"fread_unlocked\", true);\n    g_fcfs_preload_global_vars.fwrite_unlocked = dlsym_one(\"fwrite_unlocked\", true);\n    g_fcfs_preload_global_vars.fgets_unlocked = dlsym_one(\"fgets_unlocked\", true);\n    g_fcfs_preload_global_vars.fputs_unlocked = dlsym_one(\"fputs_unlocked\", true);\n    g_fcfs_preload_global_vars.clearerr = dlsym_one(\"clearerr\", true);\n    g_fcfs_preload_global_vars.feof = dlsym_one(\"feof\", true);\n    g_fcfs_preload_global_vars.ferror = dlsym_one(\"ferror\", true);\n    g_fcfs_preload_global_vars.fileno = dlsym_one(\"fileno\", true);\n    g_fcfs_preload_global_vars.fgetc = dlsym_one(\"fgetc\", true);\n    g_fcfs_preload_global_vars.fgets = dlsym_one(\"fgets\", true);\n    g_fcfs_preload_global_vars.getc = dlsym_one(\"getc\", true);\n    g_fcfs_preload_global_vars.ungetc = dlsym_one(\"ungetc\", true);\n    g_fcfs_preload_global_vars.fputc = dlsym_one(\"fputc\", true);\n    g_fcfs_preload_global_vars.fputs = dlsym_one(\"fputs\", true);\n    g_fcfs_preload_global_vars.putc = dlsym_one(\"putc\", true);\n    g_fcfs_preload_global_vars.fread = dlsym_one(\"fread\", true);\n    g_fcfs_preload_global_vars.fwrite = dlsym_one(\"fwrite\", true);\n    g_fcfs_preload_global_vars.fprintf = dlsym_one(\"fprintf\", true);\n    g_fcfs_preload_global_vars.vfprintf = dlsym_one(\"vfprintf\", true);\n    g_fcfs_preload_global_vars.__fprintf_chk =\n        dlsym_one(\"__fprintf_chk\", false);\n    g_fcfs_preload_global_vars.__vfprintf_chk =\n        dlsym_one(\"__vfprintf_chk\", false);\n    g_fcfs_preload_global_vars.getdelim = dlsym_one(\"getdelim\", true);\n    g_fcfs_preload_global_vars.getline = dlsym_one(\"getline\", true);\n    g_fcfs_preload_global_vars.fscanf = dlsym_one(\"fscanf\", true);\n    g_fcfs_preload_global_vars.vfscanf = dlsym_one(\"vfscanf\", true);\n    g_fcfs_preload_global_vars.setvbuf = dlsym_one(\"setvbuf\", true);\n    g_fcfs_preload_global_vars.setbuf = dlsym_one(\"setbuf\", true);\n    g_fcfs_preload_global_vars.setbuffer = dlsym_one(\"setbuffer\", true);\n    g_fcfs_preload_global_vars.setlinebuf = dlsym_one(\"setlinebuf\", true);\n    g_fcfs_preload_global_vars.fflush = dlsym_one(\"fflush\", true);\n\n    return 0;\n}\n\nstatic inline int dlsym_all()\n{\n    int result;\n\n    if ((result=dlsym_papi()) != 0) {\n        return result;\n    }\n\n    return dlsym_capi();\n}\n\nint fcfs_preload_global_init()\n{\n    return dlsym_all();\n}\n"
  },
  {
    "path": "src/preload/global.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_PRELOAD_GLOBAL_H\n#define _FCFS_PRELOAD_GLOBAL_H\n\n#include \"types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern FCFSPreloadGlobalVars g_fcfs_preload_global_vars;\n\n\tint fcfs_preload_global_init();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/preload/types.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_PRELOAD_TYPES_H\n#define _FCFS_PRELOAD_TYPES_H\n\n#include \"fastcfs/api/std/posix_api.h\"\n\n#ifdef OS_LINUX\ntypedef struct __dirstream DIR;\n#endif\n\n#define FCFS_PRELOAD_CALL_SYSTEM   1645611685\n#define FCFS_PRELOAD_CALL_FASTCFS  1645611708\n\ntypedef struct fcfs_preload_dir_wrapper {\n    int call_type;\n    DIR *dirp;\n} FCFSPreloadDIRWrapper;\n\ntypedef struct fcfs_preload_file_wrapper {\n    int call_type;\n    FILE *fp;\n} FCFSPreloadFILEWrapper;\n\ntypedef struct fcfs_preload_global_vars {\n    bool inited;\n    int cwd_call_type;\n\n    struct {\n        int (*unsetenv)(const char *name);\n\n        int (*clearenv)(void);\n\n        struct {\n            struct {\n                int (*fstatat)(int fd, const char *path,\n                        struct stat *buf, int flags);\n            };\n\n            struct {\n                int (*__xstat)(int ver, const char *path, struct stat *buf);\n\n                int (*__lxstat)(int ver, const char *path, struct stat *buf);\n\n                int (*__fxstat)(int ver, int fd, struct stat *buf);\n\n                int (*__fxstatat)(int ver, int fd, const char *path,\n                        struct stat *buf, int flags);\n            };\n        };\n\n        int (*__xmknod)(int ver, const char *path,\n                mode_t mode, dev_t *dev);\n\n        int (*__xmknodat)(int ver, int fd, const char *path,\n                mode_t mode, dev_t *dev);\n\n        int (*mkfifo)(const char *path, mode_t mode);\n\n        int (*mkfifoat)(int fd, const char *path, mode_t mode);\n\n        int (*euidaccess)(const char *path, int mode);\n\n        int (*eaccess)(const char *path, int mode);\n\n        int (*futimes)(int fd, const struct timeval times[2]);\n\n        int (*futimens)(int fd, const struct timespec times[2]);\n\n        int (*statvfs)(const char *path, struct statvfs *buf);\n\n        int (*fstatvfs)(int fd, struct statvfs *buf);\n\n        int (*lockf)(int fd, int cmd, off_t len);\n\n        int (*posix_fallocate)(int fd, off_t offset, off_t len);\n\n        int (*posix_fadvise)(int fd, off_t offset, off_t len, int advice);\n\n        int (*vdprintf)(int fd, const char *format, va_list ap);\n\n        DIR *(*opendir)(const char *path);\n\n        DIR *(*fdopendir)(int fd);\n\n        int (*closedir)(DIR *dirp);\n\n        struct dirent *(*readdir)(DIR *dirp);\n\n        int (*readdir_r)(DIR *dirp, struct dirent *entry,\n                struct dirent **result);\n\n        void (*seekdir)(DIR *dirp, long loc);\n\n        long (*telldir)(DIR *dirp);\n\n        void (*rewinddir)(DIR *dirp);\n\n        int (*dirfd)(DIR *dirp);\n\n        int (*scandir)(const char *path, struct dirent ***namelist,\n                fcfs_dir_filter_func filter, fcfs_dir_compare_func compar);\n\n        int (*scandirat)(int fd, const char *path, struct dirent ***namelist,\n                fcfs_dir_filter_func filter, fcfs_dir_compare_func compar);\n\n        int (*scandir64)(const char *path, struct dirent ***namelist,\n                fcfs_dir_filter_func filter, fcfs_dir_compare_func compar);\n\n        int (*scandirat64)(int fd, const char *path, struct dirent ***namelist,\n                fcfs_dir_filter_func filter, fcfs_dir_compare_func compar);\n\n        char *(*getcwd)(char *buf, size_t size);\n\n        char *(*getwd)(char *buf);\n\n        int (*__uflow)(FILE *fp);\n        int (*__overflow)(FILE *fp, int ch);\n    };\n\n    struct {\n        FILE * (*fopen)(const char *path, const char *mode);\n\n        FILE * (*fdopen)(int fd, const char *mode);\n\n        FILE * (*freopen)(const char *path, const char *mode, FILE *fp);\n\n        int (*fclose)(FILE *fp);\n\n        int (*fcloseall)();\n\n        void (*flockfile)(FILE *fp);\n\n        int (*ftrylockfile)(FILE *fp);\n\n        void (*funlockfile)(FILE *fp);\n\n        int (*fseek)(FILE *fp, long offset, int whence);\n\n        int (*fseeko)(FILE *fp, off_t offset, int whence);\n\n        long (*ftell)(FILE *fp);\n\n        off_t (*ftello)(FILE *fp);\n\n        void (*rewind)(FILE *fp);\n\n        int (*fgetpos)(FILE *fp, fpos_t *pos);\n\n        int (*fsetpos)(FILE *fp, const fpos_t *pos);\n\n        int (*fgetc_unlocked)(FILE *fp);\n\n        int (*fputc_unlocked)(int c, FILE *fp);\n\n        int (*getc_unlocked)(FILE *fp);\n\n        int (*putc_unlocked)(int c, FILE *fp);\n\n        void (*clearerr_unlocked)(FILE *fp);\n\n        int (*feof_unlocked)(FILE *fp);\n\n        int (*ferror_unlocked)(FILE *fp);\n\n        int (*fileno_unlocked)(FILE *fp);\n\n        int (*fflush_unlocked)(FILE *fp);\n\n        size_t (*fread_unlocked)(void *buff, size_t size, size_t n, FILE *fp);\n\n        size_t (*fwrite_unlocked)(const void *buff, size_t size, size_t n, FILE *fp);\n\n        char * (*fgets_unlocked)(char *s, int size, FILE *fp);\n\n        int (*fputs_unlocked)(const char *s, FILE *fp);\n\n        void (*clearerr)(FILE *fp);\n\n        int (*feof)(FILE *fp);\n\n        int (*ferror)(FILE *fp);\n\n        int (*fileno)(FILE *fp);\n\n        int (*fgetc)(FILE *fp);\n\n        char * (*fgets)(char *s, int size, FILE *fp);\n\n        int (*getc)(FILE *fp);\n\n        int (*ungetc)(int c, FILE *fp);\n\n        int (*fputc)(int c, FILE *fp);\n\n        int (*fputs)(const char *s, FILE *fp);\n\n        int (*putc)(int c, FILE *fp);\n\n        size_t (*fread)(void *buff, size_t size, size_t nmemb, FILE *fp);\n\n        size_t (*fwrite)(const void *buff, size_t size, size_t nmemb, FILE *fp);\n\n        int (*fprintf)(FILE *fp, const char *format, ...);\n\n        int (*vfprintf)(FILE *fp, const char *format, va_list ap);\n\n        int (*__fprintf_chk)(FILE *fp, int flag, const char *format, ...);\n\n        int (*__vfprintf_chk)(FILE *fp, int flag, const char *format, va_list ap);\n\n        ssize_t (*getdelim)(char **line, size_t *size, int delim, FILE *fp);\n\n        ssize_t (*getline)(char **line, size_t *size, FILE *fp);\n\n        int (*fscanf)(FILE *fp, const char *format, ...);\n\n        int (*vfscanf)(FILE *fp, const char *format, va_list ap);\n\n        int (*setvbuf)(FILE *fp, char *buf, int mode, size_t size);\n\n        void (*setbuf)(FILE *fp, char *buf);\n\n        void (*setbuffer)(FILE *fp, char *buf, size_t size);\n\n        void (*setlinebuf)(FILE *fp);\n\n        int (*fflush)(FILE *fp);\n\n        ssize_t (*__libc_readline_unlocked)(FILE *fp,\n                char *buffer, size_t size);\n    };\n\n} FCFSPreloadGlobalVars;\n\n#endif\n"
  },
  {
    "path": "src/tools/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I../include -I/usr/local/include\nLIB_PATH = -L../api $(LIBS) -lfastcommon -lserverframe -lfcfsapi\nTARGET_PATH = $(TARGET_PREFIX)/bin\n\nSTATIC_OBJS =\n\nALL_PRGS = fcfs_active_test fcfs_pool_stat\n\nall: $(STATIC_OBJS) $(ALL_PRGS)\n\n.o:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n\ninstall:\n\tmkdir -p $(TARGET_PATH)\n\tcp -f $(ALL_PRGS) $(TARGET_PATH)\n\nclean:\n\trm -f $(STATIC_OBJS) $(ALL_PRGS)\n\n"
  },
  {
    "path": "src/tools/fcfs_active_test.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/connection_pool.h\"\n#include \"sf/sf_proto.h\"\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"Usage: %s [options] <host:port>\\n\"\n            \"\\t-C | --connect-timeout=2: connect timeout in seconds\\n\"\n            \"\\t-N | --network-timeout=10: network timeout in seconds\\n\\n\",\n            argv[0]);\n}\n\nint main(int argc, char *argv[])\n{\n    int ch;\n    const struct option longopts[] = {\n        {\"connect-timeout\", required_argument, NULL, 'C'},\n        {\"network-timeout\", required_argument, NULL, 'N'},\n        {NULL, 0, NULL, 0}\n    };\n    char host[256];\n    ConnectionInfo conn;\n    SFResponseInfo response;\n    int connect_timeout;\n    int network_timeout;\n\tint result;\n\n    if (argc < 2) {\n        usage(argv);\n        return 1;\n    }\n\n    log_init();\n    //g_log_context.log_level = LOG_DEBUG;\n\n    connect_timeout = 2;\n    network_timeout = 10;\n    while ((ch=getopt_long(argc, argv, \"C:N:\", longopts, NULL)) != -1) {\n        switch (ch) {\n            case 'C':\n                connect_timeout = strtol(optarg, NULL, 10);\n                break;\n            case 'N':\n                network_timeout = strtol(optarg, NULL, 10);\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    if (argc - optind >= 2) {\n        if (strchr(argv[optind], ':') != NULL) {\n            fprintf(stderr, \"Error: too many arguments!\\n\");\n            usage(argv);\n            return 1;\n        }\n        snprintf(host, sizeof(host), \"%s:%s\",\n                argv[optind], argv[optind+1]);\n    } else if (argc - optind >= 1) {\n        if (strchr(argv[optind], ':') == NULL) {\n            fprintf(stderr, \"Error: expect service port!\\n\");\n            usage(argv);\n            return 1;\n        }\n        snprintf(host, sizeof(host), \"%s\", argv[optind]);\n    } else {\n        fprintf(stderr, \"Error: expect host!\\n\");\n        usage(argv);\n        return 1;\n    }\n\n    if ((result=conn_pool_parse_server_info(host, &conn, 0)) != 0) {\n        return result;\n    }\n\n    if ((result=conn_pool_connect_server(&conn, connect_timeout)) != 0) {\n        return result;\n    }\n\n    response.error.length = 0;\n    if ((result=sf_active_test(&conn, &response, network_timeout)) != 0) {\n        sf_log_network_error(&response, &conn, NULL, result);\n        return result;\n    }\n\n    printf(\"OK\\n\");\n    return 0;\n}\n"
  },
  {
    "path": "src/tools/fcfs_pool_stat.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/connection_pool.h\"\n#include \"sf/sf_proto.h\"\n#include \"fastcfs/api/fcfs_api.h\"\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"Usage: %s [-c config_file=%s] [poolname]\\n\",\n            argv[0], FCFS_FUSE_DEFAULT_CONFIG_FILENAME);\n}\n\nint main(int argc, char *argv[])\n{\n    const bool publish = false;\n    const char *config_filename = FCFS_FUSE_DEFAULT_CONFIG_FILENAME;\n    struct statvfs stbuf;\n    const char *poolname;\n    char *ns_str;\n    char ns_holder[NAME_MAX];\n    IniContext ini_context;\n    int64_t total;\n    int64_t used;\n    int64_t avail;\n    int ch;\n\tint result;\n\n    log_init();\n\n    while ((ch=getopt(argc, argv, \"c:h\")) != -1) {\n        switch (ch) {\n            case 'h':\n                usage(argv);\n                return 0;\n            case 'c':\n                config_filename = optarg;\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    if (argc - optind >= 1) {\n        poolname = argv[optind];\n    } else {\n        if ((result=iniLoadFromFile(config_filename, &ini_context)) != 0) {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                    __LINE__, config_filename, result);\n            return result;\n        }\n        ns_str = iniGetStrValue(FCFS_API_DEFAULT_FASTDIR_SECTION_NAME,\n                \"namespace\", &ini_context);\n        if (ns_str == NULL || *ns_str == '\\0') {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"config file: %s, section: %s, item: namespace \"\n                    \"not exist or is empty\", __LINE__, config_filename,\n                    FCFS_API_DEFAULT_FASTDIR_SECTION_NAME);\n            return ENOENT;\n        }\n\n        fc_safe_strcpy(ns_holder, ns_str);\n        poolname = ns_holder;\n        iniFreeContext(&ini_context);\n    }\n\n    if ((result=fcfs_api_init_with_auth(poolname,\n                    config_filename, publish)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=fcfs_api_statvfs(\"/\", &stbuf)) == 0) {\n        total = stbuf.f_blocks * stbuf.f_frsize;\n        used = (stbuf.f_blocks - stbuf.f_bfree) * stbuf.f_frsize;\n        avail = stbuf.f_bavail  * stbuf.f_frsize;\n        printf(\"{\\\"total\\\": %\"PRId64\", \\\"used\\\": %\"PRId64\", \"\n                \"\\\"avail\\\": %\"PRId64\"}\\n\", total, used, avail);\n    }\n\n    return result;\n}\n"
  },
  {
    "path": "src/vote/client/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I../common\nLIB_PATH = $(LIBS) -lfastcommon -lserverframe\nTARGET_LIB = $(TARGET_PREFIX)/$(LIB_VERSION)\n\nFAST_SHARED_OBJS = ../common/vote_global.lo ../common/vote_proto.lo \\\n                   client_func.lo client_global.lo client_proto.lo\n\nFAST_STATIC_OBJS = ../common/vote_global.o ../common/vote_proto.o \\\n                   client_func.o client_global.o client_proto.o\n\nHEADER_FILES = ../common/vote_global.h ../common/vote_types.h \\\n               ../common/vote_proto.h client_types.h client_func.h \\\n               client_global.h client_proto.h fcfs_vote_client.h\n\nALL_OBJS = $(FAST_STATIC_OBJS) $(FAST_SHARED_OBJS)\n\nALL_PRGS = \nSHARED_LIBS = libfcfsvoteclient.so\nSTATIC_LIBS = libfcfsvoteclient.a\nALL_LIBS = $(SHARED_LIBS) $(STATIC_LIBS)\n\nall: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)\nlibfcfsvoteclient.so: $(FAST_SHARED_OBJS)\n\t$(COMPILE) -o $@ -shared $(FAST_SHARED_OBJS) $(LIB_PATH)\nlibfcfsvoteclient.a: $(FAST_STATIC_OBJS)\n\tar rcs $@ $(FAST_STATIC_OBJS)\n.o:\n\t$(COMPILE) -o $@ $<  $(FAST_STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c:\n\t$(COMPILE) -o $@ $<  $(FAST_STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n.c.lo:\n\t$(COMPILE) -c -fPIC -o $@ $<  $(INC_PATH)\ninstall:\n\tmkdir -p $(TARGET_LIB)\n\tmkdir -p $(TARGET_PREFIX)/lib\n\tmkdir -p $(TARGET_PREFIX)/include/fastcfs/vote\n\n\tinstall -m 755 $(SHARED_LIBS) $(TARGET_LIB)\n\tinstall -m 644 $(HEADER_FILES) $(TARGET_PREFIX)/include/fastcfs/vote\n\t@BUILDROOT=$$(echo \"$(TARGET_PREFIX)\" | grep BUILDROOT); \\\n\tif [ -z \"$$BUILDROOT\" ] && [ \"$(TARGET_LIB)\" != \"$(TARGET_PREFIX)/lib\" ]; then ln -sf $(TARGET_LIB)/libfcfsvoteclient.so $(TARGET_PREFIX)/lib/libfcfsvoteclient.so; fi\nclean:\n\trm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)\n\n"
  },
  {
    "path": "src/vote/client/client_func.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include \"fastcommon/ini_file_reader.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"sf/sf_global.h\"\n#include \"client_global.h\"\n#include \"client_func.h\"\n\nstatic int fcfs_vote_client_do_init_ex(FCFSVoteClientContext *client_ctx,\n        IniFullContext *ini_ctx)\n{\n    int result;\n\n    client_ctx->connect_timeout = iniGetIntValueEx(\n            ini_ctx->section_name, \"connect_timeout\",\n            ini_ctx->context, SF_DEFAULT_CONNECT_TIMEOUT, true);\n    if (client_ctx->connect_timeout <= 0) {\n        client_ctx->connect_timeout = SF_DEFAULT_CONNECT_TIMEOUT;\n    }\n\n    client_ctx->network_timeout = iniGetIntValueEx(\n            ini_ctx->section_name, \"network_timeout\",\n            ini_ctx->context, SF_DEFAULT_NETWORK_TIMEOUT, true);\n    if (client_ctx->network_timeout <= 0) {\n        client_ctx->network_timeout = SF_DEFAULT_NETWORK_TIMEOUT;\n    }\n\n    if ((result=sf_load_cluster_config(&client_ctx->cluster, ini_ctx,\n                    FCFS_VOTE_DEFAULT_CLUSTER_PORT)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nvoid fcfs_vote_client_log_config_ex(FCFSVoteClientContext *client_ctx,\n        const char *extra_config)\n{\n    logInfo(\"fvote v%d.%d.%d, \"\n            \"connect_timeout=%d, \"\n            \"network_timeout=%d, \"\n            \"vote_server_count=%d%s%s\",\n            g_fcfs_vote_global_vars.version.major,\n            g_fcfs_vote_global_vars.version.minor,\n            g_fcfs_vote_global_vars.version.patch,\n            client_ctx->connect_timeout, client_ctx->network_timeout,\n            FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg),\n            extra_config != NULL ? \", \" : \"\",\n            extra_config != NULL ? extra_config : \"\");\n}\n\nint fcfs_vote_client_load_from_file_ex1(FCFSVoteClientContext *client_ctx,\n        IniFullContext *ini_ctx)\n{\n    IniContext iniContext;\n    int result;\n\n    if (ini_ctx->context == NULL) {\n        if ((result=iniLoadFromFile(ini_ctx->filename, &iniContext)) != 0) {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                    __LINE__, ini_ctx->filename, result);\n            return result;\n        }\n        ini_ctx->context = &iniContext;\n    }\n\n    result = fcfs_vote_client_do_init_ex(client_ctx, ini_ctx);\n    if (ini_ctx->context == &iniContext) {\n        iniFreeContext(&iniContext);\n        ini_ctx->context = NULL;\n    }\n\n    return result;\n}\n\nint fcfs_vote_client_init_ex2(FCFSVoteClientContext *client_ctx,\n        IniFullContext *ini_ctx)\n{\n    int result;\n\n    if ((result=fcfs_vote_client_load_from_file_ex1(\n                    client_ctx, ini_ctx)) != 0)\n    {\n        return result;\n    }\n\n    return 0;\n}\n\nint fcfs_vote_client_init_for_server(IniFullContext *ini_ctx,\n        bool *vote_node_enabled)\n{\n    g_fcfs_vote_client_vars.client_ctx.connect_timeout = SF_G_CONNECT_TIMEOUT;\n    g_fcfs_vote_client_vars.client_ctx.network_timeout = SF_G_NETWORK_TIMEOUT;\n    *vote_node_enabled = iniGetBoolValue(ini_ctx->section_name,\n            \"vote_node_enabled\", ini_ctx->context, false);\n    if (*vote_node_enabled) {\n        return sf_load_cluster_config1(&g_fcfs_vote_client_vars.client_ctx.\n                cluster, ini_ctx, \"vote_node_cluster_filename\",\n                FCFS_VOTE_DEFAULT_CLUSTER_PORT);\n    } else {\n        return 0;\n    }\n}\n\nvoid fcfs_vote_client_destroy_ex(FCFSVoteClientContext *client_ctx)\n{\n    fc_server_destroy(&client_ctx->cluster.server_cfg);\n    memset(client_ctx, 0, sizeof(FCFSVoteClientContext));\n}\n"
  },
  {
    "path": "src/vote/client/client_func.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_VOTE_CLIENT_FUNC_H\n#define _FCFS_VOTE_CLIENT_FUNC_H\n\n#include \"vote_global.h\"\n#include \"client_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define fcfs_vote_client_load_from_file(filename) \\\n    fcfs_vote_client_load_from_file_ex((&g_fcfs_vote_client_vars. \\\n                client_ctx), filename, NULL)\n\n#define fcfs_vote_client_init(filename)  \\\n    fcfs_vote_client_init_ex1((&g_fcfs_vote_client_vars. \\\n                client_ctx), filename, NULL)\n\n#define fcfs_vote_client_destroy() \\\n    fcfs_vote_client_destroy_ex((&g_fcfs_vote_client_vars.client_ctx))\n\n#define fcfs_vote_client_log_config(client_ctx) \\\n    fcfs_vote_client_log_config_ex(client_ctx, NULL)\n\nint fcfs_vote_client_load_from_file_ex1(FCFSVoteClientContext\n        *client_ctx, IniFullContext *ini_ctx);\n\n/**\n* client initial from config file\n* params:\n*       client_ctx: the client context\n*       config_filename: the client config filename\n*       section_name: the section name, NULL or empty for global section\n* return: 0 success, != 0 fail, return the error code\n**/\nstatic inline int fcfs_vote_client_load_from_file_ex(FCFSVoteClientContext\n        *client_ctx, const char *config_filename, const char *section_name)\n{\n    IniFullContext ini_ctx;\n\n    FAST_INI_SET_FULL_CTX(ini_ctx, config_filename, section_name);\n    return fcfs_vote_client_load_from_file_ex1(client_ctx, &ini_ctx);\n}\n\nint fcfs_vote_client_init_ex2(FCFSVoteClientContext *client_ctx,\n        IniFullContext *ini_ctx);\n\nstatic inline int fcfs_vote_client_init_ex1(FCFSVoteClientContext *client_ctx,\n        const char *config_filename, const char *section_name)\n{\n    IniFullContext ini_ctx;\n\n    FAST_INI_SET_FULL_CTX(ini_ctx, config_filename, section_name);\n    return fcfs_vote_client_init_ex2(client_ctx, &ini_ctx);\n}\n\nint fcfs_vote_client_init_for_server(IniFullContext *ini_ctx,\n        bool *vote_node_enabled);\n\n/**\n* client destroy function\n* params:\n*       client_ctx: tracker group\n* return: none\n**/\nvoid fcfs_vote_client_destroy_ex(FCFSVoteClientContext *client_ctx);\n\nvoid fcfs_vote_client_log_config_ex(FCFSVoteClientContext *client_ctx,\n        const char *extra_config);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/client/client_global.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"client_global.h\"\n\nFCFSVoteClientGlobalVars g_fcfs_vote_client_vars;\n"
  },
  {
    "path": "src/vote/client/client_global.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_VOTE_CLIENT_GLOBAL_H\n#define _FCFS_VOTE_CLIENT_GLOBAL_H\n\n#include \"vote_global.h\"\n#include \"client_types.h\"\n\ntypedef struct fcfs_vote_client_global_vars {\n    FCFSVoteClientContext client_ctx;\n} FCFSVoteClientGlobalVars;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern FCFSVoteClientGlobalVars g_fcfs_vote_client_vars;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/client/client_proto.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/ini_file_reader.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/connection_pool.h\"\n#include \"client_global.h\"\n#include \"client_proto.h\"\n\nstatic inline int get_spec_connection(FCFSVoteClientContext *client_ctx,\n        const ConnectionInfo *target, ConnectionInfo *conn)\n{\n    *conn = *target;\n    conn->sock = -1;\n    return conn_pool_connect_server(conn, client_ctx->connect_timeout);\n}\n\nstatic inline int make_connection(FCFSVoteClientContext *client_ctx,\n        FCServerInfo *server, ConnectionInfo *conn)\n{\n    return fc_server_make_connection(&server->group_addrs[client_ctx->\n            cluster.service_group_index].address_array, conn,\n            \"fvote\", client_ctx->connect_timeout);\n}\n\nstatic int get_connection(FCFSVoteClientContext *client_ctx,\n        ConnectionInfo *conn)\n{\n    int index;\n    int i;\n    int result;\n    FCServerInfo *server;\n\n    index = rand() % FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg);\n    server = FC_SID_SERVERS(client_ctx->cluster.server_cfg) + index;\n    if ((result=make_connection(client_ctx, server, conn)) == 0) {\n        return 0;\n    }\n\n    i = (index + 1) % FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg);\n    while (i != index) {\n        server = FC_SID_SERVERS(client_ctx->cluster.server_cfg) + i;\n        if ((result=make_connection(client_ctx, server, conn)) == 0) {\n            return 0;\n        }\n\n        i = (i + 1) % FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg);\n    }\n\n    if (FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg) > 1) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"get vote connection fail, configured server count: %d\",\n                __LINE__, FC_SID_SERVER_COUNT(client_ctx->cluster.server_cfg));\n    }\n    return result;\n}\n\nint vote_client_proto_get_master_connection_ex(FCFSVoteClientContext\n        *client_ctx, ConnectionInfo *conn)\n{\n    int result;\n    FCFSVoteProtoHeader *header;\n    FCFSVoteClientServerEntry master;\n    SFResponseInfo response;\n    FCFSVoteProtoGetServerResp server_resp;\n    char out_buff[sizeof(FCFSVoteProtoHeader)];\n\n    if ((result=get_connection(client_ctx, conn)) != 0) {\n        return result;\n    }\n\n    header = (FCFSVoteProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ,\n            sizeof(out_buff) - sizeof(FCFSVoteProtoHeader));\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_response(conn, out_buff, sizeof(out_buff),\n                    &response, client_ctx->network_timeout,\n                    FCFS_VOTE_SERVICE_PROTO_GET_MASTER_RESP, (char *)\n                    &server_resp, sizeof(FCFSVoteProtoGetServerResp))) != 0)\n    {\n        vote_log_network_error(&response, conn, result);\n        conn_pool_disconnect_server(conn);\n        return result;\n    }\n\n    master.server_id = buff2int(server_resp.server_id);\n    memcpy(master.conn.ip_addr, server_resp.ip_addr, IP_ADDRESS_SIZE);\n    *(master.conn.ip_addr + IP_ADDRESS_SIZE - 1) = '\\0';\n    master.conn.port = buff2short(server_resp.port);\n    master.conn.comm_type = conn->comm_type;\n    if (FC_CONNECTION_SERVER_EQUAL1(*conn, master.conn)) {\n        return 0;\n    }\n\n    conn_pool_disconnect_server(conn);\n    return get_spec_connection(client_ctx, &master.conn, conn);\n}\n\nint fcfs_vote_client_cluster_stat_ex(FCFSVoteClientContext *client_ctx,\n        FCFSVoteClientClusterStatEntry *stats, const int size, int *count)\n{\n    FCFSVoteProtoHeader *header;\n    FCFSVoteProtoClusterStatRespBodyHeader *body_header;\n    FCFSVoteProtoClusterStatRespBodyPart *body_part;\n    FCFSVoteProtoClusterStatRespBodyPart *body_end;\n    FCFSVoteClientClusterStatEntry *stat;\n    ConnectionInfo conn;\n    char out_buff[sizeof(FCFSVoteProtoHeader)];\n    char fixed_buff[8 * 1024];\n    char *in_buff;\n    SFResponseInfo response;\n    int result;\n    int calc_size;\n\n    if ((result=vote_client_proto_get_master_connection_ex(\n                    client_ctx, &conn)) != 0)\n    {\n        return result;\n    }\n\n    header = (FCFSVoteProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_REQ,\n            sizeof(out_buff) - sizeof(FCFSVoteProtoHeader));\n\n    response.error.length = 0;\n    in_buff = fixed_buff;\n    if ((result=sf_send_and_check_response_header(&conn,\n                    out_buff, sizeof(out_buff), &response,\n                    client_ctx->network_timeout,\n                    FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_RESP)) == 0)\n    {\n        if (response.header.body_len > sizeof(fixed_buff)) {\n            in_buff = (char *)fc_malloc(response.header.body_len);\n            if (in_buff == NULL) {\n                response.error.length = sprintf(response.error.message,\n                        \"malloc %d bytes fail\", response.header.body_len);\n                result = ENOMEM;\n            }\n        }\n\n        if (result == 0) {\n            result = tcprecvdata_nb(conn.sock, in_buff, response.\n                    header.body_len, client_ctx->network_timeout);\n        }\n    }\n\n    body_header = (FCFSVoteProtoClusterStatRespBodyHeader *)in_buff;\n    body_part = (FCFSVoteProtoClusterStatRespBodyPart *)(in_buff +\n            sizeof(FCFSVoteProtoClusterStatRespBodyHeader));\n    if (result == 0) {\n        *count = buff2int(body_header->count);\n\n        calc_size = sizeof(FCFSVoteProtoClusterStatRespBodyHeader) +\n            (*count) * sizeof(FCFSVoteProtoClusterStatRespBodyPart);\n        if (calc_size != response.header.body_len) {\n            response.error.length = sprintf(response.error.message,\n                    \"response body length: %d != calculate size: %d, \"\n                    \"server count: %d\", response.header.body_len,\n                    calc_size, *count);\n            result = EINVAL;\n        } else if (size < *count) {\n            response.error.length = sprintf(response.error.message,\n                    \"entry size %d too small < %d\", size, *count);\n            *count = 0;\n            result = ENOSPC;\n        }\n    } else {\n        *count = 0;\n    }\n\n    if (result != 0) {\n        vote_log_network_error(&response, &conn, result);\n    } else {\n        body_end = body_part + (*count);\n        for (stat=stats; body_part<body_end; body_part++, stat++) {\n            stat->is_online = body_part->is_online;\n            stat->is_master = body_part->is_master;\n            stat->server_id = buff2int(body_part->server_id);\n            memcpy(stat->ip_addr, body_part->ip_addr, IP_ADDRESS_SIZE);\n            *(stat->ip_addr + IP_ADDRESS_SIZE - 1) = '\\0';\n            stat->port = buff2short(body_part->port);\n        }\n    }\n\n    vote_client_proto_close_connection_ex(client_ctx, &conn);\n    if (in_buff != fixed_buff) {\n        if (in_buff != NULL) {\n            free(in_buff);\n        }\n    }\n\n    return result;\n}\n\nint vote_client_proto_join_ex(FCFSVoteClientContext *client_ctx,\n        ConnectionInfo *conn, const FCFSVoteClientJoinRequest *r)\n{\n    int result;\n    FCFSVoteProtoHeader *header;\n    FCFSVoteProtoClientJoinReq *req;\n    SFResponseInfo response;\n    char out_buff[sizeof(FCFSVoteProtoHeader) +\n        sizeof(FCFSVoteProtoClientJoinReq)];\n\n    header = (FCFSVoteProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_REQ,\n            sizeof(out_buff) - sizeof(FCFSVoteProtoHeader));\n    req = (FCFSVoteProtoClientJoinReq *)(header + 1);\n    int2buff(r->server_id, req->server_id);\n    int2buff(r->group_id, req->group_id);\n    short2buff(r->response_size, req->response_size);\n    req->service_id = r->service_id;\n    req->is_leader = (r->is_leader ? 1 : 0);\n    req->persistent = (r->persistent ? 1 : 0);\n    memcpy(req->config_sign, client_ctx->cluster.md5_digest,\n            SF_CLUSTER_CONFIG_SIGN_LEN);\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn,\n                    out_buff, sizeof(out_buff), &response,\n                    client_ctx->network_timeout,\n                    FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_RESP)) != 0)\n    {\n        vote_log_network_error(&response, conn, result);\n    }\n    return result;\n}\n\nint vote_client_proto_get_vote_ex(FCFSVoteClientContext *client_ctx,\n        ConnectionInfo *conn, const SFGetServerStatusRequest *r,\n        char *in_buff, const int in_len)\n{\n    int result;\n    FCFSVoteProtoHeader *header;\n    SFProtoGetServerStatusReq *req;\n    SFResponseInfo response;\n    char out_buff[sizeof(FCFSVoteProtoHeader) +\n        sizeof(SFProtoGetServerStatusReq)];\n\n    header = (FCFSVoteProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_VOTE_SERVICE_PROTO_GET_VOTE_REQ,\n            sizeof(out_buff) - sizeof(FCFSVoteProtoHeader));\n    req = (SFProtoGetServerStatusReq *)(header + 1);\n    sf_proto_get_server_status_pack(r, req);\n\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_response(conn, out_buff, sizeof(out_buff),\n                    &response, client_ctx->network_timeout,\n                    FCFS_VOTE_SERVICE_PROTO_GET_VOTE_RESP, in_buff,\n                    in_len)) != 0)\n    {\n        vote_log_network_error(&response, conn, result);\n    }\n    return result;\n}\n\nint vote_client_proto_notify_next_leader_ex(FCFSVoteClientContext *client_ctx,\n        ConnectionInfo *conn, const unsigned char req_cmd)\n{\n    int result;\n    FCFSVoteProtoHeader *header;\n    SFResponseInfo response;\n    char out_buff[sizeof(FCFSVoteProtoHeader)];\n\n    header = (FCFSVoteProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, req_cmd, sizeof(out_buff) -\n            sizeof(FCFSVoteProtoHeader));\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff,\n                    sizeof(out_buff), &response, client_ctx->\n                    network_timeout, SF_PROTO_ACK)) != 0)\n    {\n        vote_log_network_error(&response, conn, result);\n    }\n    return result;\n}\n\nint vote_client_proto_active_check_ex(FCFSVoteClientContext\n        *client_ctx, ConnectionInfo *conn)\n{\n    int result;\n    FCFSVoteProtoHeader *header;\n    SFResponseInfo response;\n    char out_buff[sizeof(FCFSVoteProtoHeader)];\n\n    header = (FCFSVoteProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_REQ,\n            sizeof(out_buff) - sizeof(FCFSVoteProtoHeader));\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn,\n                    out_buff, sizeof(out_buff), &response,\n                    client_ctx->network_timeout,\n                    FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_RESP)) != 0)\n    {\n        vote_log_network_error(&response, conn, result);\n    }\n    return result;\n}\n\nint fcfs_vote_client_get_vote_ex(FCFSVoteClientContext *client_ctx,\n        const FCFSVoteClientJoinRequest *join_request,\n        const unsigned char *servers_sign,\n        const unsigned char *cluster_sign,\n        char *in_buff, const int in_len)\n{\n    int result;\n    ConnectionInfo conn;\n    SFGetServerStatusRequest status_request;\n\n    if ((result=fcfs_vote_client_join_ex(client_ctx,\n                    &conn, join_request)) == 0)\n    {\n        status_request.servers_sign = servers_sign;\n        status_request.cluster_sign = cluster_sign;\n        status_request.server_id = join_request->server_id;\n        status_request.is_leader = join_request->is_leader;\n        result = vote_client_proto_get_vote_ex(client_ctx,\n                &conn, &status_request, in_buff, in_len);\n        vote_client_proto_close_connection_ex(client_ctx, &conn);\n    }\n\n    return result;\n}\n\nint fcfs_vote_client_notify_next_leader_ex(FCFSVoteClientContext\n        *client_ctx, const FCFSVoteClientJoinRequest *join_request,\n        const unsigned char req_cmd)\n{\n    int result;\n    ConnectionInfo conn;\n\n    if ((result=fcfs_vote_client_join_ex(client_ctx,\n                    &conn, join_request)) == 0)\n    {\n        result = vote_client_proto_notify_next_leader_ex(\n                client_ctx, &conn, req_cmd);\n        vote_client_proto_close_connection_ex(client_ctx, &conn);\n    }\n\n    return result;\n}\n"
  },
  {
    "path": "src/vote/client/client_proto.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_VOTE_CLIENT_PROTO_H\n#define _FCFS_VOTE_CLIENT_PROTO_H\n\n#include \"fastcommon/fast_mpool.h\"\n#include \"fastcommon/connection_pool.h\"\n#include \"sf/sf_proto.h\"\n#include \"vote_proto.h\"\n#include \"client_types.h\"\n\ntypedef struct fcfs_vote_client_cluster_stat_ex_entry {\n    int server_id;\n    bool is_online;\n    bool is_master;\n    uint16_t port;\n    char ip_addr[IP_ADDRESS_SIZE];\n} FCFSVoteClientClusterStatEntry;\n\ntypedef struct fcfs_vote_client_join_request {\n    int server_id;\n    int group_id;\n    short response_size;\n    short service_id;\n    bool is_leader;\n    bool persistent;\n} FCFSVoteClientJoinRequest;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint vote_client_proto_get_master_connection_ex(FCFSVoteClientContext\n        *client_ctx, ConnectionInfo *conn);\n\nint vote_client_proto_join_ex(FCFSVoteClientContext *client_ctx,\n        ConnectionInfo *conn, const FCFSVoteClientJoinRequest *r);\n\nint vote_client_proto_get_vote_ex(FCFSVoteClientContext *client_ctx,\n        ConnectionInfo *conn, const SFGetServerStatusRequest *r,\n        char *in_buff, const int in_len);\n\nint vote_client_proto_notify_next_leader_ex(FCFSVoteClientContext *client_ctx,\n        ConnectionInfo *conn, const unsigned char req_cmd);\n\nstatic inline int vote_client_proto_pre_set_next_leader_ex(\n        FCFSVoteClientContext *client_ctx, ConnectionInfo *conn)\n{\n    return vote_client_proto_notify_next_leader_ex(client_ctx, conn,\n            FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER);\n}\n\nstatic inline int vote_client_proto_commit_next_leader_ex(\n        FCFSVoteClientContext *client_ctx, ConnectionInfo *conn)\n{\n    return vote_client_proto_notify_next_leader_ex(client_ctx, conn,\n            FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER);\n}\n\nint vote_client_proto_active_check_ex(FCFSVoteClientContext\n        *client_ctx, ConnectionInfo *conn);\n\nstatic inline void vote_client_proto_close_connection_ex(\n        FCFSVoteClientContext *client_ctx, ConnectionInfo *conn)\n{\n    conn_pool_disconnect_server(conn);\n}\n\nstatic inline int fcfs_vote_client_join_ex(FCFSVoteClientContext *client_ctx,\n        ConnectionInfo *conn, const FCFSVoteClientJoinRequest *r)\n{\n    int result;\n\n    if ((result=vote_client_proto_get_master_connection_ex(\n                    client_ctx, conn)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=vote_client_proto_join_ex(client_ctx, conn, r)) != 0) {\n        vote_client_proto_close_connection_ex(client_ctx, conn);\n    }\n    return result;\n}\n\nint fcfs_vote_client_get_vote_ex(FCFSVoteClientContext *client_ctx,\n        const FCFSVoteClientJoinRequest *join_request,\n        const unsigned char *servers_sign,\n        const unsigned char *cluster_sign,\n        char *in_buff, const int in_len);\n\nint fcfs_vote_client_notify_next_leader_ex(FCFSVoteClientContext\n        *client_ctx, const FCFSVoteClientJoinRequest *join_request,\n        const unsigned char req_cmd);\n\nint fcfs_vote_client_cluster_stat_ex(FCFSVoteClientContext *client_ctx,\n        FCFSVoteClientClusterStatEntry *stats, const int size, int *count);\n\n\n#define vote_client_proto_get_master_connection(conn) \\\n    vote_client_proto_get_master_connection_ex(&g_fcfs_vote_client_vars. \\\n            client_ctx, conn)\n\n#define vote_client_proto_join(conn, r) \\\n    vote_client_proto_join_ex(&g_fcfs_vote_client_vars.client_ctx, conn, r)\n\n#define vote_client_proto_get_vote(conn, r, in_buff, in_len) \\\n    vote_client_proto_get_vote_ex(&g_fcfs_vote_client_vars. \\\n            client_ctx, conn, r, in_buff, in_len)\n\n#define vote_client_proto_notify_next_leader(conn, req_cmd) \\\n    vote_client_proto_notify_next_leader_ex(&g_fcfs_vote_client_vars. \\\n            client_ctx, conn, req_cmd)\n\n#define vote_client_proto_pre_set_next_leader(conn) \\\n    vote_client_proto_pre_set_next_leader_ex(&g_fcfs_vote_client_vars. \\\n            client_ctx, conn)\n\n#define vote_client_proto_commit_next_leader(conn) \\\n    vote_client_proto_commit_next_leader_ex(&g_fcfs_vote_client_vars. \\\n            client_ctx, conn)\n\n#define vote_client_proto_active_check(conn) \\\n    vote_client_proto_active_check_ex(&g_fcfs_vote_client_vars. \\\n            client_ctx, conn)\n\n#define vote_client_proto_close_connection(conn) \\\n    vote_client_proto_close_connection_ex(&g_fcfs_vote_client_vars. \\\n            client_ctx, conn)\n\n#define fcfs_vote_client_join(conn, r) \\\n    fcfs_vote_client_join_ex(&g_fcfs_vote_client_vars.client_ctx, conn, r)\n\n#define fcfs_vote_client_get_vote(join_request, servers_sign, \\\n        cluster_sign, in_buff, in_len) \\\n        fcfs_vote_client_get_vote_ex(&g_fcfs_vote_client_vars.client_ctx, \\\n                join_request, servers_sign, cluster_sign, in_buff, in_len)\n\n#define fcfs_vote_client_notify_next_leader(join_request, req_cmd) \\\n    fcfs_vote_client_notify_next_leader_ex(  \\\n            &g_fcfs_vote_client_vars.client_ctx, \\\n            join_request, req_cmd)\n\n#define fcfs_vote_client_pre_set_next_leader(join_request) \\\n    fcfs_vote_client_notify_next_leader_ex(  \\\n            &g_fcfs_vote_client_vars.client_ctx, join_request, \\\n            FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER)\n\n#define fcfs_vote_client_commit_next_leader(join_request) \\\n    fcfs_vote_client_notify_next_leader_ex(  \\\n            &g_fcfs_vote_client_vars.client_ctx, join_request, \\\n            FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER)\n\n#define fcfs_vote_client_cluster_stat(stats, size, count) \\\n    fcfs_vote_client_cluster_stat_ex(&g_fcfs_vote_client_vars. \\\n            client_ctx, stats, size, count)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/client/client_types.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_VOTE_CLIENT_TYPES_H\n#define _FCFS_VOTE_CLIENT_TYPES_H\n\n#include \"fastcommon/common_define.h\"\n#include \"sf/sf_configs.h\"\n#include \"sf/sf_connection_manager.h\"\n#include \"sf/sf_cluster_cfg.h\"\n#include \"vote_types.h\"\n\n#define FCFS_VOTE_CLIENT_DEFAULT_CONFIG_FILENAME \"/etc/fastcfs/vote/client.conf\"\n\ntypedef struct fcfs_vote_client_server_entry {\n    int server_id;\n    ConnectionInfo conn;\n    char status;\n} FCFSVoteClientServerEntry;\n\ntypedef struct fcfs_vote_client_context {\n    SFClusterConfig cluster;\n    int connect_timeout;\n    int network_timeout;\n} FCFSVoteClientContext;\n\n#endif\n"
  },
  {
    "path": "src/vote/client/fcfs_vote_client.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_VOTE_CLIENT_H\n#define _FCFS_VOTE_CLIENT_H\n\n#include \"client_types.h\"\n#include \"client_func.h\"\n#include \"client_global.h\"\n#include \"client_proto.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/client/tools/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I../../common -I/usr/local/include\nLIB_PATH = -L.. $(LIBS) -lfcfsvoteclient -lfastcommon -lserverframe\nTARGET_PATH = $(TARGET_PREFIX)/bin\n\nSTATIC_OBJS =\n\nALL_PRGS = fvote_cluster_stat\n\nall: $(STATIC_OBJS) $(ALL_PRGS)\n\n.o:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n\ninstall:\n\tmkdir -p $(TARGET_PATH)\n\tcp -f $(ALL_PRGS) $(TARGET_PATH)\n\nclean:\n\trm -f $(STATIC_OBJS) $(ALL_PRGS)\n"
  },
  {
    "path": "src/vote/client/tools/fvote_cluster_stat.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"fastcommon/logger.h\"\n#include \"../fcfs_vote_client.h\"\n\nstatic void usage(char *argv[])\n{\n    fprintf(stderr, \"Usage: %s [-c config_filename=%s]\\n\",\n            argv[0], FCFS_VOTE_CLIENT_DEFAULT_CONFIG_FILENAME);\n}\n\nstatic void output(FCFSVoteClientClusterStatEntry *stats, const int count)\n{\n    FCFSVoteClientClusterStatEntry *stat;\n    FCFSVoteClientClusterStatEntry *end;\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n    end = stats + count;\n    for (stat=stats; stat<end; stat++) {\n        format_ip_address(stat->ip_addr, formatted_ip);\n        printf( \"server_id: %d, host: %s:%u, \"\n                \"is_online: %d, is_master: %d\\n\",\n                stat->server_id, formatted_ip, stat->port,\n                stat->is_online, stat->is_master);\n    }\n    printf(\"\\nserver count: %d\\n\\n\", count);\n}\n\nint main(int argc, char *argv[])\n{\n#define CLUSTER_MAX_SERVER_COUNT  16\n\tint ch;\n    const char *config_filename = FCFS_VOTE_CLIENT_DEFAULT_CONFIG_FILENAME;\n    int count;\n    FCFSVoteClientClusterStatEntry stats[CLUSTER_MAX_SERVER_COUNT];\n\tint result;\n\n    while ((ch=getopt(argc, argv, \"hc:\")) != -1) {\n        switch (ch) {\n            case 'h':\n                usage(argv);\n                break;\n            case 'c':\n                config_filename = optarg;\n                break;\n            default:\n                usage(argv);\n                return 1;\n        }\n    }\n\n    log_init();\n    //g_log_context.log_level = LOG_DEBUG;\n\n    if ((result=fcfs_vote_client_init(config_filename)) != 0) {\n        return result;\n    }\n\n    if ((result=fcfs_vote_client_cluster_stat(stats,\n                    CLUSTER_MAX_SERVER_COUNT, &count)) != 0)\n    {\n        fprintf(stderr, \"fcfs_vote_client_cluster_stat fail, \"\n                \"errno: %d, error info: %s\\n\", result, STRERROR(result));\n        return result;\n    }\n\n    output(stats, count);\n    return 0;\n}\n"
  },
  {
    "path": "src/vote/common/vote_global.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"vote_global.h\"\n\nFCFSVoteGlobalVars g_fcfs_vote_global_vars = {\n    {5, 5, 0}\n};\n"
  },
  {
    "path": "src/vote/common/vote_global.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_VOTE_GLOBAL_H\n#define _FCFS_VOTE_GLOBAL_H\n\n#include \"fastcommon/common_define.h\"\n\ntypedef struct fcfs_vote_global_vars {\n    Version version;\n} FCFSVoteGlobalVars;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern FCFSVoteGlobalVars g_fcfs_vote_global_vars;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/common/vote_proto.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"vote_proto.h\"\n\nvoid fcfs_vote_proto_init()\n{\n}\n\nconst char *fcfs_vote_get_cmd_caption(const int cmd)\n{\n    switch (cmd) {\n        case FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ:\n            return \"GET_MASTER_REQ\";\n        case FCFS_VOTE_SERVICE_PROTO_GET_MASTER_RESP:\n            return \"GET_MASTER_RESP\";\n        case FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_REQ:\n            return \"CLIENT_JOIN_REQ\";\n        case FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_RESP:\n            return \"CLIENT_JOIN_RESP\";\n        case FCFS_VOTE_SERVICE_PROTO_GET_VOTE_REQ:\n            return \"GET_VOTE_REQ\";\n        case FCFS_VOTE_SERVICE_PROTO_GET_VOTE_RESP:\n            return \"GET_VOTE_RESP\";\n\n        case FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER:\n            return \"VOTE_PRE_SET_NEXT_LEADER\";\n        case FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER:\n            return \"VOTE_COMMIT_NEXT_LEADER\";\n        case FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_REQ:\n            return \"VOTE_ACTIVE_CHECK_REQ\";\n        case FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_RESP:\n            return \"VOTE_ACTIVE_CHECK_RESP\";\n\n        case FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_REQ:\n            return \"CLUSTER_STAT_REQ\";\n        case FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_RESP:\n            return \"CLUSTER_STAT_RESP\";\n        case FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_REQ:\n            return \"GET_SERVER_STATUS_REQ\";\n        case FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_RESP:\n            return \"GET_SERVER_STATUS_RESP\";\n        case FCFS_VOTE_CLUSTER_PROTO_JOIN_MASTER:\n            return \"JOIN_MASTER\";\n        case FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_REQ:\n            return \"PING_MASTER_REQ\";\n        case FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_RESP:\n            return \"PING_MASTER_RESP\";\n        case FCFS_VOTE_CLUSTER_PROTO_PRE_SET_NEXT_MASTER:\n            return \"PRE_SET_NEXT_MASTER\";\n        case FCFS_VOTE_CLUSTER_PROTO_COMMIT_NEXT_MASTER:\n            return \"COMMIT_NEXT_MASTER\";\n        default:\n            return sf_get_cmd_caption(cmd);\n    }\n}\n"
  },
  {
    "path": "src/vote/common/vote_proto.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_VOTE_PROTO_H\n#define _FCFS_VOTE_PROTO_H\n\n#include \"sf/sf_proto.h\"\n#include \"vote_types.h\"\n\n//service commands\n#define FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ            61\n#define FCFS_VOTE_SERVICE_PROTO_GET_MASTER_RESP           62\n#define FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_REQ           63\n#define FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_RESP          64\n#define FCFS_VOTE_SERVICE_PROTO_GET_VOTE_REQ    \\\n    SF_CLUSTER_PROTO_GET_SERVER_STATUS_REQ\n#define FCFS_VOTE_SERVICE_PROTO_GET_VOTE_RESP   \\\n    SF_CLUSTER_PROTO_GET_SERVER_STATUS_RESP\n#define FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER       67\n#define FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER        69\n#define FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_REQ          71\n#define FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_RESP         72\n#define FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_REQ          77\n#define FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_RESP         78\n\n//cluster commands\n#define FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_REQ     81\n#define FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_RESP    82\n#define FCFS_VOTE_CLUSTER_PROTO_JOIN_MASTER               83  //slave -> master\n#define FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_REQ           85\n#define FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_RESP          86\n#define FCFS_VOTE_CLUSTER_PROTO_PRE_SET_NEXT_MASTER       87  //notify next master to other servers\n#define FCFS_VOTE_CLUSTER_PROTO_COMMIT_NEXT_MASTER        88  //commit next master to other servers\n\ntypedef SFCommonProtoHeader  FCFSVoteProtoHeader;\n\ntypedef struct fcfs_vote_proto_client_join_req {\n    char server_id[4];\n    char group_id[4];\n    char response_size[2];\n    char is_leader;\n    unsigned char service_id;   //which service: fauth/fdir/fstore\n    char persistent;\n    char padding[3];\n    char config_sign[SF_CLUSTER_CONFIG_SIGN_LEN];\n} FCFSVoteProtoClientJoinReq;\n\ntypedef SFProtoGetServerResp FCFSVoteProtoGetServerResp;\n\ntypedef struct fcfs_vote_proto_get_server_status_req {\n    char config_sign[SF_CLUSTER_CONFIG_SIGN_LEN];\n    char server_id[4];\n    char padding[4];\n} FCFSVoteProtoGetServerStatusReq;\n\ntypedef struct fcfs_vote_proto_get_server_status_resp {\n    char server_id[4];\n    char is_master;\n    char padding[3];\n} FCFSVoteProtoGetServerStatusResp;\n\ntypedef struct fcfs_vote_proto_join_master_req {\n    char server_id[4];     //the slave server id\n    char padding[4];\n    char config_sign[SF_CLUSTER_CONFIG_SIGN_LEN];\n} FCFSVoteProtoJoinMasterReq;\n\ntypedef struct fcfs_vote_proto_cluster_stat_resp_body_header {\n    char count[4];\n    char padding[4];\n} FCFSVoteProtoClusterStatRespBodyHeader;\n\ntypedef struct fcfs_vote_proto_cluster_stat_resp_body_part {\n    char server_id[4];\n    char is_online;\n    char is_master;\n    char padding[1];\n    char port[2];\n    char ip_addr[IP_ADDRESS_SIZE];\n} FCFSVoteProtoClusterStatRespBodyPart;\n\n#define vote_log_network_error_ex(response, conn, result, log_level) \\\n    sf_log_network_error_ex(response, conn, \"fvote\", result, log_level)\n\n#define vote_log_network_error(response, conn, result) \\\n    sf_log_network_error(response, conn, \"fvote\", result)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid fcfs_vote_proto_init();\n\nconst char *fcfs_vote_get_cmd_caption(const int cmd);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/common/vote_types.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#ifndef _FCFS_VOTE_TYPES_H\n#define _FCFS_VOTE_TYPES_H\n\n#include \"fastcommon/common_define.h\"\n#include \"sf/sf_types.h\"\n\n#define FCFS_VOTE_DEFAULT_CLUSTER_PORT  41011\n#define FCFS_VOTE_DEFAULT_SERVICE_PORT  41012\n\n#define FCFS_VOTE_SERVICE_ID_FAUTH    'A'\n#define FCFS_VOTE_SERVICE_ID_FDIR     'D'\n#define FCFS_VOTE_SERVICE_ID_FSTORE   'S'\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    static inline const char *fcfs_vote_get_service_name(const int service_id)\n    {\n        switch (service_id) {\n            case FCFS_VOTE_SERVICE_ID_FAUTH:\n                return \"fauth\";\n            case FCFS_VOTE_SERVICE_ID_FDIR:\n                return \"fdir\";\n            case FCFS_VOTE_SERVICE_ID_FSTORE:\n                return \"fstore\";\n            default:\n                return \"unkown\";\n        }\n    }\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/conf/client.conf",
    "content": "# config the cluster servers\ncluster_config_filename = cluster.conf\n"
  },
  {
    "path": "src/vote/conf/cluster.conf",
    "content": "\n[group-cluster]\n# the default cluster port\nport = 41011\n\n[group-service]\n# the default service port\nport = 41012\n\n## Important:server group mark, don't modify this line.\n\n# config a server\n# section format: [server-$id]\n# server id is a 32 bits natural number (1, 2, 3 etc.),\n[server-1]\n\n# format: host[:port]\n# host can be an IP address or a hostname, IPv6 is supported\n# IP address is recommended\n# can occur more than once\nhost = 172.16.168.128\n"
  },
  {
    "path": "src/vote/conf/full/client.conf",
    "content": "# connect timeout in seconds\n# default value is 10\n# Note: in the intranet network (LAN), 10 seconds is enough.\nconnect_timeout = 10\n\n# network timeout in seconds\n# default value is 60\nnetwork_timeout = 60\n\n# config the cluster servers\ncluster_config_filename = cluster.conf\n\n#standard log level as syslog, case insensitive, value list:\n### emerg for emergency\n### alert\n### crit for critical\n### error\n### warn for warning\n### notice\n### info\n### debug\nlog_level = info\n"
  },
  {
    "path": "src/vote/conf/full/cluster.conf",
    "content": "\n[master-election]\n# the quorum for master election\n# set quorum to majority to avoid brain-split\n# value list:\n##  any: no requirement\n##  majority: more than half\n##  auto: set to majority when the number of nodes is odd,\n##        otherwise set to any\n# default value is auto\nquorum = auto\n\n# the timeout to determinate master lost\n# the default value is 3 seconds\nmaster_lost_timeout = 3\n\n# the max wait time for master election\n# this parameter is for the master restart\n# the default value is 5 seconds\nmax_wait_time = 5\n\n\n[group-cluster]\n# the default cluster port\nport = 41011\n\n[group-service]\n# the default service port\nport = 41012\n\n## Important:server group mark, don't modify this line.\n\n# config a server\n# section format: [server-$id]\n# server id is a 32 bits natural number (1, 2, 3 etc.),\n[server-1]\n\n# format: host[:port]\n# host can be an IP address or a hostname, IPv6 is supported\n# IP address is recommended\n# can occur more than once\nhost = 172.16.168.128\n"
  },
  {
    "path": "src/vote/conf/full/server.conf",
    "content": "# connect timeout in seconds\n# default value is 10\n# Note: in the intranet network (LAN), 10 seconds is enough.\n# do NOT set to 1 second because of time accuracy!\nconnect_timeout = 10\n\n# network timeout in seconds for send and recv\n# default value is 60\nnetwork_timeout = 60\n\n# the base path to store log files\n# this path must be exist\nbase_path = /opt/fastcfs/vote\n\n# max concurrent connections this server support\n# you should set this parameter larger, eg. 10240\n# default value is 256\nmax_connections = 10240\n\n# the min network buff size\n# default value 8KB\nmin_buff_size = 64KB\n\n# the max network buff size\n# default value 128KB\nmax_buff_size = 256KB\n\n# max pkg size\n# default value is 16K\nmax_pkg_size = 256KB\n\n# if io_uring send with zero copy when Linux kernel version >= 5.19\n# set to true when the NIC support zero copy\n# can be overwritten in sections: [cluster] and [service]\n# default value is true\nuse_send_zc = true\n\n# TCP quick ack for Linux (setsockopt with TCP_QUICKACK option)\n# default value is true\ntcp_quick_ack = true\n\n# config the cluster servers and groups\ncluster_config_filename = cluster.conf\n\n# the session config filename\nsession_config_filename = session.conf\n\n#standard log level as syslog, case insensitive, value list:\n### emerg for emergency\n### alert\n### crit for critical\n### error\n### warn for warning\n### notice\n### info\n### debug\nlog_level = info\n\n#unix group name to run this program, \n#not set (empty) means run by the group of current user\nrun_by_group=\n\n#unix username to run this program,\n#not set (empty) means run by current user\nrun_by_user =\n\n# thread stack size, should >= 64KB\n# default value is 256KB\nthread_stack_size = 256KB\n\n\n# NOTE: following global parameters for error log and slow log\n# which can be overwritten in [error-log] and [slow-log] sections\n\n# sync log buff to disk every interval seconds\n# default value is 1 seconds\nsync_log_buff_interval = 1\n\n# if rotate the log file every day\n# set to true for rotate the log file anyway at the rotate time\n# default value is true\nlog_file_rotate_everyday = true\n\n# the time to rotate the log file, format is Hour:Minute\n# Hour from 0 to 23, Minute from 0 to 59\n# valid only when log_file_rotate_everyday is true\n# default value is 00:00\nlog_file_rotate_time = 00:00\n\n# if compress the old log file by gzip\n# default value is false\nlog_file_compress_old = false\n\n# compress the log file days before\n# default value is 1\nlog_file_compress_days_before = 7\n\n# rotate the log file when the log file exceeds this size\n# 0 means never rotates log file by log file size\n# default value is 0\nlog_file_rotate_on_size = 0\n\n# keep days of the log files\n# 0 means do not delete the old log files\n# default value is 15\nlog_file_keep_days = 15\n\n# the time to delete the old log files, format is Hour:Minute\n# Hour from 0 to 23, Minute from 0 to 59\n# valid only when log_file_keep_days > 0\n# default value is 01:30\nlog_file_delete_old_time = 01:30\n\n\n[error-log]\n# global log parameters can be overwritten here for error log\n\n[slow-log]\n# global log parameters can be overwritten here for slow log\n\n# if enable the slow log\n# default value is false\nenabled = true\n\n# the filename prefix of the slow log\n# default value is slow\nfilename_prefix = slow\n\n# log the request to the slow log whose response time exceeds this parameter\n# default value is 100ms\nlog_slower_than_ms = 50\n\n\n[cluster]\n# bind an address of this host\n# empty for bind all addresses of this host\nbind_addr =\n\n# the listen port\nport = 41011\n\n# the accept thread count\n# default value is 1 which is recommended\naccept_threads = 1\n\n# the network thread count\n# these threads deal network io\n# dispatched by the incoming socket fd\n# default value is 4\nwork_threads = 4\n\n# if io_uring send with zero copy when Linux kernel version >= 5.19\n# set to true when the NIC support zero copy\n# default value set by global config\nuse_send_zc = true\n\n\n[service]\nbind_addr =\nport = 41012\naccept_threads = 1\nwork_threads = 4\nuse_send_zc = true\n"
  },
  {
    "path": "src/vote/conf/server.conf",
    "content": "# the base path to store log files\n# this path must be exist\nbase_path = /opt/fastcfs/vote\n\n# max concurrent connections this server support\n# you should set this parameter larger, eg. 10240\n# default value is 256\nmax_connections = 10240\n\n# config the cluster servers and groups\ncluster_config_filename = cluster.conf\n\n[cluster]\n# the listen port\nport = 41011\n\n# the network thread count\n# these threads deal network io\n# dispatched by the incoming socket fd\n# default value is 4\nwork_threads = 4\n\n\n[service]\n# the listen port\nport = 41012\n\n# the network thread count\n# these threads deal network io\n# dispatched by the incoming socket fd\n# default value is 4\nwork_threads = 4\n"
  },
  {
    "path": "src/vote/server/Makefile.in",
    "content": ".SUFFIXES: .c .o .lo\n\nCOMPILE = $(CC) $(CFLAGS)\nINC_PATH = -I.. -I../common\nLIB_PATH = $(LIBS) -lfastcommon -lserverframe\nTARGET_PATH = $(TARGET_PREFIX)/bin\n\nSTATIC_OBJS = ../common/vote_global.o ../common/vote_proto.o  \\\n              server_global.o server_func.o common_handler.o service_handler.o \\\n              cluster_handler.o cluster_relationship.o cluster_info.o \\\n              service_group_htable.o\n\nALL_PRGS = fcfs_voted\n\nall: $(STATIC_OBJS) $(ALL_PRGS)\n\n$(ALL_PRGS): $(STATIC_OBJS)\n\n.o:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c:\n\t$(COMPILE) -o $@ $<  $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)\n.c.o:\n\t$(COMPILE) -c -o $@ $<  $(INC_PATH)\n\ninstall:\n\tmkdir -p $(TARGET_PATH)\n\tcp -f $(ALL_PRGS) $(TARGET_PATH)\n\nclean:\n\trm -f $(STATIC_OBJS) $(ALL_PRGS)\n"
  },
  {
    "path": "src/vote/server/cluster_handler.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//cluster_handler.c\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <sys/stat.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <limits.h>\n#include <fcntl.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"fastcommon/ioevent_loop.h\"\n#include \"sf/sf_util.h\"\n#include \"sf/sf_func.h\"\n#include \"sf/sf_nio.h\"\n#include \"sf/sf_global.h\"\n#include \"common/vote_proto.h\"\n#include \"server_global.h\"\n#include \"server_func.h\"\n#include \"cluster_info.h\"\n#include \"cluster_relationship.h\"\n#include \"common_handler.h\"\n#include \"cluster_handler.h\"\n\nint cluster_handler_init()\n{\n    return 0;\n}\n\nint cluster_handler_destroy()\n{   \n    return 0;\n}\n\nint cluster_recv_timeout_callback(struct fast_task_info *task)\n{\n    if (SERVER_TASK_TYPE == VOTE_SERVER_TASK_TYPE_RELATIONSHIP &&\n            CLUSTER_PEER != NULL)\n    {\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"cluster client ip: %s, server id: %d, recv timeout\",\n                __LINE__, task->client_ip, CLUSTER_PEER->server->id);\n        return ETIMEDOUT;\n    }\n\n    return 0;\n}\n\nvoid cluster_task_finish_cleanup(struct fast_task_info *task)\n{\n    switch (SERVER_TASK_TYPE) {\n        case VOTE_SERVER_TASK_TYPE_RELATIONSHIP:\n            if (CLUSTER_PEER != NULL) {\n                CLUSTER_PEER->is_online = false;\n                cluster_relationship_add_to_inactive_sarray(CLUSTER_PEER);\n                CLUSTER_PEER = NULL;\n            } else {\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"mistake happen! task: %p, SERVER_TASK_TYPE: %d, \"\n                        \"CLUSTER_PEER is NULL\", __LINE__, task,\n                        SERVER_TASK_TYPE);\n            }\n            SERVER_TASK_TYPE = SF_SERVER_TASK_TYPE_NONE;\n            break;\n        default:\n            break;\n    }\n\n    sf_task_finish_clean_up(task);\n}\n\nstatic int cluster_check_config_sign(struct fast_task_info *task,\n        const int server_id, const char *config_sign)\n{\n    if (memcmp(config_sign, CLUSTER_CONFIG_SIGN_BUF,\n                SF_CLUSTER_CONFIG_SIGN_LEN) != 0)\n    {\n        char peer_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1];\n        char my_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1];\n\n        bin2hex(config_sign, SF_CLUSTER_CONFIG_SIGN_LEN, peer_hex);\n        bin2hex((const char *)CLUSTER_CONFIG_SIGN_BUF,\n                SF_CLUSTER_CONFIG_SIGN_LEN, my_hex);\n\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"server #%d 's cluster config md5: %s != mine: %s\",\n                server_id, peer_hex, my_hex);\n        return EFAULT;\n    }\n\n    return 0;\n}\n\nstatic int cluster_deal_get_server_status(struct fast_task_info *task)\n{\n    int result;\n    int server_id;\n    FCFSVoteProtoGetServerStatusReq *req;\n    FCFSVoteProtoGetServerStatusResp *resp;\n\n    if ((result=server_expect_body_length(sizeof(\n                        FCFSVoteProtoGetServerStatusReq))) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSVoteProtoGetServerStatusReq *)REQUEST.body;\n    server_id = buff2int(req->server_id);\n    if ((result=cluster_check_config_sign(task, server_id,\n                    req->config_sign)) != 0)\n    {\n        return result;\n    }\n\n    resp = (FCFSVoteProtoGetServerStatusResp *)SF_PROTO_SEND_BODY(task);\n    resp->is_master = MYSELF_IS_MASTER;\n    int2buff(CLUSTER_MY_SERVER_ID, resp->server_id);\n\n    RESPONSE.header.body_len = sizeof(FCFSVoteProtoGetServerStatusResp);\n    RESPONSE.header.cmd = FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_RESP;\n    TASK_CTX.common.response_done = true;\n    return 0;\n}\n\nstatic int cluster_deal_join_master(struct fast_task_info *task)\n{\n    int result;\n    int server_id;\n    FCFSVoteProtoJoinMasterReq *req;\n    FCFSVoteClusterServerInfo *peer;\n\n    if ((result=server_expect_body_length(sizeof(\n                        FCFSVoteProtoJoinMasterReq))) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSVoteProtoJoinMasterReq *)REQUEST.body;\n    server_id = buff2int(req->server_id);\n    peer = fcfs_vote_get_server_by_id(server_id);\n    if (peer == NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"peer server id: %d not exist\", server_id);\n        return ENOENT;\n    }\n\n    if ((result=cluster_check_config_sign(task, server_id,\n                    req->config_sign)) != 0)\n    {\n        return result;\n    }\n\n    if (CLUSTER_MYSELF_PTR != CLUSTER_MASTER_ATOM_PTR &&\n            CLUSTER_MYSELF_PTR != CLUSTER_NEXT_MASTER)\n    {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"i am not master\");\n        return SF_RETRIABLE_ERROR_NOT_MASTER;\n    }\n\n    if (peer == CLUSTER_MYSELF_PTR) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"can't join self\");\n        return EINVAL;\n    }\n\n    if (CLUSTER_PEER != NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"peer server id: %d already joined\", server_id);\n        return EEXIST;\n    }\n\n    SERVER_TASK_TYPE = VOTE_SERVER_TASK_TYPE_RELATIONSHIP;\n    CLUSTER_PEER = peer;\n    CLUSTER_PEER->is_online = true;\n    cluster_relationship_remove_from_inactive_sarray(peer);\n    return 0;\n}\n\nstatic int cluster_deal_ping_master(struct fast_task_info *task)\n{\n    int result;\n\n    if ((result=server_expect_body_length(0)) != 0) {\n        return result;\n    }\n\n    if (CLUSTER_PEER == NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"please join first\");\n        return EINVAL;\n    }\n\n    if (CLUSTER_MYSELF_PTR != CLUSTER_MASTER_ATOM_PTR &&\n            CLUSTER_MYSELF_PTR != CLUSTER_NEXT_MASTER)\n    {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"i am not master\");\n        return SF_RETRIABLE_ERROR_NOT_MASTER;\n    }\n\n    RESPONSE.header.cmd = FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_RESP;\n    return 0;\n}\n\nstatic int cluster_deal_next_master(struct fast_task_info *task)\n{\n    int result;\n    int master_id;\n    FCFSVoteClusterServerInfo *master;\n\n    if ((result=server_expect_body_length(4)) != 0) {\n        return result;\n    }\n\n    if (CLUSTER_MYSELF_PTR == CLUSTER_MASTER_ATOM_PTR) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"i am already master\");\n        cluster_relationship_trigger_reselect_master();\n        return EEXIST;\n    }\n\n    master_id = buff2int(REQUEST.body);\n    master = fcfs_vote_get_server_by_id(master_id);\n    if (master == NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"master server id: %d not exist\", master_id);\n        return ENOENT;\n    }\n\n    if (REQUEST.header.cmd == FCFS_VOTE_CLUSTER_PROTO_PRE_SET_NEXT_MASTER) {\n        return cluster_relationship_pre_set_master(master);\n    } else {\n        return cluster_relationship_commit_master(master);\n    }\n}\n\nstatic int cluster_process(struct fast_task_info *task)\n{\n    int result;\n    int cmd;\n\n    cmd = REQUEST.header.cmd;\n    if (!MYSELF_IS_MASTER) {\n    }\n\n    switch (cmd) {\n        case SF_PROTO_ACTIVE_TEST_REQ:\n            /*\n            if (SERVER_TASK_TYPE == VOTE_SERVER_TASK_TYPE_SUBSCRIBE &&\n                    !MYSELF_IS_MASTER)\n            {\n                RESPONSE.error.length = sprintf(\n                        RESPONSE.error.message,\n                        \"i am not master\");\n                return SF_RETRIABLE_ERROR_NOT_MASTER;\n            }\n            */\n            RESPONSE.header.cmd = SF_PROTO_ACTIVE_TEST_RESP;\n            return sf_proto_deal_active_test(task, &REQUEST, &RESPONSE);\n        case FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_REQ:\n            return cluster_deal_get_server_status(task);\n        case FCFS_VOTE_CLUSTER_PROTO_PRE_SET_NEXT_MASTER:\n        case FCFS_VOTE_CLUSTER_PROTO_COMMIT_NEXT_MASTER:\n            return cluster_deal_next_master(task);\n        case FCFS_VOTE_CLUSTER_PROTO_JOIN_MASTER:\n            return cluster_deal_join_master(task);\n        case FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_REQ:\n            return cluster_deal_ping_master(task);\n        case FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ:\n            if ((result=fcfs_vote_deal_get_master(task,\n                            CLUSTER_GROUP_INDEX)) == 0)\n            {\n                RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_GET_MASTER_RESP;\n            }\n            return result;\n        case SF_SERVICE_PROTO_GET_LEADER_REQ:\n            if ((result=fcfs_vote_deal_get_master(task,\n                            CLUSTER_GROUP_INDEX)) == 0)\n            {\n                RESPONSE.header.cmd = SF_SERVICE_PROTO_GET_LEADER_RESP;\n            }\n            return result;\n        default:\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"unkown cluster cmd: %d\", REQUEST.header.cmd);\n            return -EINVAL;\n    }\n}\n\nint cluster_deal_task(struct fast_task_info *task, const int stage)\n{\n    int result;\n\n    if (stage == SF_NIO_STAGE_CONTINUE) {\n        if (task->continue_callback != NULL) {\n            result = task->continue_callback(task);\n        } else {\n            result = RESPONSE_STATUS;\n            if (result == TASK_STATUS_CONTINUE) {\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"unexpect status: %d\", __LINE__, result);\n                result = EBUSY;\n            }\n        }\n    } else {\n        sf_proto_init_task_context(task, &TASK_CTX.common);\n        result = cluster_process(task);\n    }\n\n    if (result == TASK_STATUS_CONTINUE) {\n        return 0;\n    } else {\n        RESPONSE_STATUS = result;\n        return sf_proto_deal_task_done(task, \"cluster\", &TASK_CTX.common);\n    }\n}\n"
  },
  {
    "path": "src/vote/server/cluster_handler.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//cluster_handler.h\n\n#ifndef FCFS_VOTE_CLUSTER_HANDLER_H\n#define FCFS_VOTE_CLUSTER_HANDLER_H\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"fastcommon/fast_task_queue.h\"\n#include \"server_types.h\"\n\n#ifdef __cplusplus\n\nextern \"C\" {\n#endif\n\nint cluster_handler_init();\nint cluster_handler_destroy();\nint cluster_deal_task(struct fast_task_info *task, const int stage);\nvoid cluster_task_finish_cleanup(struct fast_task_info *task);\nint cluster_recv_timeout_callback(struct fast_task_info *task);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/server/cluster_info.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"fastcommon/local_ip_func.h\"\n#include \"server_global.h\"\n#include \"cluster_info.h\"\n\nstatic int init_cluster_server_array()\n{\n    int bytes;\n    FCFSVoteClusterServerInfo *cs;\n    FCServerInfo *server;\n    FCServerInfo *end;\n\n    bytes = sizeof(FCFSVoteClusterServerInfo) *\n        FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG);\n    if ((CLUSTER_SERVER_ARRAY.servers=(FCFSVoteClusterServerInfo *)\n                fc_malloc(bytes)) == NULL)\n    {\n        return ENOMEM;\n    }\n    memset(CLUSTER_SERVER_ARRAY.servers, 0, bytes);\n\n    end = FC_SID_SERVERS(CLUSTER_SERVER_CONFIG) +\n        FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG);\n    for (server=FC_SID_SERVERS(CLUSTER_SERVER_CONFIG),\n            cs=CLUSTER_SERVER_ARRAY.servers; server<end; server++, cs++)\n    {\n        cs->server = server;\n    }\n\n    CLUSTER_SERVER_ARRAY.count = FC_SID_SERVER_COUNT(CLUSTER_SERVER_CONFIG);\n    return 0;\n}\n\nstatic int find_myself_in_cluster_config(const char *filename)\n{\n    const char *local_ip;\n    struct {\n        const char *ip_addr;\n        int port;\n    } found;\n    FCServerInfo *server;\n    FCFSVoteClusterServerInfo *myself;\n    SFNetworkHandler *service_handler;\n    SFNetworkHandler *cluster_handler;\n    char formatted_found_ip[FORMATTED_IP_SIZE];\n    char formatted_local_ip[FORMATTED_IP_SIZE];\n    int ports[4];\n    int count;\n    int i;\n\n    service_handler = SERVICE_SF_CTX.handlers[SF_IPV4_ADDRESS_FAMILY_INDEX].\n        handlers + SF_SOCKET_NETWORK_HANDLER_INDEX;\n    cluster_handler = CLUSTER_SF_CTX.handlers[SF_IPV4_ADDRESS_FAMILY_INDEX].\n        handlers + SF_SOCKET_NETWORK_HANDLER_INDEX;\n    count = 0;\n    ports[count++] = service_handler->inner.port;\n    if (service_handler->outer.port != service_handler->inner.port) {\n        ports[count++] = service_handler->outer.port;\n    }\n\n    ports[count++] = cluster_handler->inner.port;\n    if (cluster_handler->outer.port != cluster_handler->inner.port) {\n        ports[count++] = cluster_handler->outer.port;\n    }\n\n    found.ip_addr = NULL;\n    found.port = 0;\n    local_ip = get_first_local_ip();\n    while (local_ip != NULL) {\n        for (i=0; i<count; i++) {\n            server = fc_server_get_by_ip_port(&CLUSTER_SERVER_CONFIG,\n                    local_ip, ports[i]);\n            if (server != NULL) {\n                myself = CLUSTER_SERVER_ARRAY.servers +\n                    (server - FC_SID_SERVERS(CLUSTER_SERVER_CONFIG));\n                if (CLUSTER_MYSELF_PTR == NULL) {\n                    CLUSTER_MYSELF_PTR = myself;\n                } else if (myself != CLUSTER_MYSELF_PTR) {\n                    format_ip_address(found.ip_addr, formatted_found_ip);\n                    format_ip_address(local_ip, formatted_local_ip);\n                    logError(\"file: \"__FILE__\", line: %d, \"\n                            \"cluster config file: %s, my ip and port \"\n                            \"in more than one instances, %s:%u in \"\n                            \"server id %d, and %s:%u in server id %d\",\n                            __LINE__, filename, formatted_found_ip, found.port,\n                            CLUSTER_MY_SERVER_ID, formatted_local_ip,\n                            ports[i], myself->server->id);\n                    return EEXIST;\n                }\n\n                found.ip_addr = local_ip;\n                found.port = ports[i];\n            }\n        }\n\n        local_ip = get_next_local_ip(local_ip);\n    }\n\n    if (CLUSTER_MYSELF_PTR == NULL) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"cluster config file: %s, can't find myself \"\n                \"by my local ip and listen port\", __LINE__, filename);\n        return ENOENT;\n    }\n\n    return 0;\n}\n\nFCFSVoteClusterServerInfo *fcfs_vote_get_server_by_id(const int server_id)\n{\n    FCServerInfo *server;\n    server = fc_server_get_by_id(&CLUSTER_SERVER_CONFIG, server_id);\n    if (server == NULL) {\n        return NULL;\n    }\n\n    return CLUSTER_SERVER_ARRAY.servers + (server -\n            FC_SID_SERVERS(CLUSTER_SERVER_CONFIG));\n}\n\nint cluster_info_init(const char *cluster_config_filename)\n{\n    int result;\n\n    if ((result=init_cluster_server_array()) != 0) {\n        return result;\n    }\n\n    if ((result=find_myself_in_cluster_config(cluster_config_filename)) != 0) {\n        return result;\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "src/vote/server/cluster_info.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//cluster_info.h\n\n#ifndef _CLUSTER_INFO_H_\n#define _CLUSTER_INFO_H_\n\n#include \"server_global.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint cluster_info_init(const char *cluster_config_filename);\n\nFCFSVoteClusterServerInfo *fcfs_vote_get_server_by_id(const int server_id);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/server/cluster_relationship.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"sf/sf_configs.h\"\n#include \"sf/sf_service.h\"\n#include \"sf/sf_func.h\"\n#include \"common/vote_proto.h\"\n#include \"server_global.h\"\n#include \"service_group_htable.h\"\n#include \"cluster_relationship.h\"\n\ntypedef struct fcfs_vote_cluster_server_status {\n    FCFSVoteClusterServerInfo *cs;\n    bool is_master;\n    int server_id;\n} FCFSVoteClusterServerStatus;\n\ntypedef struct fcfs_vote_cluster_server_detect_entry {\n    FCFSVoteClusterServerInfo *cs;\n    int next_time;\n} FCFSVoteClusterServerDetectEntry;\n\ntypedef struct fcfs_vote_cluster_server_detect_array {\n    FCFSVoteClusterServerDetectEntry *entries;\n    int count;\n    int alloc;\n    pthread_mutex_t lock;\n} FCFSVoteClusterServerDetectArray;\n\ntypedef struct fcfs_vote_cluster_relationship_context {\n    FCFSVoteClusterServerDetectArray inactive_server_array;\n} FCFSVoteClusterRelationshipContext;\n\n#define INACTIVE_SERVER_ARRAY relationship_ctx.inactive_server_array\n\nstatic FCFSVoteClusterRelationshipContext relationship_ctx = {\n    {NULL, 0, 0}\n};\n\n#define SET_SERVER_DETECT_ENTRY(entry, server) \\\n    do {  \\\n        entry->cs = server;    \\\n        entry->next_time = g_current_time + 1; \\\n    } while (0)\n\nstatic int proto_get_server_status(ConnectionInfo *conn,\n        const int network_timeout,\n        FCFSVoteClusterServerStatus *server_status)\n{\n\tint result;\n\tFCFSVoteProtoHeader *header;\n    FCFSVoteProtoGetServerStatusReq *req;\n    FCFSVoteProtoGetServerStatusResp *resp;\n    SFResponseInfo response;\n\tchar out_buff[sizeof(FCFSVoteProtoHeader) +\n        sizeof(FCFSVoteProtoGetServerStatusReq)];\n\tchar in_body[sizeof(FCFSVoteProtoGetServerStatusResp)];\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n    header = (FCFSVoteProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_REQ,\n            sizeof(out_buff) - sizeof(FCFSVoteProtoHeader));\n\n    req = (FCFSVoteProtoGetServerStatusReq *)(out_buff +\n            sizeof(FCFSVoteProtoHeader));\n    int2buff(CLUSTER_MY_SERVER_ID, req->server_id);\n    memcpy(req->config_sign, CLUSTER_CONFIG_SIGN_BUF,\n            SF_CLUSTER_CONFIG_SIGN_LEN);\n\n    response.error.length = 0;\n\tif ((result=sf_send_and_check_response_header(conn, out_buff,\n\t\t\tsizeof(out_buff), &response, network_timeout,\n            FCFS_VOTE_CLUSTER_PROTO_GET_SERVER_STATUS_RESP)) != 0)\n    {\n        vote_log_network_error(&response, conn, result);\n        return result;\n    }\n\n    if (response.header.body_len != sizeof(FCFSVoteProtoGetServerStatusResp)) {\n        format_ip_address(conn->ip_addr, formatted_ip);\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"server %s:%u, recv body length: %d != %d\",\n                __LINE__, formatted_ip, conn->port,\n                response.header.body_len, (int)\n                sizeof(FCFSVoteProtoGetServerStatusResp));\n        return EINVAL;\n    }\n\n    if ((result=tcprecvdata_nb(conn->sock, in_body, response.\n                    header.body_len, network_timeout)) != 0)\n    {\n        format_ip_address(conn->ip_addr, formatted_ip);\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"recv from server %s:%u fail, \"\n                \"errno: %d, error info: %s\",\n                __LINE__, formatted_ip, conn->port,\n                result, STRERROR(result));\n        return result;\n    }\n\n    resp = (FCFSVoteProtoGetServerStatusResp *)in_body;\n    server_status->is_master = resp->is_master;\n    server_status->server_id = buff2int(resp->server_id);\n    return 0;\n}\n\nstatic void init_inactive_server_array()\n{\n    FCFSVoteClusterServerInfo *cs;\n    FCFSVoteClusterServerInfo *end;\n    FCFSVoteClusterServerDetectEntry *entry;\n\n    PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock);\n    entry = INACTIVE_SERVER_ARRAY.entries;\n    end = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count;\n    for (cs=CLUSTER_SERVER_ARRAY.servers; cs<end; cs++) {\n        if (cs != CLUSTER_MYSELF_PTR) {\n            SET_SERVER_DETECT_ENTRY(entry, cs);\n            entry++;\n        }\n    }\n\n    INACTIVE_SERVER_ARRAY.count = entry - INACTIVE_SERVER_ARRAY.entries;\n    PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock);\n}\n\nstatic inline bool cluster_unset_master(FCFSVoteClusterServerInfo *master)\n{\n    return __sync_bool_compare_and_swap(&CLUSTER_MASTER_PTR, master, NULL);\n}\n\nstatic int proto_join_master(ConnectionInfo *conn, const int network_timeout)\n{\n\tint result;\n\tFCFSVoteProtoHeader *header;\n    FCFSVoteProtoJoinMasterReq *req;\n    SFResponseInfo response;\n\tchar out_buff[sizeof(FCFSVoteProtoHeader) + sizeof(FCFSVoteProtoJoinMasterReq)];\n\n    header = (FCFSVoteProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, FCFS_VOTE_CLUSTER_PROTO_JOIN_MASTER,\n            sizeof(out_buff) - sizeof(FCFSVoteProtoHeader));\n\n    req = (FCFSVoteProtoJoinMasterReq *)(out_buff + sizeof(FCFSVoteProtoHeader));\n    int2buff(CLUSTER_MY_SERVER_ID, req->server_id);\n    memcpy(req->config_sign, CLUSTER_CONFIG_SIGN_BUF,\n            SF_CLUSTER_CONFIG_SIGN_LEN);\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, out_buff,\n                    sizeof(out_buff), &response, network_timeout,\n                    SF_PROTO_ACK)) != 0)\n    {\n        vote_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nstatic int proto_ping_master(ConnectionInfo *conn, const int network_timeout)\n{\n    FCFSVoteProtoHeader header;\n    SFResponseInfo response;\n    int result;\n\n    SF_PROTO_SET_HEADER(&header, FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_REQ, 0);\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(conn, (char *)&header,\n                    sizeof(header), &response, network_timeout,\n                    FCFS_VOTE_CLUSTER_PROTO_PING_MASTER_RESP)) != 0)\n    {\n        vote_log_network_error(&response, conn, result);\n    }\n\n    return result;\n}\n\nstatic int cluster_cmp_server_status(const void *p1, const void *p2)\n{\n    FCFSVoteClusterServerStatus *status1;\n    FCFSVoteClusterServerStatus *status2;\n    int sub;\n\n    status1 = (FCFSVoteClusterServerStatus *)p1;\n    status2 = (FCFSVoteClusterServerStatus *)p2;\n    sub = (int)status1->is_master - (int)status2->is_master;\n    if (sub != 0) {\n        return sub;\n    }\n\n    return (int)status1->server_id - (int)status2->server_id;\n}\n\n#define cluster_get_server_status(server_status) \\\n    cluster_get_server_status_ex(server_status, true)\n\nstatic int cluster_get_server_status_ex(FCFSVoteClusterServerStatus\n        *server_status, const bool log_connect_error)\n{\n    const int connect_timeout = 2;\n    const int network_timeout = 2;\n    ConnectionInfo conn;\n    int result;\n\n    if (server_status->cs == CLUSTER_MYSELF_PTR) {\n        server_status->is_master = MYSELF_IS_MASTER;\n        server_status->server_id = CLUSTER_MY_SERVER_ID;\n        return 0;\n    } else {\n        if ((result=fc_server_make_connection_ex(&CLUSTER_GROUP_ADDRESS_ARRAY(\n                            server_status->cs->server), &conn, \"fvote\",\n                        connect_timeout, NULL, log_connect_error)) != 0)\n        {\n            return result;\n        }\n\n        result = proto_get_server_status(&conn,\n                network_timeout, server_status);\n        conn_pool_disconnect_server(&conn);\n        return result;\n    }\n}\n\nstatic int do_check_brainsplit(FCFSVoteClusterServerInfo *cs)\n{\n    int result;\n    const bool log_connect_error = false;\n    FCFSVoteClusterServerStatus server_status;\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n    server_status.cs = cs;\n    server_status.is_master = false;\n    server_status.server_id = 0;\n    if ((result=cluster_get_server_status_ex(&server_status,\n                    log_connect_error)) != 0)\n    {\n        return result;\n    }\n\n    if (server_status.is_master) {\n        format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                    cs->server), formatted_ip);\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"two masters occurs, anonther master id: %d, %s:%u, \"\n                \"trigger re-select master ...\", __LINE__, cs->server->id,\n                formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(cs->server));\n        cluster_relationship_trigger_reselect_master();\n        return EEXIST;\n    }\n\n    return 0;\n}\n\nstatic int cluster_check_brainsplit(int *inactive_count)\n{\n    FCFSVoteClusterServerDetectEntry *entry;\n    FCFSVoteClusterServerDetectEntry *end;\n    int result;\n\n    end = INACTIVE_SERVER_ARRAY.entries + *inactive_count;\n    for (entry=INACTIVE_SERVER_ARRAY.entries; entry<end; entry++) {\n        if (entry >= INACTIVE_SERVER_ARRAY.entries +\n                INACTIVE_SERVER_ARRAY.count)\n        {\n            break;\n        }\n        if (entry->next_time > g_current_time) {\n            continue;\n        }\n\n        result = do_check_brainsplit(entry->cs);\n        if (result == 0) { //success\n            --(*inactive_count);\n        } else if (result == EEXIST) {  //brain-split occurs\n            return result;\n        }\n\n        entry->next_time = g_current_time + 1;\n    }\n\n    return 0;\n}\n\nstatic int master_check()\n{\n    int result;\n    int active_count;\n    int inactive_count;\n\n    PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock);\n    inactive_count = INACTIVE_SERVER_ARRAY.count;\n    PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock);\n    if (inactive_count > 0) {\n        if ((result=cluster_check_brainsplit(&inactive_count)) != 0) {\n            return result;\n        }\n\n        active_count = CLUSTER_SERVER_ARRAY.count - inactive_count;\n        if (!sf_election_quorum_check(MASTER_ELECTION_QUORUM, false,\n                    CLUSTER_SERVER_ARRAY.count, active_count))\n        {\n            logWarning(\"file: \"__FILE__\", line: %d, \"\n                    \"trigger re-select master because alive server \"\n                    \"count: %d < half of total server count: %d ...\",\n                    __LINE__, active_count, CLUSTER_SERVER_ARRAY.count);\n            cluster_relationship_trigger_reselect_master();\n            return EBUSY;\n        }\n    }\n\n    return 0;\n}\n\nstatic int cluster_get_master(FCFSVoteClusterServerStatus *server_status,\n        const bool log_connect_error, int *active_count)\n{\n#define STATUS_ARRAY_FIXED_COUNT  8\n\tFCFSVoteClusterServerInfo *server;\n\tFCFSVoteClusterServerInfo *end;\n\tFCFSVoteClusterServerStatus *current_status;\n\tFCFSVoteClusterServerStatus *cs_status;\n\tFCFSVoteClusterServerStatus status_array[STATUS_ARRAY_FIXED_COUNT];\n    char formatted_ip[FORMATTED_IP_SIZE];\n\tint result;\n\tint r;\n\tint i;\n\n\tmemset(server_status, 0, sizeof(FCFSVoteClusterServerStatus));\n    if (CLUSTER_SERVER_ARRAY.count < STATUS_ARRAY_FIXED_COUNT) {\n        cs_status = status_array;\n    } else {\n        int bytes;\n        bytes = sizeof(FCFSVoteClusterServerStatus) * CLUSTER_SERVER_ARRAY.count;\n        cs_status = (FCFSVoteClusterServerStatus *)fc_malloc(bytes);\n        if (cs_status == NULL) {\n            return ENOMEM;\n        }\n    }\n\n\tcurrent_status = cs_status;\n\tresult = 0;\n\tend = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count;\n\tfor (server=CLUSTER_SERVER_ARRAY.servers; server<end; server++) {\n\t\tcurrent_status->cs = server;\n        r = cluster_get_server_status_ex(current_status, log_connect_error);\n\t\tif (r == 0) {\n\t\t\tcurrent_status++;\n\t\t} else if (r != ENOENT) {\n\t\t\tresult = r;\n\t\t}\n\t}\n\n\t*active_count = current_status - cs_status;\n    if (*active_count == 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"get server status fail, \"\n                \"server count: %d\", __LINE__,\n                CLUSTER_SERVER_ARRAY.count);\n        return result == 0 ? ENOENT : result;\n    }\n\n\tqsort(cs_status, *active_count, sizeof(FCFSVoteClusterServerStatus),\n\t\tcluster_cmp_server_status);\n\n    if (FC_LOG_BY_LEVEL(LOG_DEBUG)) {\n        for (i=0; i<*active_count; i++) {\n            format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                        cs_status[i].cs->server), formatted_ip);\n            logDebug(\"file: \"__FILE__\", line: %d, \"\n                    \"server_id: %d, ip and port %s:%u, is_master: %d\",\n                    __LINE__, cs_status[i].server_id, formatted_ip,\n                    CLUSTER_GROUP_ADDRESS_FIRST_PORT(cs_status[i].cs->server),\n                    cs_status[i].is_master);\n        }\n    }\n\n\tmemcpy(server_status, cs_status + (*active_count - 1),\n\t\t\tsizeof(FCFSVoteClusterServerStatus));\n    if (cs_status != status_array) {\n        free(cs_status);\n    }\n\treturn 0;\n}\n\nstatic int do_notify_master_changed(FCFSVoteClusterServerInfo *cs,\n\t\tFCFSVoteClusterServerInfo *master, const unsigned char cmd,\n        bool *bConnectFail)\n{\n    int connect_timeout;\n    char out_buff[sizeof(FCFSVoteProtoHeader) + 4];\n    ConnectionInfo conn;\n    FCFSVoteProtoHeader *header;\n    SFResponseInfo response;\n    int result;\n\n    connect_timeout = FC_MIN(CLUSTER_CONNECT_TIMEOUT, 2);\n    if ((result=fc_server_make_connection(&CLUSTER_GROUP_ADDRESS_ARRAY(\n                        cs->server), &conn, \"fvote\", connect_timeout)) != 0)\n    {\n        *bConnectFail = true;\n        return result;\n    }\n    *bConnectFail = false;\n\n    header = (FCFSVoteProtoHeader *)out_buff;\n    SF_PROTO_SET_HEADER(header, cmd, sizeof(out_buff) -\n            sizeof(FCFSVoteProtoHeader));\n    int2buff(master->server->id, out_buff + sizeof(FCFSVoteProtoHeader));\n    response.error.length = 0;\n    if ((result=sf_send_and_recv_none_body_response(&conn, out_buff,\n                    sizeof(out_buff), &response, CLUSTER_NETWORK_TIMEOUT,\n                    SF_PROTO_ACK)) != 0)\n    {\n        vote_log_network_error(&response, &conn, result);\n    }\n\n    conn_pool_disconnect_server(&conn);\n    return result;\n}\n\nint cluster_relationship_pre_set_master(FCFSVoteClusterServerInfo *master)\n{\n    FCFSVoteClusterServerInfo *next_master;\n\n    next_master = CLUSTER_NEXT_MASTER;\n    if (next_master == NULL) {\n        CLUSTER_NEXT_MASTER = master;\n    } else if (next_master != master) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"try to set next master id: %d, \"\n                \"but next master: %d already exist\",\n                __LINE__, master->server->id, next_master->server->id);\n        CLUSTER_NEXT_MASTER = NULL;\n        return EEXIST;\n    }\n\n    return 0;\n}\n\nstatic int cluster_relationship_set_master(FCFSVoteClusterServerInfo\n        *new_master, const time_t start_time)\n{\n    FCFSVoteClusterServerInfo *old_master;\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n    old_master = CLUSTER_MASTER_ATOM_PTR;\n    if (new_master == old_master) {\n        format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                    new_master->server), formatted_ip);\n        logDebug(\"file: \"__FILE__\", line: %d, \"\n                \"the server id: %d, %s:%u already is master\",\n                __LINE__, new_master->server->id, formatted_ip,\n                CLUSTER_GROUP_ADDRESS_FIRST_PORT(new_master->server));\n        return 0;\n    }\n\n    if (CLUSTER_MYSELF_PTR == new_master) {\n    } else {\n        char time_used[128];\n        if (start_time > 0) {\n            sprintf(time_used, \", election time used: %ds\",\n                    (int)(g_current_time - start_time));\n        } else {\n            *time_used = '\\0';\n        }\n\n        format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                    new_master->server), formatted_ip);\n        logInfo(\"file: \"__FILE__\", line: %d, \"\n                \"the master server id: %d, %s:%u%s\", __LINE__,\n                new_master->server->id, formatted_ip,\n                CLUSTER_GROUP_ADDRESS_FIRST_PORT(new_master->server),\n                time_used);\n    }\n\n    do {\n        if (__sync_bool_compare_and_swap(&CLUSTER_MASTER_PTR,\n                    old_master, new_master))\n        {\n            break;\n        }\n        old_master = CLUSTER_MASTER_ATOM_PTR;\n    } while (old_master != new_master);\n\n    return 0;\n}\n\nint cluster_relationship_commit_master(FCFSVoteClusterServerInfo *master)\n{\n    const time_t start_time = 0;\n    FCFSVoteClusterServerInfo *next_master;\n    int result;\n\n    next_master = CLUSTER_NEXT_MASTER;\n    if (next_master == NULL) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"next master is NULL\", __LINE__);\n        return EBUSY;\n    }\n    if (next_master != master) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"next master server id: %d != expected server id: %d\",\n                __LINE__, next_master->server->id, master->server->id);\n        CLUSTER_NEXT_MASTER = NULL;\n        return EBUSY;\n    }\n\n    result = cluster_relationship_set_master(master, start_time);\n    CLUSTER_NEXT_MASTER = NULL;\n    return result;\n}\n\nvoid cluster_relationship_trigger_reselect_master()\n{\n    FCFSVoteClusterServerInfo *master;\n\n    master = CLUSTER_MASTER_ATOM_PTR;\n    if (CLUSTER_MYSELF_PTR != master) {\n        return;\n    }\n\n    if (cluster_unset_master(master)) {\n        service_group_htable_clear_tasks();\n    }\n}\n\nstatic int cluster_notify_next_master(FCFSVoteClusterServerInfo *cs,\n        FCFSVoteClusterServerStatus *server_status, bool *bConnectFail)\n{\n    FCFSVoteClusterServerInfo *master;\n    int result;\n\n    master = server_status->cs;\n    if (cs == CLUSTER_MYSELF_PTR) {\n        if ((result=cluster_relationship_pre_set_master(master)) == 0) {\n            init_inactive_server_array();\n        }\n        return result;\n    } else {\n        return do_notify_master_changed(cs, master,\n                FCFS_VOTE_CLUSTER_PROTO_PRE_SET_NEXT_MASTER, bConnectFail);\n    }\n}\n\nstatic int cluster_commit_next_master(FCFSVoteClusterServerInfo *cs,\n        FCFSVoteClusterServerStatus *server_status, bool *bConnectFail)\n{\n    FCFSVoteClusterServerInfo *master;\n\n    master = server_status->cs;\n    if (cs == CLUSTER_MYSELF_PTR) {\n        return cluster_relationship_commit_master(master);\n    } else {\n        return do_notify_master_changed(cs, master,\n                FCFS_VOTE_CLUSTER_PROTO_COMMIT_NEXT_MASTER, bConnectFail);\n    }\n}\n\ntypedef int (*cluster_notify_next_master_func)(FCFSVoteClusterServerInfo *cs,\n        FCFSVoteClusterServerStatus *server_status, bool *bConnectFail);\n\nstatic int notify_next_master(cluster_notify_next_master_func notify_func,\n        FCFSVoteClusterServerStatus *server_status)\n{\n\tFCFSVoteClusterServerInfo *server;\n\tFCFSVoteClusterServerInfo *send;\n\tint result;\n\tbool bConnectFail;\n\tint success_count;\n\n\tresult = ENOENT;\n\tsuccess_count = 0;\n\tsend = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count;\n    for (server=CLUSTER_SERVER_ARRAY.servers; server<send; server++) {\n        if ((result=notify_func(server, server_status, &bConnectFail)) != 0) {\n            if (!bConnectFail) {\n                return result;\n            }\n        } else {\n            success_count++;\n        }\n    }\n\n    if (!sf_election_quorum_check(MASTER_ELECTION_QUORUM, false,\n                CLUSTER_SERVER_ARRAY.count, success_count))\n    {\n        return EAGAIN;\n    }\n\n\treturn 0;\n}\n\nstatic inline int cluster_notify_master_changed(\n        FCFSVoteClusterServerStatus *server_status)\n{\n    int result;\n\n    if ((result=notify_next_master(cluster_notify_next_master,\n                    server_status)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=notify_next_master(cluster_commit_next_master,\n                    server_status)) != 0)\n    {\n        cluster_relationship_trigger_reselect_master();\n    }\n\n    return result;\n}\n\nstatic int cluster_select_master()\n{\n\tint result;\n    int active_count;\n    int i;\n    bool need_log;\n    int max_sleep_secs;\n    int sleep_secs;\n    int remain_time;\n    time_t start_time;\n    time_t last_log_time;\n\tFCFSVoteClusterServerStatus server_status;\n    FCFSVoteClusterServerInfo *next_master;\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n\tlogInfo(\"file: \"__FILE__\", line: %d, \"\n\t\t\"selecting master...\", __LINE__);\n\n    start_time = g_current_time;\n    last_log_time = 0;\n    sleep_secs = 10;\n    max_sleep_secs = 1;\n    i = 0;\n    while (CLUSTER_MASTER_ATOM_PTR == NULL) {\n        if (sleep_secs > 1) {\n            need_log = true;\n            last_log_time = g_current_time;\n        } if (g_current_time - last_log_time > 8) {\n            need_log = ((i + 1) % 10 == 0);\n            if (need_log) {\n                last_log_time = g_current_time;\n            }\n        } else {\n            need_log = false;\n        }\n\n        if ((result=cluster_get_master(&server_status,\n                        need_log, &active_count)) != 0)\n        {\n            return result;\n        }\n\n        ++i;\n        if (!sf_election_quorum_check(MASTER_ELECTION_QUORUM, false,\n                    CLUSTER_SERVER_ARRAY.count, active_count))\n        {\n            sleep_secs = 1;\n            if (need_log) {\n                logWarning(\"file: \"__FILE__\", line: %d, \"\n                        \"round %dth select master fail because alive server \"\n                        \"count: %d < half of total server count: %d, \"\n                        \"try again after %d seconds.\", __LINE__, i,\n                        active_count, CLUSTER_SERVER_ARRAY.count,\n                        sleep_secs);\n            }\n            sleep(sleep_secs);\n            continue;\n        }\n\n        if ((active_count == CLUSTER_SERVER_ARRAY.count) ||\n                (active_count >= 2 && server_status.is_master))\n        {\n            break;\n        }\n\n        remain_time = ELECTION_MAX_WAIT_TIME - (g_current_time - start_time);\n        if (remain_time <= 0) {\n            break;\n        }\n\n        sleep_secs = FC_MIN(remain_time, max_sleep_secs);\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"round %dth select master, alive server count: %d \"\n                \"< server count: %d, try again after %d seconds.\",\n                __LINE__, i, active_count, CLUSTER_SERVER_ARRAY.count,\n                sleep_secs);\n\n        sleep(sleep_secs);\n        if ((i % 2 == 0) && (max_sleep_secs < 8)) {\n            max_sleep_secs *= 2;\n        }\n    }\n\n    next_master = CLUSTER_MASTER_ATOM_PTR;\n    if (next_master != NULL) {\n        format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                    next_master->server), formatted_ip);\n        logInfo(\"file: \"__FILE__\", line: %d, \"\n                \"abort election because the master exists, \"\n                \"master id: %d, %s:%u, election time used: %ds\",\n                __LINE__, next_master->server->id, formatted_ip,\n                CLUSTER_GROUP_ADDRESS_FIRST_PORT(next_master->server),\n                (int)(g_current_time - start_time));\n        return 0;\n    }\n\n    next_master = server_status.cs;\n    if (CLUSTER_MYSELF_PTR == next_master) {\n\t\tif ((result=cluster_notify_master_changed(\n                        &server_status)) != 0)\n        {\n            return result;\n        }\n\n        format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                    next_master->server), formatted_ip);\n\t\tlogInfo(\"file: \"__FILE__\", line: %d, \"\n\t\t\t\"I am the new master, id: %d, %s:%u, election time used: \"\n            \"%ds\", __LINE__, next_master->server->id, formatted_ip,\n            CLUSTER_GROUP_ADDRESS_FIRST_PORT(next_master->server),\n            (int)(g_current_time - start_time));\n    } else {\n        if (server_status.is_master) {\n            cluster_relationship_set_master(next_master, start_time);\n        } else if (CLUSTER_MASTER_ATOM_PTR == NULL) {\n            format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                        next_master->server), formatted_ip);\n            logInfo(\"file: \"__FILE__\", line: %d, \"\n                    \"election time used: %ds, waiting for the candidate \"\n                    \"master server id: %d, %s:%u notify ...\", __LINE__,\n                    (int)(g_current_time - start_time), next_master->server->id,\n                    formatted_ip, CLUSTER_GROUP_ADDRESS_FIRST_PORT(\n                        next_master->server));\n            return ENOENT;\n        }\n    }\n\n\treturn 0;\n}\n\nstatic int cluster_ping_master(FCFSVoteClusterServerInfo *master,\n        ConnectionInfo *conn, const int timeout, bool *is_ping)\n{\n    int connect_timeout;\n    int network_timeout;\n    int result;\n\n    if (CLUSTER_MYSELF_PTR == master) {\n        *is_ping = false;\n        return master_check();\n    }\n\n    network_timeout = FC_MIN(CLUSTER_NETWORK_TIMEOUT, timeout);\n    *is_ping = true;\n    if (conn->sock < 0) {\n        connect_timeout = FC_MIN(CLUSTER_CONNECT_TIMEOUT, timeout);\n        if ((result=fc_server_make_connection(&CLUSTER_GROUP_ADDRESS_ARRAY(\n                            master->server), conn, \"fvote\",\n                        connect_timeout)) != 0)\n        {\n            return result;\n        }\n\n        if ((result=proto_join_master(conn, network_timeout)) != 0) {\n            conn_pool_disconnect_server(conn);\n            return result;\n        }\n    }\n\n    if ((result=proto_ping_master(conn, network_timeout)) != 0) {\n        conn_pool_disconnect_server(conn);\n    }\n\n    return result;\n}\n\nstatic void *cluster_thread_entrance(void* arg)\n{\n#define MAX_SLEEP_SECONDS  10\n\n    int result;\n    int fail_count;\n    int sleep_seconds;\n    int ping_remain_time;\n    time_t ping_start_time;\n    bool is_ping;\n    FCFSVoteClusterServerInfo *master;\n    ConnectionInfo mconn;  //master connection\n    char formatted_ip[FORMATTED_IP_SIZE];\n\n#ifdef OS_LINUX\n    prctl(PR_SET_NAME, \"relationship\");\n#endif\n\n    memset(&mconn, 0, sizeof(mconn));\n    mconn.sock = -1;\n\n    fail_count = 0;\n    sleep_seconds = 1;\n    ping_start_time = g_current_time;\n    while (SF_G_CONTINUE_FLAG) {\n        master = CLUSTER_MASTER_ATOM_PTR;\n        if (master == NULL) {\n            if (cluster_select_master() != 0) {\n                sleep_seconds = 1 + (int)((double)rand()\n                        * (double)MAX_SLEEP_SECONDS / RAND_MAX);\n            } else {\n                if (mconn.sock >= 0) {\n                    conn_pool_disconnect_server(&mconn);\n                }\n                ping_start_time = g_current_time;\n                sleep_seconds = 1;\n            }\n        } else {\n            ping_remain_time = ELECTION_MASTER_LOST_TIMEOUT -\n                (g_current_time - ping_start_time);\n            if (ping_remain_time < 2) {\n                ping_remain_time = 2;\n            }\n            if ((result=cluster_ping_master(master, &mconn,\n                            ping_remain_time, &is_ping)) == 0)\n            {\n                fail_count = 0;\n                ping_start_time = g_current_time;\n                sleep_seconds = 1;\n            } else if (is_ping) {\n                ++fail_count;\n                format_ip_address(CLUSTER_GROUP_ADDRESS_FIRST_IP(\n                            master->server), formatted_ip);\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"%dth ping master id: %d, %s:%u fail\", __LINE__,\n                        fail_count, master->server->id, formatted_ip,\n                        CLUSTER_GROUP_ADDRESS_FIRST_PORT(master->server));\n                if (result == SF_RETRIABLE_ERROR_NOT_MASTER) {\n                    cluster_unset_master(master);\n                    fail_count = 0;\n                    sleep_seconds = 0;\n                } else if (g_current_time - ping_start_time >\n                        ELECTION_MASTER_LOST_TIMEOUT)\n                {\n                    if (fail_count > 1) {\n                        cluster_unset_master(master);\n                        fail_count = 0;\n                    }\n                    sleep_seconds = 0;\n                } else {\n                    sleep_seconds = 1;\n                }\n            } else {\n                sleep_seconds = 1;\n            }\n        }\n\n        if (sleep_seconds > 0) {\n            sleep(sleep_seconds);\n        }\n    }\n\n    return NULL;\n}\n\nint cluster_relationship_init()\n{\n    int result;\n    int bytes;\n    pthread_t tid;\n\n    bytes = sizeof(FCFSVoteClusterServerDetectEntry) * CLUSTER_SERVER_ARRAY.count;\n    INACTIVE_SERVER_ARRAY.entries = (FCFSVoteClusterServerDetectEntry *)\n        fc_malloc(bytes);\n    if (INACTIVE_SERVER_ARRAY.entries == NULL) {\n        return ENOMEM;\n    }\n    if ((result=init_pthread_lock(&INACTIVE_SERVER_ARRAY.lock)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"init_pthread_lock fail, errno: %d, error info: %s\",\n                __LINE__, result, STRERROR(result));\n        return result;\n    }\n    INACTIVE_SERVER_ARRAY.alloc = CLUSTER_SERVER_ARRAY.count;\n\n    return fc_create_thread(&tid, cluster_thread_entrance, NULL,\n            SF_G_THREAD_STACK_SIZE);\n}\n\nint cluster_relationship_destroy()\n{\n\treturn 0;\n}\n\nvoid cluster_relationship_add_to_inactive_sarray(FCFSVoteClusterServerInfo *cs)\n{\n    FCFSVoteClusterServerDetectEntry *entry;\n    FCFSVoteClusterServerDetectEntry *end;\n    bool found;\n\n    found = false;\n    PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock);\n    if (INACTIVE_SERVER_ARRAY.count > 0) {\n        end = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count;\n        for (entry=INACTIVE_SERVER_ARRAY.entries; entry<end; entry++) {\n            if (entry->cs == cs) {\n                found = true;\n                break;\n            }\n        }\n    }\n    if (!found) {\n        if (INACTIVE_SERVER_ARRAY.count < INACTIVE_SERVER_ARRAY.alloc) {\n            entry = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count;\n            SET_SERVER_DETECT_ENTRY(entry, cs);\n            INACTIVE_SERVER_ARRAY.count++;\n        } else {\n            logError(\"file: \"__FILE__\", line: %d, \"\n                    \"server id: %d, add to inactive array fail \"\n                    \"because array is full\", __LINE__, cs->server->id);\n        }\n    } else {\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"server id: %d, already in inactive array!\",\n                __LINE__, cs->server->id);\n    }\n    PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock);\n}\n\nvoid cluster_relationship_remove_from_inactive_sarray(FCFSVoteClusterServerInfo *cs)\n{\n    FCFSVoteClusterServerDetectEntry *entry;\n    FCFSVoteClusterServerDetectEntry *p;\n    FCFSVoteClusterServerDetectEntry *end;\n\n    PTHREAD_MUTEX_LOCK(&INACTIVE_SERVER_ARRAY.lock);\n    end = INACTIVE_SERVER_ARRAY.entries + INACTIVE_SERVER_ARRAY.count;\n    for (entry=INACTIVE_SERVER_ARRAY.entries; entry<end; entry++) {\n        if (entry->cs == cs) {\n            break;\n        }\n    }\n\n    if (entry < end) {\n        for (p=entry+1; p<end; p++) {\n            *(p - 1) = *p;\n        }\n        INACTIVE_SERVER_ARRAY.count--;\n    }\n    PTHREAD_MUTEX_UNLOCK(&INACTIVE_SERVER_ARRAY.lock);\n}\n"
  },
  {
    "path": "src/vote/server/cluster_relationship.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//cluster_relationship.h\n\n#ifndef _CLUSTER_RELATIONSHIP_H_\n#define _CLUSTER_RELATIONSHIP_H_\n\n#include <time.h>\n#include <pthread.h>\n#include \"server_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint cluster_relationship_init();\nint cluster_relationship_destroy();\n\nint cluster_relationship_pre_set_master(FCFSVoteClusterServerInfo *master);\n\nint cluster_relationship_commit_master(FCFSVoteClusterServerInfo *master);\n\nvoid cluster_relationship_trigger_reselect_master();\n\nvoid cluster_relationship_add_to_inactive_sarray(FCFSVoteClusterServerInfo *cs);\n\nvoid cluster_relationship_remove_from_inactive_sarray(FCFSVoteClusterServerInfo *cs);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/server/common_handler.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//common_handler.c\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <sys/stat.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <limits.h>\n#include <fcntl.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"common/vote_proto.h\"\n#include \"server_global.h\"\n#include \"common_handler.h\"\n\n#define LOG_LEVEL_FOR_DEBUG  LOG_DEBUG\n\nstatic int fcfs_vote_get_cmd_log_level(const int cmd)\n{\n    switch (cmd) {\n        case SF_PROTO_ACTIVE_TEST_REQ:\n        case SF_PROTO_ACTIVE_TEST_RESP:\n            return LOG_NOTHING;\n        case SF_SERVICE_PROTO_REPORT_REQ_RECEIPT_REQ:\n            return LOG_DEBUG;\n        default:\n            return LOG_LEVEL_FOR_DEBUG;\n    }\n}\n\nvoid common_handler_init()\n{\n    SFHandlerContext handler_ctx;\n\n    fcfs_vote_proto_init();\n\n    handler_ctx.slow_log = &SLOW_LOG;\n    handler_ctx.callbacks.get_cmd_caption = fcfs_vote_get_cmd_caption;\n    if (FC_LOG_BY_LEVEL(LOG_LEVEL_FOR_DEBUG)) {\n        handler_ctx.callbacks.get_cmd_log_level = fcfs_vote_get_cmd_log_level;\n    } else {\n        handler_ctx.callbacks.get_cmd_log_level = NULL;\n    }\n    sf_proto_set_handler_context(&handler_ctx);\n}\n\nint fcfs_vote_deal_get_master(struct fast_task_info *task,\n        const int group_index)\n{\n    int result;\n    FCFSVoteProtoGetServerResp *resp;\n    FCFSVoteClusterServerInfo *master;\n    const FCAddressInfo *addr;\n\n    if ((result=server_expect_body_length(0)) != 0) {\n        return result;\n    }\n\n    master = (CLUSTER_MASTER_ATOM_PTR != NULL) ?\n        CLUSTER_MASTER_ATOM_PTR : CLUSTER_NEXT_MASTER;\n    if (master == NULL) {\n        RESPONSE.error.length = sprintf(\n                RESPONSE.error.message,\n                \"the master NOT exist\");\n        return SF_RETRIABLE_ERROR_NO_SERVER;\n    }\n\n    resp = (FCFSVoteProtoGetServerResp *)SF_PROTO_SEND_BODY(task);\n    if (group_index == SERVICE_GROUP_INDEX) {\n        addr = fc_server_get_address_by_peer(&SERVICE_GROUP_ADDRESS_ARRAY(\n                    master->server), task->client_ip);\n    } else {\n        addr = fc_server_get_address_by_peer(&CLUSTER_GROUP_ADDRESS_ARRAY(\n                    master->server), task->client_ip);\n    }\n\n    int2buff(master->server->id, resp->server_id);\n    fc_safe_strcpy(resp->ip_addr, addr->conn.ip_addr);\n    short2buff(addr->conn.port, resp->port);\n\n    RESPONSE.header.body_len = sizeof(FCFSVoteProtoGetServerResp);\n    TASK_CTX.common.response_done = true;\n    return 0;\n}\n"
  },
  {
    "path": "src/vote/server/common_handler.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//common_handler.h\n\n#ifndef FCFS_VOTE_COMMON_HANDLER_H\n#define FCFS_VOTE_COMMON_HANDLER_H\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"server_types.h\"\n\n#ifdef __cplusplus\n\nextern \"C\" {\n#endif\n\nvoid common_handler_init();\n\nint fcfs_vote_deal_get_master(struct fast_task_info *task,\n        const int group_index);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/server/fcfs_voted.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <unistd.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <signal.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <pthread.h>\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"fastcommon/process_ctrl.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"sf/sf_global.h\"\n#include \"sf/sf_func.h\"\n#include \"sf/sf_nio.h\"\n#include \"sf/sf_service.h\"\n#include \"sf/sf_util.h\"\n#include \"common/vote_proto.h\"\n#include \"server_global.h\"\n#include \"server_func.h\"\n#include \"service_group_htable.h\"\n#include \"cluster_relationship.h\"\n#include \"common_handler.h\"\n#include \"cluster_handler.h\"\n#include \"service_handler.h\"\n\nstatic int setup_server_env(const char *config_filename);\n\nstatic bool daemon_mode = true;\nstatic const char *config_filename;\nstatic char g_pid_filename[MAX_PATH_SIZE];\n\nstatic int process_cmdline(int argc, char *argv[], bool *continue_flag)\n{\n    char *action;\n    bool stop;\n    int result;\n\n    *continue_flag = false;\n    if (argc < 2) {\n        sf_usage(argv[0]);\n        return 1;\n    }\n\n    config_filename = sf_parse_daemon_mode_and_action(argc, argv,\n            &g_fcfs_vote_global_vars.version, &daemon_mode, &action);\n    if (config_filename == NULL) {\n        return 0;\n    }\n\n    log_init2();\n    //log_set_time_precision(&g_log_context, LOG_TIME_PRECISION_USECOND);\n\n    result = sf_get_base_path_from_conf_file(config_filename);\n    if (result != 0) {\n        log_destroy();\n        return result;\n    }\n\n    fc_get_full_filename(SF_G_BASE_PATH_STR, SF_G_BASE_PATH_LEN,\n            \"voted.pid\", sizeof(\"voted.pid\") - 1, g_pid_filename);\n    stop = false;\n    result = process_action(g_pid_filename, action, &stop);\n    if (result != 0) {\n        if (result == EINVAL) {\n            sf_usage(argv[0]);\n        }\n        log_destroy();\n        return result;\n    }\n\n    if (stop) {\n        log_destroy();\n        return 0;\n    }\n\n    *continue_flag = true;\n    return 0;\n}\n\nint main(int argc, char *argv[])\n{\n    pthread_t schedule_tid;\n    int wait_count;\n    int result;\n\n    result = process_cmdline(argc, argv, (bool *)&SF_G_CONTINUE_FLAG);\n    if (!SF_G_CONTINUE_FLAG) {\n        return result;\n    }\n\n    sf_enable_exit_on_oom();\n    srand(time(NULL));\n    fast_mblock_manager_init();\n\n    //sched_set_delay_params(300, 1024);\n    do {\n        if ((result=setup_server_env(config_filename)) != 0) {\n            break;\n        }\n\n        if ((result=sf_startup_schedule(&schedule_tid)) != 0) {\n            break;\n        }\n\n        if ((result=sf_add_slow_log_schedule(&g_server_global_vars.\n                        slow_log)) != 0)\n        {\n            break;\n        }\n\n        if ((result=sf_socket_server()) != 0) {\n            break;\n        }\n        if ((result=sf_socket_server_ex(&CLUSTER_SF_CTX)) != 0) {\n            break;\n        }\n\n        if ((result=write_to_pid_file(g_pid_filename)) != 0) {\n            break;\n        }\n\n        if ((result=service_group_htable_init()) != 0) {\n            break;\n        }\n\n        if ((result=service_handler_init()) != 0) {\n            break;\n        }\n\n        common_handler_init();\n        //sched_print_all_entries();\n\n        result = sf_service_init_ex(&CLUSTER_SF_CTX, \"cluster\", NULL,\n                NULL, NULL, sf_proto_set_body_length, NULL, cluster_deal_task,\n                cluster_task_finish_cleanup, cluster_recv_timeout_callback,\n                1000, sizeof(FCFSVoteProtoHeader), sizeof(VoteServerTaskArg));\n        if (result != 0) {\n            break;\n        }\n        sf_enable_thread_notify_ex(&CLUSTER_SF_CTX, true);\n        sf_accept_loop_ex(&CLUSTER_SF_CTX, false);\n\n        result = sf_service_init_ex(&SERVICE_SF_CTX, \"service\", NULL, NULL,\n                NULL, sf_proto_set_body_length, NULL, service_deal_task,\n                service_task_finish_cleanup, service_recv_timeout_callback,\n                1000, sizeof(FCFSVoteProtoHeader), sizeof(VoteServerTaskArg));\n        if (result != 0) {\n            break;\n        }\n        sf_enable_thread_notify(true);\n\n        if ((result=cluster_relationship_init()) != 0) {\n            break;\n        }\n    } while (0);\n\n    if (result != 0) {\n        lcrit(\"program exit abnomally\");\n        log_destroy();\n        return result;\n    }\n\n    //sched_print_all_entries();\n    sf_accept_loop();\n\n    if (g_schedule_flag) {\n        pthread_kill(schedule_tid, SIGINT);\n    }\n\n    wait_count = 0;\n    while ((SF_G_ALIVE_THREAD_COUNT != 0) || g_schedule_flag) {\n        fc_sleep_ms(10);\n        if (++wait_count > 1000) {\n            lwarning(\"waiting timeout, exit!\");\n            break;\n        }\n    }\n\n    sf_service_destroy();\n    delete_pid_file(g_pid_filename);\n    logInfo(\"file: \"__FILE__\", line: %d, \"\n            \"program exit normally.\\n\", __LINE__);\n    log_destroy();\n    return 0;\n}\n\nstatic int setup_server_env(const char *config_filename)\n{\n    int result;\n\n    sf_set_current_time();\n    if ((result=server_load_config(config_filename)) != 0) {\n        return result;\n    }\n\n    if (daemon_mode) {\n        daemon_init(false);\n    }\n    umask(0);\n\n    result = sf_setup_signal_handler();\n\n    log_set_cache(true);\n    return result;\n}\n"
  },
  {
    "path": "src/vote/server/server_func.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#include <sys/stat.h>\n#include <limits.h>\n#include \"fastcommon/ini_file_reader.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/local_ip_func.h\"\n#include \"sf/sf_global.h\"\n#include \"sf/sf_configs.h\"\n#include \"sf/sf_service.h\"\n#include \"sf/sf_cluster_cfg.h\"\n#include \"common/vote_proto.h\"\n#include \"server_global.h\"\n#include \"cluster_info.h\"\n#include \"server_func.h\"\n\nstatic void log_cluster_server_config()\n{\n    FastBuffer buffer;\n\n    if (fast_buffer_init1(&buffer, 1024) != 0) {\n        return;\n    }\n    fc_server_to_config_string(&CLUSTER_SERVER_CONFIG, &buffer);\n    log_it1(LOG_INFO, buffer.data, buffer.length);\n    fast_buffer_destroy(&buffer);\n\n    fc_server_to_log(&CLUSTER_SERVER_CONFIG);\n}\n\nstatic void server_log_configs()\n{\n    char sz_server_config[512];\n    char sz_global_config[512];\n    char sz_slowlog_config[256];\n    char sz_service_config[256];\n\n    sf_global_config_to_string(sz_global_config, sizeof(sz_global_config));\n    sf_slow_log_config_to_string(&SLOW_LOG_CFG, \"slow-log\",\n            sz_slowlog_config, sizeof(sz_slowlog_config));\n    sf_context_config_to_string(&SERVICE_SF_CTX,\n            sz_service_config, sizeof(sz_service_config));\n\n    snprintf(sz_server_config, sizeof(sz_server_config),\n            \"master-election {quorum: %s, master_lost_timeout: %ds, \"\n            \"max_wait_time: %ds}\", sf_get_election_quorum_caption(\n                MASTER_ELECTION_QUORUM), ELECTION_MASTER_LOST_TIMEOUT,\n            ELECTION_MAX_WAIT_TIME);\n\n    logInfo(\"FCFSVote V%d.%d.%d, %s, %s, service: {%s}, %s\",\n            g_fcfs_vote_global_vars.version.major,\n            g_fcfs_vote_global_vars.version.minor,\n            g_fcfs_vote_global_vars.version.patch,\n            sz_global_config, sz_slowlog_config,\n            sz_service_config, sz_server_config);\n\n    log_local_host_ip_addrs();\n    log_cluster_server_config();\n}\n\nstatic int load_master_election_config(const char *cluster_filename)\n{\n    IniContext ini_context;\n    IniFullContext ini_ctx;\n    int result;\n\n    if ((result=iniLoadFromFile(cluster_filename, &ini_context)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                __LINE__, cluster_filename, result);\n        return result;\n    }\n\n    FAST_INI_SET_FULL_CTX_EX(ini_ctx, cluster_filename,\n            \"master-election\", &ini_context);\n    ELECTION_MASTER_LOST_TIMEOUT = iniGetIntCorrectValue(\n            &ini_ctx, \"master_lost_timeout\", 3, 1, 30);\n    ELECTION_MAX_WAIT_TIME = iniGetIntCorrectValue(\n            &ini_ctx, \"max_wait_time\", 5, 1, 300);\n    if ((result=sf_load_election_quorum_config(&MASTER_ELECTION_QUORUM,\n                    &ini_ctx)) != 0)\n    {\n        return result;\n    }\n\n    iniFreeContext(&ini_context);\n    return 0;\n}\n\nstatic int load_cluster_config(IniFullContext *ini_ctx,\n        char *full_cluster_filename)\n{\n    int result;\n\n    if ((result=sf_load_cluster_config_ex(&CLUSTER_CONFIG,\n                    ini_ctx, FCFS_VOTE_DEFAULT_CLUSTER_PORT,\n                    full_cluster_filename, PATH_MAX)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=load_master_election_config(full_cluster_filename)) != 0) {\n        return result;\n    }\n\n    if ((result=cluster_info_init(full_cluster_filename)) != 0) {\n        return result;\n    }\n\n    sf_set_address_family_by_ip(&SERVICE_SF_CTX, &SERVICE_GROUP_ADDRESS_ARRAY(\n                CLUSTER_MYSELF_PTR->server));\n    sf_set_address_family_by_ip(&CLUSTER_SF_CTX, &CLUSTER_GROUP_ADDRESS_ARRAY(\n                CLUSTER_MYSELF_PTR->server));\n    return 0;\n}\n\nint server_load_config(const char *filename)\n{\n    const int fixed_buffer_size = 0;\n    const int task_buffer_extra_size = 0;\n    IniContext ini_context;\n    IniFullContext ini_ctx;\n    char full_cluster_filename[PATH_MAX];\n    int result;\n\n    if ((result=iniLoadFromFile(filename, &ini_context)) != 0) {\n        logError(\"file: \"__FILE__\", line: %d, \"\n                \"load conf file \\\"%s\\\" fail, ret code: %d\",\n                __LINE__, filename, result);\n        return result;\n    }\n\n    if ((result=sf_load_config(\"fcfs_voted\", fc_comm_type_sock,\n                    filename, &ini_context, \"service\",\n                    FCFS_VOTE_DEFAULT_SERVICE_PORT,\n                    FCFS_VOTE_DEFAULT_SERVICE_PORT,\n                    fixed_buffer_size,\n                    task_buffer_extra_size)) != 0)\n    {\n        return result;\n    }\n    if ((result=sf_load_context_from_config(&CLUSTER_SF_CTX,\n                    fc_comm_type_sock, filename,\n                    &ini_context, \"cluster\",\n                    FCFS_VOTE_DEFAULT_CLUSTER_PORT,\n                    FCFS_VOTE_DEFAULT_CLUSTER_PORT,\n                    fixed_buffer_size,\n                    task_buffer_extra_size)) != 0)\n    {\n        return result;\n    }\n\n    FAST_INI_SET_FULL_CTX_EX(ini_ctx, filename, NULL, &ini_context);\n    if ((result=load_cluster_config(&ini_ctx,\n                    full_cluster_filename)) != 0)\n    {\n        return result;\n    }\n\n    if ((result=sf_load_slow_log_config(filename, &ini_context,\n                    &SLOW_LOG_CTX, &SLOW_LOG_CFG)) != 0)\n    {\n        return result;\n    }\n\n    iniFreeContext(&ini_context);\n\n    load_local_host_ip_addrs();\n    server_log_configs();\n\n    return 0;\n}\n"
  },
  {
    "path": "src/vote/server/server_func.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _FCFS_VOTE_SERVER_FUNC_H\n#define _FCFS_VOTE_SERVER_FUNC_H\n\n#include \"server_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint server_load_config(const char *filename);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/server/server_global.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"server_global.h\"\n\nVoteServerGlobalVars g_server_global_vars;\n"
  },
  {
    "path": "src/vote/server/server_global.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _SERVER_GLOBAL_H\n#define _SERVER_GLOBAL_H\n\n#include \"fastcommon/common_define.h\"\n#include \"sf/sf_global.h\"\n#include \"../common/vote_global.h\"\n#include \"server_types.h\"\n\ntypedef struct server_global_vars {\n    struct {\n        FCFSVoteClusterServerInfo *master;\n        FCFSVoteClusterServerInfo *myself;\n        FCFSVoteClusterServerInfo *next_master;\n        SFClusterConfig config;\n        FCFSVoteClusterServerArray server_array;\n\n        struct {\n            SFElectionQuorum quorum;\n            bool vote_node_enabled;\n            int master_lost_timeout;\n            int max_wait_time;\n        } master_election;\n\n        SFContext sf_context;  //for cluster communication\n    } cluster;\n\n    SFSlowLogContext slow_log;\n} VoteServerGlobalVars;\n\n#define MASTER_ELECTION_QUORUM g_server_global_vars.cluster. \\\n    master_election.quorum\n#define ELECTION_MASTER_LOST_TIMEOUT g_server_global_vars.cluster. \\\n    master_election.master_lost_timeout\n#define ELECTION_MAX_WAIT_TIME   g_server_global_vars.cluster. \\\n    master_election.max_wait_time\n\n#define CLUSTER_CONFIG          g_server_global_vars.cluster.config\n#define CLUSTER_SERVER_CONFIG   CLUSTER_CONFIG.server_cfg\n\n#define CLUSTER_NEXT_MASTER     g_server_global_vars.cluster.next_master\n#define CLUSTER_MYSELF_PTR      g_server_global_vars.cluster.myself\n#define CLUSTER_MASTER_PTR      g_server_global_vars.cluster.master\n#define CLUSTER_MASTER_ATOM_PTR ((FCFSVoteClusterServerInfo *)  \\\n        __sync_add_and_fetch(&CLUSTER_MASTER_PTR, 0))\n#define MYSELF_IS_MASTER        (CLUSTER_MASTER_ATOM_PTR == CLUSTER_MYSELF_PTR)\n\n#define CLUSTER_SERVER_ARRAY    g_server_global_vars.cluster.server_array\n#define CLUSTER_MY_SERVER_ID    CLUSTER_MYSELF_PTR->server->id\n\n#define SERVICE_SF_CTX          g_sf_context\n#define CLUSTER_SF_CTX          g_server_global_vars.cluster.sf_context\n\n#define CLUSTER_CONNECT_TIMEOUT   CLUSTER_SF_CTX.net_buffer_cfg.connect_timeout\n#define CLUSTER_NETWORK_TIMEOUT   CLUSTER_SF_CTX.net_buffer_cfg.network_timeout\n\n#define SLOW_LOG                g_server_global_vars.slow_log\n#define SLOW_LOG_CFG            SLOW_LOG.cfg\n#define SLOW_LOG_CTX            SLOW_LOG.ctx\n\n#define CLUSTER_GROUP_INDEX     g_server_global_vars.cluster.config.cluster_group_index\n#define SERVICE_GROUP_INDEX     g_server_global_vars.cluster.config.service_group_index\n\n#define CLUSTER_GROUP_ADDRESS_ARRAY(server) \\\n    (server)->group_addrs[CLUSTER_GROUP_INDEX].address_array\n#define SERVICE_GROUP_ADDRESS_ARRAY(server) \\\n    (server)->group_addrs[SERVICE_GROUP_INDEX].address_array\n\n#define CLUSTER_GROUP_ADDRESS_FIRST_PTR(server) \\\n    (*(server)->group_addrs[CLUSTER_GROUP_INDEX].address_array.addrs)\n#define SERVICE_GROUP_ADDRESS_FIRST_PTR(server) \\\n    (*(server)->group_addrs[SERVICE_GROUP_INDEX].address_array.addrs)\n\n#define CLUSTER_GROUP_ADDRESS_FIRST_IP(server) \\\n    CLUSTER_GROUP_ADDRESS_FIRST_PTR(server)->conn.ip_addr\n#define CLUSTER_GROUP_ADDRESS_FIRST_PORT(server) \\\n    CLUSTER_GROUP_ADDRESS_FIRST_PTR(server)->conn.port\n\n#define SERVICE_GROUP_ADDRESS_FIRST_IP(server) \\\n    SERVICE_GROUP_ADDRESS_FIRST_PTR(server)->conn.ip_addr\n#define SERVICE_GROUP_ADDRESS_FIRST_PORT(server) \\\n    SERVICE_GROUP_ADDRESS_FIRST_PTR(server)->conn.port\n\n#define CLUSTER_CONFIG_SIGN_BUF g_server_global_vars.cluster.config.md5_digest\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n    extern VoteServerGlobalVars g_server_global_vars;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/server/server_types.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n\n#ifndef _SERVER_TYPES_H\n#define _SERVER_TYPES_H\n\n#include \"fastcommon/uniq_skiplist.h\"\n#include \"fastcommon/fc_queue.h\"\n#include \"fastcommon/fast_mblock.h\"\n#include \"fastcommon/locked_list.h\"\n#include \"sf/sf_types.h\"\n#include \"common/vote_types.h\"\n\n#define TASK_STATUS_CONTINUE           12345\n#define TASK_ARG           ((VoteServerTaskArg *)task->arg)\n#define TASK_CTX           TASK_ARG->context\n#define REQUEST            TASK_CTX.common.request\n#define RESPONSE           TASK_CTX.common.response\n#define RESPONSE_STATUS    RESPONSE.header.status\n#define REQUEST_STATUS     REQUEST.header.status\n#define SERVER_TASK_TYPE   TASK_CTX.task_type\n#define CLUSTER_PEER       TASK_CTX.shared.cluster.peer\n#define SERVICE_PEER       TASK_CTX.shared.service.peer\n\n#define VOTE_SERVER_TASK_TYPE_NONE         0\n#define VOTE_SERVER_TASK_TYPE_VOTE_NODE    1\n#define VOTE_SERVER_TASK_TYPE_RELATIONSHIP 2\n\n#define SERVER_CTX        ((VoteServerContext *)task->thread_data->arg)\n\ntypedef struct fcfs_vote_cluster_server_info {\n    FCServerInfo *server;\n    volatile bool is_online;\n} FCFSVoteClusterServerInfo;\n\ntypedef struct fcfs_vote_cluster_server_array {\n    FCFSVoteClusterServerInfo *servers;\n    int count;\n} FCFSVoteClusterServerArray;\n\ntypedef struct fcfs_vote_service_peer_info {\n    struct fcfs_vote_service_group_info *group;\n    int server_id;\n    bool persistent;\n} FCFSVoteServicePeerInfo;\n\ntypedef struct server_task_arg {\n    struct {\n        SFCommonTaskContext common;\n        int task_type;\n        union {\n            struct {\n                FCFSVoteServicePeerInfo peer;\n            } service;\n\n            union {\n                FCFSVoteClusterServerInfo *peer;   //the peer server in the cluster\n            } cluster;\n        } shared;\n\n    } context;\n} VoteServerTaskArg;\n\n#endif\n"
  },
  {
    "path": "src/vote/server/service_group_htable.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/fc_atomic.h\"\n#include \"sf/sf_nio.h\"\n#include \"server_global.h\"\n#include \"service_group_htable.h\"\n\ntypedef struct fcfs_vote_shared_lock_array {\n    pthread_mutex_t *locks;\n    int count;\n} FCFSVoteSharedLockArray;\n\ntypedef struct fcfs_vote_service_group_htable {\n    FCFSVoteServiceGroupInfo **buckets;\n    int capacity;\n} FCFSVoteServiceGroupHtable;\n\ntypedef struct fcfs_vote_service_group_context {\n    struct fast_mblock_man allocator;  //element: FCFSVoteServiceGroupInfo\n    FCFSVoteSharedLockArray lock_array;\n    FCFSVoteServiceGroupHtable htable;\n} FCFSVoteServiceGroupContext;\n\nstatic FCFSVoteServiceGroupContext service_group_ctx;\n\nint service_group_htable_init()\n{\n    int result;\n    int bytes;\n    pthread_mutex_t *lock;\n    pthread_mutex_t *end;\n\n    if ((result=fast_mblock_init_ex1(&service_group_ctx.allocator,\n                    \"service_group\", sizeof(FCFSVoteServiceGroupInfo),\n                    4 * 1024, 0, NULL, NULL, true)) != 0)\n    {\n        return result;\n    }\n\n    service_group_ctx.lock_array.count = 17;\n    bytes = sizeof(pthread_mutex_t) * service_group_ctx.lock_array.count;\n    service_group_ctx.lock_array.locks = fc_malloc(bytes);\n    if (service_group_ctx.lock_array.locks == NULL) {\n        return ENOMEM;\n    }\n\n    end = service_group_ctx.lock_array.locks +\n        service_group_ctx.lock_array.count;\n    for (lock=service_group_ctx.lock_array.locks; lock<end; lock++) {\n        if ((result=init_pthread_lock(lock)) != 0) {\n            return result;\n        }\n    }\n\n    service_group_ctx.htable.capacity = 1361;\n    bytes = sizeof(FCFSVoteServiceGroupInfo *) *\n        service_group_ctx.htable.capacity;\n    service_group_ctx.htable.buckets = fc_malloc(bytes);\n    if (service_group_ctx.htable.buckets == NULL) {\n        return ENOMEM;\n    }\n    memset(service_group_ctx.htable.buckets, 0, bytes);\n\n    return 0;\n}\n\nint service_group_htable_get(const short service_id, const int group_id,\n        const int leader_id, const short response_size,\n        struct fast_task_info *task, FCFSVoteServiceGroupInfo **group)\n{\n    uint64_t hash_code;\n    int bucket_index;\n    int old_leader_id;\n    int result;\n    pthread_mutex_t *lock;\n    FCFSVoteServiceGroupInfo **bucket;\n    FCFSVoteServiceGroupInfo *current;\n\n    hash_code = ((int64_t)service_id << 32) | (int64_t)group_id;\n    bucket_index = hash_code % service_group_ctx.htable.capacity;\n    lock = service_group_ctx.lock_array.locks + bucket_index %\n        service_group_ctx.lock_array.count;\n    bucket = service_group_ctx.htable.buckets + bucket_index;\n\n    PTHREAD_MUTEX_LOCK(lock);\n    current = *bucket;\n    while (current != NULL) {\n        if (hash_code == current->hash_code) {\n            break;\n        }\n        current = current->next;\n    }\n\n    if (current == NULL) {\n        current = fast_mblock_alloc_object(&service_group_ctx.allocator);\n        if (current != NULL) {\n            current->hash_code = hash_code;\n            current->service_id = service_id;\n            current->response_size = response_size;\n            current->group_id = group_id;\n            current->leader_id = leader_id;\n            current->task = (leader_id > 0 ? task : NULL);\n            current->next = *bucket;\n            *bucket = current;\n            result = 0;\n        } else {\n            result = ENOMEM;\n        }\n    } else {\n        if (leader_id > 0) {\n            old_leader_id = FC_ATOMIC_GET(current->leader_id);\n            if (old_leader_id == 0) {\n                if (__sync_bool_compare_and_swap(&current->leader_id,\n                            old_leader_id, leader_id))\n                {\n                    result = 0;\n                } else {\n                    result = SF_CLUSTER_ERROR_LEADER_INCONSISTENT;\n                }\n            } else if (old_leader_id == leader_id) {\n                result = 0;\n            } else {\n                result = SF_CLUSTER_ERROR_LEADER_INCONSISTENT;\n            }\n\n            if (result == 0 && task != NULL && task != current->task) {\n                if (current->task != NULL) {\n                    logWarning(\"file: \"__FILE__\", line: %d, \"\n                            \"network task already exist, clean it!\",\n                            __LINE__);\n                    sf_nio_notify(current->task, SF_NIO_STAGE_CLOSE);\n                }\n                current->task = task;\n            }\n        } else {\n            result = 0;\n        }\n    }\n\n    PTHREAD_MUTEX_UNLOCK(lock);\n\n    *group = current;\n    return result;\n}\n\nvoid service_group_htable_unset_task(FCFSVoteServiceGroupInfo *group)\n{\n    int bucket_index;\n    pthread_mutex_t *lock;\n\n    bucket_index = group->hash_code % service_group_ctx.htable.capacity;\n    lock = service_group_ctx.lock_array.locks + bucket_index %\n        service_group_ctx.lock_array.count;\n    PTHREAD_MUTEX_LOCK(lock);\n    group->task = NULL;\n    PTHREAD_MUTEX_UNLOCK(lock);\n}\n\nvoid service_group_htable_clear_tasks()\n{\n    FCFSVoteServiceGroupInfo **bucket;\n    FCFSVoteServiceGroupInfo **end;\n    FCFSVoteServiceGroupInfo *current;\n    int bucket_index;\n    int group_count;\n    int clear_count;\n    pthread_mutex_t *lock;\n\n    group_count = clear_count = 0;\n    end = service_group_ctx.htable.buckets +\n        service_group_ctx.htable.capacity;\n    for (bucket=service_group_ctx.htable.buckets; bucket<end; bucket++) {\n        bucket_index = bucket - service_group_ctx.htable.buckets;\n        lock = service_group_ctx.lock_array.locks + bucket_index %\n            service_group_ctx.lock_array.count;\n        PTHREAD_MUTEX_LOCK(lock);\n        if (*bucket != NULL) {\n            current = *bucket;\n            do {\n                ++group_count;\n                if (current->task != NULL) {\n                    ++clear_count;\n                    sf_nio_notify(current->task, SF_NIO_STAGE_CLOSE);\n                }\n            } while ((current=current->next) != NULL);\n        }\n        PTHREAD_MUTEX_UNLOCK(lock);\n    }\n\n    logInfo(\"file: \"__FILE__\", line: %d, \"\n            \"service group count: %d, clear count: %d\",\n            __LINE__, group_count, clear_count);\n}\n"
  },
  {
    "path": "src/vote/server/service_group_htable.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//service_group_htable.h\n\n#ifndef _SERVICE_GROUP_HTABLE_H_\n#define _SERVICE_GROUP_HTABLE_H_\n\n#include <time.h>\n#include \"server_global.h\"\n\ntypedef struct fcfs_vote_service_group_info {\n    uint64_t hash_code;\n    short service_id;\n    short response_size;\n    int group_id;\n    volatile int next_leader;\n    volatile int leader_id;\n    struct fast_task_info *task;\n    struct fcfs_vote_service_group_info *next;  //for htable\n} FCFSVoteServiceGroupInfo;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint service_group_htable_init();\n\nint service_group_htable_get(const short service_id, const int group_id,\n        const int leader_id, const short response_size,\n        struct fast_task_info *task, FCFSVoteServiceGroupInfo **group);\n\nvoid service_group_htable_unset_task(FCFSVoteServiceGroupInfo *group);\n\nvoid service_group_htable_clear_tasks();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/vote/server/service_handler.c",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//service_handler.c\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <sys/stat.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <limits.h>\n#include <fcntl.h>\n#include \"fastcommon/logger.h\"\n#include \"fastcommon/sockopt.h\"\n#include \"fastcommon/shared_func.h\"\n#include \"fastcommon/pthread_func.h\"\n#include \"fastcommon/sched_thread.h\"\n#include \"fastcommon/ioevent_loop.h\"\n#include \"sf/sf_util.h\"\n#include \"sf/sf_func.h\"\n#include \"sf/sf_nio.h\"\n#include \"sf/sf_service.h\"\n#include \"sf/sf_global.h\"\n#include \"common/vote_proto.h\"\n#include \"server_global.h\"\n#include \"server_func.h\"\n#include \"service_group_htable.h\"\n#include \"common_handler.h\"\n#include \"service_handler.h\"\n\nint service_handler_init()\n{\n    return 0;\n}\n\nint service_handler_destroy()\n{\n    return 0;\n}\n\nint service_recv_timeout_callback(struct fast_task_info *task)\n{\n    if (SERVER_TASK_TYPE == VOTE_SERVER_TASK_TYPE_VOTE_NODE &&\n            SERVICE_PEER.group != NULL)\n    {\n        logWarning(\"file: \"__FILE__\", line: %d, \"\n                \"client ip: %s, service: %s, group_id: %d, server id: %d, \"\n                \"recv timeout\", __LINE__, task->client_ip,\n                fcfs_vote_get_service_name(SERVICE_PEER.group->service_id),\n                SERVICE_PEER.group->group_id, SERVICE_PEER.server_id);\n        return ETIMEDOUT;\n    }\n\n    return 0;\n}\n\nvoid service_task_finish_cleanup(struct fast_task_info *task)\n{\n    if (SERVER_TASK_TYPE == VOTE_SERVER_TASK_TYPE_VOTE_NODE) {\n        if (__sync_bool_compare_and_swap(&SERVICE_PEER.group->\n                    leader_id, SERVICE_PEER.server_id, 0))\n        {\n            /*\n            logInfo(\"service: %s, group_id: %d, server id: %d, \"\n                    \"persistent: %d, next_leader: %d, offline\",\n                    fcfs_vote_get_service_name(SERVICE_PEER.group->service_id),\n                    SERVICE_PEER.group->group_id, SERVICE_PEER.server_id,\n                    SERVICE_PEER.persistent,\n                    FC_ATOMIC_GET(SERVICE_PEER.group->next_leader));\n                    */\n\n            if (SERVICE_PEER.persistent) {\n                service_group_htable_unset_task(SERVICE_PEER.group);\n                SERVICE_PEER.persistent = false;\n\n            }\n        }\n\n        SERVICE_PEER.server_id = 0;\n        SERVICE_PEER.group = NULL;\n        SERVER_TASK_TYPE = VOTE_SERVER_TASK_TYPE_NONE;\n    }\n\n    sf_task_finish_clean_up(task);\n}\n\nstatic int service_deal_cluster_stat(struct fast_task_info *task)\n{\n    int result;\n    FCFSVoteProtoClusterStatRespBodyHeader *body_header;\n    FCFSVoteProtoClusterStatRespBodyPart *body_part;\n    FCFSVoteClusterServerInfo *cs;\n    FCFSVoteClusterServerInfo *send;\n\n    if ((result=server_expect_body_length(0)) != 0) {\n        return result;\n    }\n\n    body_header = (FCFSVoteProtoClusterStatRespBodyHeader *)\n        SF_PROTO_SEND_BODY(task);\n    body_part = (FCFSVoteProtoClusterStatRespBodyPart *)(body_header + 1);\n    int2buff(CLUSTER_SERVER_ARRAY.count, body_header->count);\n\n    send = CLUSTER_SERVER_ARRAY.servers + CLUSTER_SERVER_ARRAY.count;\n    for (cs=CLUSTER_SERVER_ARRAY.servers; cs<send; cs++, body_part++) {\n        int2buff(cs->server->id, body_part->server_id);\n        body_part->is_online = ((cs == CLUSTER_MASTER_ATOM_PTR ||\n                    cs->is_online) ? 1 : 0);\n        body_part->is_master = (cs == CLUSTER_MASTER_ATOM_PTR ? 1 : 0);\n        fc_safe_strcpy(body_part->ip_addr, SERVICE_GROUP_ADDRESS_FIRST_IP(\n                    cs->server));\n        short2buff(SERVICE_GROUP_ADDRESS_FIRST_PORT(cs->server),\n                body_part->port);\n    }\n\n    RESPONSE.header.body_len = (char *)body_part - SF_PROTO_SEND_BODY(task);\n    RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_RESP;\n    TASK_CTX.common.response_done = true;\n    return 0;\n}\n\nstatic int service_check_config_sign(struct fast_task_info *task,\n        const int service_id, const int server_id,\n        const char *config_sign)\n{\n    if (memcmp(config_sign, CLUSTER_CONFIG_SIGN_BUF,\n                SF_CLUSTER_CONFIG_SIGN_LEN) != 0)\n    {\n        char peer_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1];\n        char my_hex[2 * SF_CLUSTER_CONFIG_SIGN_LEN + 1];\n\n        bin2hex(config_sign, SF_CLUSTER_CONFIG_SIGN_LEN, peer_hex);\n        bin2hex((const char *)CLUSTER_CONFIG_SIGN_BUF,\n                SF_CLUSTER_CONFIG_SIGN_LEN, my_hex);\n\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"service: %s, server_id: %d, client 's \"\n                \"cluster config md5: %s != mine: %s\",\n                fcfs_vote_get_service_name(service_id),\n                server_id, peer_hex, my_hex);\n        return EFAULT;\n    }\n\n    return 0;\n}\n\nstatic int service_deal_client_join(struct fast_task_info *task)\n{\n    int result;\n    int server_id;\n    int service_id;\n    int group_id;\n    int response_size;\n    FCFSVoteProtoClientJoinReq *req;\n    FCFSVoteServiceGroupInfo *group;\n\n    if ((result=server_expect_body_length(sizeof(\n                        FCFSVoteProtoClientJoinReq))) != 0)\n    {\n        return result;\n    }\n\n    req = (FCFSVoteProtoClientJoinReq *)REQUEST.body;\n    server_id = buff2int(req->server_id);\n    group_id = buff2int(req->group_id);\n    response_size = buff2short(req->response_size);\n    service_id = req->service_id;\n    switch (service_id) {\n        case FCFS_VOTE_SERVICE_ID_FAUTH:\n        case FCFS_VOTE_SERVICE_ID_FDIR:\n        case FCFS_VOTE_SERVICE_ID_FSTORE:\n            break;\n        default:\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"server_id: %d, unkown service id: %d\",\n                    server_id, service_id);\n            return -EINVAL;\n    }\n\n    if (server_id <= 0) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"service: %s, invalid server_id: %d\",\n                fcfs_vote_get_service_name(service_id),\n                server_id);\n        return -EINVAL;\n    }\n    if (response_size <= 0 || response_size > (task->send.ptr->size -\n                sizeof(FCFSVoteProtoHeader)))\n    {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"service: %s, server_id: %d, invalid response_size: %d\",\n                fcfs_vote_get_service_name(service_id),\n                server_id, response_size);\n        return -EINVAL;\n    }\n\n    if (req->persistent && !req->is_leader) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"service: %s, server_id: %d, unexpected is_leader: %d\",\n                fcfs_vote_get_service_name(service_id),\n                server_id, req->is_leader);\n        return -EINVAL;\n    }\n\n    if ((result=service_check_config_sign(task, service_id,\n                    server_id, req->config_sign)) != 0)\n    {\n        return result;\n    }\n\n    if (SERVICE_PEER.group != NULL) {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"service: %s, server_id: %d already joined\",\n                fcfs_vote_get_service_name(service_id), server_id);\n        return EEXIST;\n    }\n\n    /*\n    logInfo(\"service: %s, group_id: %d, server_id: %d, is_leader: %d, \"\n            \"persistent: %d\", fcfs_vote_get_service_name(service_id),\n            group_id, server_id, req->is_leader, req->persistent);\n            */\n\n    result = service_group_htable_get(service_id, group_id,\n            (req->persistent && req->is_leader ? server_id : 0),\n            response_size, (req->persistent ? task : NULL), &group);\n    if (result != 0) {\n        if (result == SF_CLUSTER_ERROR_LEADER_INCONSISTENT) {\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"service: %s, group_id: %d, target server_id: %d, \"\n                    \"current leader id: %d, leader inconsistent\",\n                    fcfs_vote_get_service_name(service_id),\n                    group_id, server_id, group->leader_id);\n        }\n        return result;\n    }\n\n    SERVICE_PEER.server_id = server_id;\n    SERVICE_PEER.group = group;\n    SERVICE_PEER.persistent = (req->persistent ? true : false);\n    SERVER_TASK_TYPE = VOTE_SERVER_TASK_TYPE_VOTE_NODE;\n    RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_RESP;\n    return 0;\n}\n\nstatic inline int service_check_login(struct fast_task_info *task)\n{\n    if (!(SERVER_TASK_TYPE == VOTE_SERVER_TASK_TYPE_VOTE_NODE &&\n                SERVICE_PEER.group != NULL))\n    {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"please login first\");\n        return EPERM;\n    }\n\n    return 0;\n}\n\nstatic int service_deal_get_vote(struct fast_task_info *task)\n{\n    int result;\n\n    if ((result=server_expect_body_length(sizeof(\n                        SFProtoGetServerStatusReq))) != 0)\n    {\n        return result;\n    }\n\n    if ((result=service_check_login(task)) != 0) {\n        return result;\n    }\n\n    memset(SF_PROTO_SEND_BODY(task), 0, SERVICE_PEER.group->response_size);\n    RESPONSE.header.body_len = SERVICE_PEER.group->response_size;\n    RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_GET_VOTE_RESP;\n    TASK_CTX.common.response_done = true;\n    return 0;\n}\n\nstatic int service_deal_active_check(struct fast_task_info *task)\n{\n    int result;\n\n    if ((result=server_expect_body_length(0)) != 0) {\n        return result;\n    }\n\n    if ((result=service_check_login(task)) != 0) {\n        return result;\n    }\n\n    if (FC_ATOMIC_GET(SERVICE_PEER.group->leader_id) !=\n            SERVICE_PEER.server_id)\n    {\n        RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                \"service: %s, leader id: %d != peer server id: %d\",\n                fcfs_vote_get_service_name(SERVICE_PEER.group->service_id),\n                FC_ATOMIC_GET(SERVICE_PEER.group->leader_id),\n                SERVICE_PEER.server_id);\n        return SF_CLUSTER_ERROR_LEADER_INCONSISTENT;\n    }\n\n    RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_RESP;\n    return 0;\n}\n\nstatic int service_deal_next_leader(struct fast_task_info *task)\n{\n    int result;\n    int next_leader;\n    int old_leader_id;\n    int leader_id;\n\n    if ((result=server_expect_body_length(0)) != 0) {\n        return result;\n    }\n\n    if ((result=service_check_login(task)) != 0) {\n        return result;\n    }\n\n    leader_id = SERVICE_PEER.server_id;\n    old_leader_id = FC_ATOMIC_GET(SERVICE_PEER.group->leader_id);\n    if (old_leader_id != 0) {\n        if (old_leader_id == leader_id) {\n            return 0;\n        } else {\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"service: %s, leader id: %d != peer server id: %d\",\n                    fcfs_vote_get_service_name(SERVICE_PEER.group->\n                        service_id), old_leader_id, leader_id);\n            return SF_CLUSTER_ERROR_LEADER_INCONSISTENT;\n        }\n    }\n\n    next_leader = FC_ATOMIC_GET(SERVICE_PEER.group->next_leader);\n    if (REQUEST.header.cmd == FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER) {\n        if (next_leader == 0) {\n            if (__sync_bool_compare_and_swap(&SERVICE_PEER.group->\n                        next_leader, 0, leader_id))\n            {\n                return 0;\n            }\n        } else if (next_leader == leader_id) {\n            return 0;\n        } else {\n            __sync_bool_compare_and_swap(&SERVICE_PEER.group->\n                    next_leader, next_leader, 0);\n        }\n    } else {  //commit leader\n        __sync_bool_compare_and_swap(&SERVICE_PEER.group->\n                next_leader, next_leader, 0);\n        if (next_leader != leader_id) {\n            //error\n        } else if (__sync_bool_compare_and_swap(&SERVICE_PEER.\n                    group->leader_id, 0, leader_id))\n        {\n            return 0;\n        } else if (FC_ATOMIC_GET(SERVICE_PEER.group->\n                    leader_id) == leader_id)\n        {\n            return 0;\n        }\n    }\n\n    RESPONSE.error.length = sprintf(RESPONSE.error.message,\n            \"service: %s, next leader id: %d != peer server id: %d\",\n            fcfs_vote_get_service_name(SERVICE_PEER.group->\n                service_id), next_leader, leader_id);\n    return SF_CLUSTER_ERROR_LEADER_INCONSISTENT;\n}\n\nstatic int service_process(struct fast_task_info *task)\n{\n    int result;\n\n    if (!MYSELF_IS_MASTER) {\n        if (!(REQUEST.header.cmd == FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ ||\n                    REQUEST.header.cmd == SF_SERVICE_PROTO_GET_LEADER_REQ))\n        {\n            RESPONSE.error.length = sprintf(\n                    RESPONSE.error.message,\n                    \"i am not master\");\n            return SF_RETRIABLE_ERROR_NOT_MASTER;\n        }\n    }\n\n    switch (REQUEST.header.cmd) {\n        case SF_PROTO_ACTIVE_TEST_REQ:\n            RESPONSE.header.cmd = SF_PROTO_ACTIVE_TEST_RESP;\n            return sf_proto_deal_active_test(task, &REQUEST, &RESPONSE);\n        case FCFS_VOTE_SERVICE_PROTO_CLUSTER_STAT_REQ:\n            return service_deal_cluster_stat(task);\n        case FCFS_VOTE_SERVICE_PROTO_GET_MASTER_REQ:\n            if ((result=fcfs_vote_deal_get_master(task,\n                            SERVICE_GROUP_INDEX)) == 0)\n            {\n                RESPONSE.header.cmd = FCFS_VOTE_SERVICE_PROTO_GET_MASTER_RESP;\n            }\n            return result;\n        case SF_SERVICE_PROTO_GET_LEADER_REQ:\n            if ((result=fcfs_vote_deal_get_master(task,\n                            SERVICE_GROUP_INDEX)) == 0)\n            {\n                RESPONSE.header.cmd = SF_SERVICE_PROTO_GET_LEADER_RESP;\n            }\n            return result;\n        case FCFS_VOTE_SERVICE_PROTO_CLIENT_JOIN_REQ:\n            return service_deal_client_join(task);\n        case FCFS_VOTE_SERVICE_PROTO_GET_VOTE_REQ:\n            return service_deal_get_vote(task);\n        case FCFS_VOTE_SERVICE_PROTO_PRE_SET_NEXT_LEADER:\n        case FCFS_VOTE_SERVICE_PROTO_COMMIT_NEXT_LEADER:\n            return service_deal_next_leader(task);\n        case FCFS_VOTE_SERVICE_PROTO_ACTIVE_CHECK_REQ:\n            return service_deal_active_check(task);\n        default:\n            RESPONSE.error.length = sprintf(RESPONSE.error.message,\n                    \"unkown service cmd: %d\", REQUEST.header.cmd);\n            return -EINVAL;\n    }\n}\n\nint service_deal_task(struct fast_task_info *task, const int stage)\n{\n    int result;\n\n    /*\n    logInfo(\"file: \"__FILE__\", line: %d, \"\n            \"task: %p, sock: %d, nio stage: %d, continue: %d, \"\n            \"cmd: %d (%s)\", __LINE__, task, task->event.fd, stage,\n            stage == SF_NIO_STAGE_CONTINUE,\n            ((FCFSVoteProtoHeader *)task->recv.ptr->data)->cmd,\n            fdir_get_cmd_caption(((FCFSVoteProtoHeader *)\n            task->recv.ptr->data)->cmd));\n            */\n\n    if (stage == SF_NIO_STAGE_CONTINUE) {\n        if (task->continue_callback != NULL) {\n            result = task->continue_callback(task);\n        } else {\n            result = RESPONSE_STATUS;\n            if (result == TASK_STATUS_CONTINUE) {\n                logError(\"file: \"__FILE__\", line: %d, \"\n                        \"unexpect status: %d\", __LINE__, result);\n                result = EBUSY;\n            }\n        }\n    } else {\n        sf_proto_init_task_context(task, &TASK_CTX.common);\n        result = service_process(task);\n    }\n\n    if (result == TASK_STATUS_CONTINUE) {\n        return 0;\n    } else {\n        RESPONSE_STATUS = result;\n        return sf_proto_deal_task_done(task, \"service\", &TASK_CTX.common);\n    }\n}\n"
  },
  {
    "path": "src/vote/server/service_handler.h",
    "content": "/*\n * Copyright (c) 2020 YuQing <384681@qq.com>\n *\n * This program is free software: you can use, redistribute, and/or modify\n * it under the terms of the GNU Affero General Public License, version 3\n * or later (\"AGPL\"), as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n//service_handler.h\n\n#ifndef FCFS_VOTE_SERVER_HANDLER_H\n#define FCFS_VOTE_SERVER_HANDLER_H\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"fastcommon/fast_task_queue.h\"\n#include \"server_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint service_handler_init();\nint service_handler_destroy();\nint service_deal_task(struct fast_task_info *task, const int stage);\nvoid service_task_finish_cleanup(struct fast_task_info *task);\nint service_recv_timeout_callback(struct fast_task_info *task);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "systemd/fastauth.service",
    "content": "[Unit]\nDescription=fastcfs auth service\nAfter=network-online.target\n\n[Service]\nType=forking\nPIDFile=/opt/fastcfs/auth/authd.pid\nExecStart=/usr/bin/fcfs_authd /etc/fastcfs/auth/server.conf start\nExecStartPost=/bin/sleep 0.1\nExecStop=/usr/bin/fcfs_authd /etc/fastcfs/auth/server.conf stop\n\n# start/stop timeout\nTimeoutSec=0\n\n# Disable OOM kill by Linux kernel\nOOMScoreAdjust=-1000\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "systemd/fastcfs.service",
    "content": "[Unit]\nDescription=fastcfs service\nAfter=network-online.target\n\n[Service]\nType=forking\nPIDFile=/opt/fastcfs/fcfs/fused.pid\nExecStart=/usr/bin/fcfs_fused /etc/fastcfs/fcfs/fuse.conf start\nExecStartPost=/bin/sleep 0.1\nExecStop=/usr/bin/fcfs_fused /etc/fastcfs/fcfs/fuse.conf stop\n\n# start/stop timeout\nTimeoutSec=60\n\n# Disable OOM kill by Linux kernel\nOOMScoreAdjust=-1000\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "systemd/fastvote.service",
    "content": "[Unit]\nDescription=fastcfs vote service\nAfter=network-online.target\n\n[Service]\nType=forking\nPIDFile=/opt/fastcfs/vote/voted.pid\nExecStart=/usr/bin/fcfs_voted /etc/fastcfs/vote/server.conf start\nExecStartPost=/bin/sleep 0.1\nExecStop=/usr/bin/fcfs_voted /etc/fastcfs/vote/server.conf stop\n\n# start/stop timeout\nTimeoutSec=0\n\n# Disable OOM kill by Linux kernel\nOOMScoreAdjust=-1000\n\n[Install]\nWantedBy=multi-user.target\n"
  }
]