pax_global_header00006660000000000000000000000064150312325110014503gustar00rootroot0000000000000052 comment=15d3c0f8435814fdb7242a8a82fa5db140013d0a glusterfs-11.2/000077500000000000000000000000001503123251100134445ustar00rootroot00000000000000glusterfs-11.2/.clang-format000066400000000000000000000057601503123251100160270ustar00rootroot00000000000000--- Language: Cpp # BasedOnStyle: Chromium AccessModifierOffset: -1 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: All AlwaysBreakAfterReturnType: All AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Linux BreakBeforeInheritanceComma: false BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeCategories: - Regex: '^<.*\.h>' Priority: 1 - Regex: '^<.*' Priority: 2 - Regex: '.*' Priority: 3 IncludeIsMainRegex: '([-_](test|unittest))?$' IndentCaseLabels: true IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: false PenaltyBreakAssignment: 200 PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Right ReflowComments: true SortIncludes: false SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Auto TabWidth: 8 UseTab: Never ... glusterfs-11.2/.github/000077500000000000000000000000001503123251100150045ustar00rootroot00000000000000glusterfs-11.2/.github/ISSUE_TEMPLATE000066400000000000000000000016271503123251100171200ustar00rootroot00000000000000 **Description of problem:** **The exact command to reproduce the issue**: **The full output of the command that failed**:
**Expected results:**
**Mandatory info:** **- The output of the `gluster volume info` command**: **- The output of the `gluster volume status` command**: **- The output of the `gluster volume heal` command**: **- Provide logs present on following locations of client and server nodes - /var/log/glusterfs/ **- Is there any crash ? Provide the backtrace and coredump **Additional info:**
**- The operating system / glusterfs version**: **Note: Please hide any confidential data which you don't want to share in public like IP address, file name, hostname or any other configuration** glusterfs-11.2/.github/PULL_REQUEST_TEMPLATE000066400000000000000000000000001503123251100201740ustar00rootroot00000000000000glusterfs-11.2/.github/RELEASE_TRACKER_TEMPLATE000066400000000000000000000003461503123251100204600ustar00rootroot00000000000000 **Description of problem:** A tracker issue to track the issues that will be fixed as a part of this release **Major or minor release**: **Release version**: glusterfs-11.2/.github/stale.yml000066400000000000000000000016131503123251100166400ustar00rootroot00000000000000# Number of days of inactivity before an issue becomes stale daysUntilStale: 210 # Number of days of inactivity before a stale issue is closed daysUntilClose: 15 # Issues with these labels will never be considered stale exemptLabels: - pinned - security # Label to use when marking an issue as stale staleLabel: wontfix # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > Thank you for your contributions. Noticed that this issue is not having any activity in last ~6 months! We are marking this issue as stale because it has not had recent activity. It will be closed in 2 weeks if no one responds with a comment here. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: > Closing this issue as there was no update since my last update on issue. If this is an issue which is still valid, feel free to open it. glusterfs-11.2/.gitignore000066400000000000000000000055731503123251100154460ustar00rootroot00000000000000aclocal.m4 autom4te.cache build config.* configure cscope.* tags TAGS ID GPATH GRTAGS GTAGS GSYMS depcomp INSTALL install-sh ltmain.sh Makefile Makefile.in missing stamp-h1 stamp-h2 test-driver *compile *.gcda *.gcno *.sw? *~ *.lo *.la *.o *.tar.gz *.rpm *.diff *.patch .libs .deps .dirstamp # Softlinks to test and log log *.vol .clang-format # cmocka unit tests *.log *.trs *_unittest # Generated files site.h *.py[co] api/examples/__init__.py api/examples/setup.py api/src/gfapi.map cli/src/gluster contrib/fuse-util/fusermount-glusterfs extras/command-completion/Makefile extras/geo-rep/gsync-sync-gfid extras/geo-rep/schedule_georep.py extras/snap_scheduler/conf.py extras/init.d/glusterd-Debian extras/init.d/glusterd-FreeBSD extras/init.d/glusterd-Redhat extras/init.d/glusterd-SuSE extras/init.d/glusterd.plist extras/ocf/glusterd extras/ocf/volume extras/run-gluster.tmpfiles extras/systemd/glusterd.service extras/systemd/gluster-ta-volume.service extras/systemd/glusterfssharedstorage.service extras/who-wrote-glusterfs/gitdm geo-replication/.tox geo-replication/gsyncd.conf geo-replication/src/gsyncd geo-replication/src/peer_gsec_create geo-replication/src/peer_mountbroker geo-replication/src/peer_mountbroker.py geo-replication/src/set_geo_rep_pem_keys.sh geo-replication/src/peer_georep-sshkey.py geo-replication/syncdaemon.egg-info geo-replication/syncdaemon/conf.py geo-replication/tests/unit/.coverage geo-replication/tests/unit/cover geo-replication/tests/unit/coverage.xml geo-replication/tests/unit/nosetests.xml geo-replication/tests/unit/results.html glusterfs-api.pc glusterfs.spec glusterfsd/src/glusterd glusterfsd/src/glusterfs glusterfsd/src/glusterfsd glusterfsd/src/gf_attach heal/src/glfsheal libgfchangelog.pc libglusterfs/src/graph.lex.c libglusterfs/src/y.tab.c libglusterfs/src/y.tab.h libglusterfs/src/defaults.c libglusterfs/src/cli1-xdr.h libglusterfs/src/protocol-common.h libtool # copied XDR for cyclic libglusterfs <-> rpc-header dependency run-tests.sh !tests/basic/fuse/Makefile !tests/basic/gfapi/Makefile tests/env.rc tests/utils/arequal-checksum xlators/features/glupy/src/__init__.py xlators/features/glupy/src/setup.py xlators/mount/fuse/utils/mount.glusterfs xlators/mount/fuse/utils/mount_glusterfs extras/peer_add_secret_pub tools/gfind_missing_files/gcrawler tools/glusterfind/glusterfind tools/glusterfind/src/tool.conf # Eventing events/src/eventsapiconf.py extras/systemd/glustereventsd.service events/src/eventtypes.py libglusterfs/src/eventtypes.h extras/init.d/glustereventsd-Debian extras/init.d/glustereventsd-FreeBSD extras/init.d/glustereventsd-Redhat tools/setgfid2path/src/gluster-setgfid2path xlators/features/cloudsync/src/cloudsync-autogen-fops.c xlators/features/cloudsync/src/cloudsync-autogen-fops.h xlators/features/utime/src/utime-autogen-fops.c xlators/features/utime/src/utime-autogen-fops.h tests/basic/metadisp/ftruncate xlators/features/metadisp/src/fops.c glusterfs-11.2/.mailmap000066400000000000000000000052611503123251100150710ustar00rootroot00000000000000# .mailmap, see 'git short-log --help' for details # # This file needs to match extras/who-wrote-glusterfs/gitdm.aliases. # # Listing of contributors that filed patches with different email addresses. # Format: [ ...] # Amar Tumballi Anand Avati Anush Shetty Csaba Henk Günther Deschner Harshavardhana Ji-Hyeon Gim Justin Clift Kaleb S. KEITHLEY Kaushal M Kaushik BV Krishna Srinivas Krishnan Parthasarathi Louis Zuckerman M S Vishwanath Bhat Michael Adam Oleksandr Natalenko Patrick Uiterwijk Pavan Sondur Pete Zaitcev Pranith Kumar K Prashanth Pai Raghavendra Bhat Raghavendra G Rahul C S Rajesh Amaravathi Ravishankar N Sakshi Bansal Shehjar Tikoo Venky Shankar Vijay Bellur Vijaykumar Koppad Vijaikumar Mallikarjuna Vikas Gorur shishir gowda glusterfs-11.2/.testignore000066400000000000000000000031131503123251100156260ustar00rootroot00000000000000.github/ISSUE_TEMPLATE .github/PULL_REQUEST_TEMPLATE .github/stale.yml .gitignore .mailmap .testignore .clang-format rfc.sh submit-for-review.sh AUTHORS CONTRIBUTING.md COPYING-GPLV2 COPYING-LGPLV3 ChangeLog INSTALL MAINTAINERS NEWS README.md THANKS COMMITMENT api/examples/README api/examples/getvolfile.py api/src/README.Symbol_Versions build-aux/checkpatch.pl contrib/fuse-lib/COPYING.LIB contrib/fuse-util/COPYING contrib/macfuse/COPYING.txt doc/* extras/FreeBSD/README.FreeBSD extras/Solaris/README.solaris extras/Ubuntu/README.Ubuntu extras/benchmarking/README extras/cliutils/README.md extras/command-completion/README extras/create_new_xlator/README.md extras/glusterfs.vim extras/glusterfs-logrotate extras/glusterfs-georep-logrotate extras/init.d/glusterd-Debian.in extras/init.d/glusterd-FreeBSD.in extras/init.d/glusterd-Redhat.in extras/init.d/glusterd-SuSE.in extras/init.d/glusterd.plist.in extras/init.d/glustereventsd-Debian.in extras/init.d/glustereventsd-FreeBSD.in extras/init.d/glustereventsd-Redhat.in extras/init.d/rhel5-load-fuse.modules extras/logger.conf.example extras/snap_scheduler/README.md extras/test/ld-preload-test/README extras/who-wrote-glusterfs/* extras/distributed-testing/* geo-replication/syncdaemon/README.md geo-replication/test-requirements.txt rpc/xdr/src/.gitignore tests/README.md xlators/experimental/README.md xlators/experimental/dht2/README.md xlators/experimental/dht2/TODO.md xlators/experimental/posix2/README.md xlators/experimental/posix2/TODO.md xlators/features/glupy/doc/README.md xlators/features/glupy/doc/TESTING xlators/features/glupy/doc/test.vol glusterfs-11.2/AUTHORS000066400000000000000000000000001503123251100145020ustar00rootroot00000000000000glusterfs-11.2/COMMITMENT000066400000000000000000000040571503123251100150510ustar00rootroot00000000000000Common Cure Rights Commitment Version 1.0 Before filing or continuing to prosecute any legal proceeding or claim (other than a Defensive Action) arising from termination of a Covered License, we commit to extend to the person or entity ('you') accused of violating the Covered License the following provisions regarding cure and reinstatement, taken from GPL version 3. As used here, the term 'this License' refers to the specific Covered License being enforced. However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. We intend this Commitment to be irrevocable, and binding and enforceable against us and assignees of or successors to our copyrights. Definitions 'Covered License' means the GNU General Public License, version 2 (GPLv2), the GNU Lesser General Public License, version 2.1 (LGPLv2.1), or the GNU Library General Public License, version 2 (LGPLv2), all as published by the Free Software Foundation. 'Defensive Action' means a legal proceeding or claim that We bring against you in response to a prior proceeding or claim initiated by you or your affiliate. 'We' means each contributor to this repository as of the date of inclusion of this file, including subsidiaries of a corporate contributor. This work is available under a Creative Commons Attribution-ShareAlike 4.0 International license (https://creativecommons.org/licenses/by-sa/4.0/). glusterfs-11.2/CONTRIBUTING.md000066400000000000000000000166671503123251100157150ustar00rootroot00000000000000# GlusterFS project Contribution guidelines ## Development Workflow We follow most of the details as per the [document here](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests). If you are not aware of the github workflow, it is recommended to go through them before continuing here. #### Get the Repository setup 0. Fork Repository - Fork [GlusterFS repository](https://github.com/gluster/glusterfs/fork). 1. Clone Repository - Clone the glusterfs repo freshly from github using below steps. ``` git clone git@github.com:${username}/glusterfs.git cd glusterfs/ git remote add upstream git@github.com:gluster/glusterfs.git ``` About two tasks are one time for the life time. You can continue to use the same repository for all the work in future. #### Development & Other flows 0. Issue: - Make sure there is an issue filed for the task you are working on. - If it is not filed, open the issue with all the description. - If it is a bug fix, add label "Type:Bug". - If it is an RFC, provide all the documentation, and request for "DocApproved", and "SpecApproved" label. 1. Code: - Start coding - Build and test locally - Make sure clang-format is installed and is run on the patch. 2. Keep up-to-date - GlusterFS is a large project with many developers, so there would be one or the other patch everyday. - It is critical for developer to be up-to-date with `devel` repo to be Conflict-Free when PR is opened. - Git provides many options to keep up-to-date, below is one of them ``` git fetch upstream git rebase upstream/devel ``` - It is recommended you keep pushing to your repo every day, so you don't loose any work. - It can be done by `./rfc.sh` (or `git push origin HEAD:issueNNN`) 2. Commit Message / PR description: - The name of the branch on your personal fork can start with issueNNNN, followed by anything of your choice. - PRs continue to have the title of format "component: \", like it is practiced now. - When you open a PR, having a reference Issue for the commit is mandatory in GlusterFS. - Commit message can have, either `Fixes: #NNNN` or `Updates: #NNNN` in a separate line in the commit message. - Here, NNNN is the Issue ID in glusterfs repository. - Each commit needs the author to have the "Signed-off-by: Name \" line. - Can do this by `-s` option for `git commit`. - If the PR is not ready for review, apply the label `work-in-progress`. - Check the availability of "Draft PR" is present for you, if yes, use that instead. 3. Tests: - All the required smoke tests would be auto-triggered. - Developers get a chance to retrigger the smoke tests using **"/recheck smoke"** as comment. - The "regression" tests would be triggered by a comment **"/run regression"** from developers in the [@gluster](https://github.com/orgs/gluster/people) organization. - Ask for help as comment in PR if you have any questions about the process! 4. Review Process: - `+2` : is equivalent to "Approve" from the people in the maintainer's group. - `+1` : can be given by a maintainer/reviewer by explicitly stating that in the comment. - `-1` : provide details on required changes and pick "Request Changes" while submitting your review. - `-2` : done by adding the `DO-NOT-MERGE` label. - Any further discussions can happen as comments in the PR. 5. Making changes: - There are 2 approaches to submit changes done after addressing review comments. - Commit changes as a new commit on top of the original commits in the branch, and push the changes to same branch (issueNNNN) - Commit changes into the same commit with `--amend` option, and do a push to the same branch with `--force` option. 6. Merging: - GlusterFS project follows 'Squash and Merge' method - This is mainly to preserve the historic Gerrit method of one patch in `git log` for one URL link. - This also makes every merge a complete patch, which has passed all tests. - The merging of the patch is expected to be done by the maintainers. - It can be done when all the tests (smoke and regression) pass. - When the PR has 'Approved' flag from corresponding maintainer. - If you feel there is delay, feel free to add a comment, discuss the same in Slack channel, or send email. ## Becoming member of gluster.org team in github This section details the process of becoming part of gluster.org team. The member list is reviewed by the current maintainers along with infra team time to time. ### How to create a presence/identity in the Gluster.org projects Words 'MUST', 'SHOULD' etc to be referred from [RFC2119](https://tools.ietf.org/html/rfc2119) * MUST have enabled [two-factor authentication](https://help.github.com/articles/about-two-factor-authentication) on their GitHub account * MUST have read the [contributor guide](#) * SHOULD have made multiple contributions to the project or community. Contribution may include, but is not limited to: - Authoring or reviewing PRs on GitHub - Filing or commenting on issues on GitHub - Contributing to subproject, or community discussions (e.g. meetings, Slack, email discussion forums, Stack Overflow) * SHOULD be subscribed to [gluster-users](https://lists.gluster.org/mailman/listinfo/gluster-users), [gluster-devel](https://lists.gluster.org/mailman/listinfo/gluster-devel) mailing lists. * SHOULD be actively contributing to 1 or more subprojects for 30 days. * Sponsored by 2 gluster-maintainers. Note the following requirements for sponsors: - Sponsors must have close interactions with the prospective member - e.g. code/design/proposal review, coordinating on issues, etc. - Sponsors must be the maintainer of an active repo in the Gluster org - An approver/reviewer in the Gluster org may sponsor someone for the Gluster org; as long as it's a project they're involved with. ### Request Process * Open an issue against the [project-infrastructure](https://github.com/gluster/project-infrastructure) repo - With your github handle - Ensure your sponsors are @mentioned on the issue or may be as approvers - Have your sponsoring reviewers reply confirmation of sponsorship: +1 * Once your sponsors have responded, your request will be reviewed by the admin. Any missing information will be requested. ## By contributing to this project, the contributor would need to agree to below. ### Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. glusterfs-11.2/COPYING-GPLV2000066400000000000000000000432541503123251100153370ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. glusterfs-11.2/COPYING-LGPLV3000066400000000000000000000167431503123251100154570ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. glusterfs-11.2/ChangeLog000066400000000000000000000000461503123251100152160ustar00rootroot00000000000000ChangeLog is maintained by "git log". glusterfs-11.2/INSTALL000066400000000000000000000025601503123251100145000ustar00rootroot00000000000000Installation Instructions ************************* 0. If you have cloned from git, run ./autogen.sh. 1. Run ./configure. bash# ./configure GlusterFS configure summary =========================== GlusterFS configure summary =========================== FUSE client : yes Infiniband verbs : yes epoll IO multiplex : yes argp-standalone : no fusermount : yes readline : yes georeplication : yes Linux-AIO : yes Enable Debug : no Block Device xlator : yes glupy : yes Use syslog : yes XML output : yes QEMU Block formats : yes Encryption xlator : yes The configure summary will tell you what all components will be built with GlusterFS. Other than 'argp-standalone' if something else says 'no', that feature in GlusterFS will not be built. 'argp-standalone' package will only be used if the system doesn't have a proper argp package installed. 2. Now just run 'make' and later run 'make install' to install the package. bash# make bash# make install Installation completed :-) bash# glusterfs --version Make sure your version is the latest from the release, and the one you just installed :-) For more information on GlusterFS installation refer# http://docs.gluster.org/en/latest/Developer-guide/Building-GlusterFS/ glusterfs-11.2/MAINTAINERS000066400000000000000000000335311503123251100151460ustar00rootroot00000000000000GlusterFS Maintainers ===================== The intention of this file is not to establish who owns what portions of the code base, but to provide a set of names that developers can consult when they have a question about a particular subset and also to provide a set of names to be CC'd when submitting a patch to obtain appropriate review. In general, if you have a question about inclusion of a patch, you should consult gluster-devel@gluster.org and not any specific individual privately. Descriptions of section entries: M: Main contact that knows and takes care of this area L: Mailing list that is relevant to this area W: Web-page with status/info Q: Patchwork web based patch tracking system site T: SCM tree type and location. Type is one of: git, hg, quilt, stgit. S: Status, one of the following: Supported: Someone is actually paid to look after this. Maintained: Someone actually looks after it. Odd Fixes: It has a maintainer but they don't have time to do much other than throw the odd patch in. See below. Orphan: No current maintainer [but maybe you could take the role as you write your new code]. Obsolete: Old code. Something tagged obsolete generally means it has been replaced by a better system and you should be using that. F: Files and directories with wildcard patterns. A trailing slash includes all files and subdirectory files. F: drivers/net/ all files in and below drivers/net F: drivers/net/* all files in drivers/net, but not below F: */net/* all files in "any top level directory"/net One pattern per line. Multiple F: lines acceptable. X: Files and directories that are NOT maintained, same rules as F: Files exclusions are tested before file matches. Can be useful for excluding a specific subdirectory, for instance: F: net/ X: net/ipv6/ matches all files in and below net excluding net/ipv6/ K: Keyword perl extended regex pattern to match content in a patch or file. For instance: K: of_get_profile matches patches or files that contain "of_get_profile" K: \b(printk|pr_(info|err))\b matches patches or files that contain one or more of the words printk, pr_info or pr_err One regex pattern per line. Multiple K: lines acceptable. P: Peer for a component General Project Architects -------------------------- M: Amar Tumballi M: Xavier Hernandez P: Pranith Karampuri P: Atin Mukherjee xlators: -------- Access Control List (ACL) M: Raghavendra Talur P: Jiffin Tony Thottan S: Maintained F: xlators/system/posix-acl/ Arbiter M: Ravishankar N P: Pranith Karampuri S: Maintained F: xlators/features/arbiter/ Automatic File Replication (AFR) M: Pranith Karampuri M: Ravishankar N P: Karthik US S: Maintained F: xlators/cluster/afr/ Barrier M: Raghavendra Bhat P: Atin Mukherjee S: Maintained F: xlators/features/barrier BitRot M: Kotresh HR P: Raghavendra Bhat S: Maintained F: xlators/features/bit-rot/ Changelog M: Aravinda Vishwanathapura P: Kotresh HR S: Maintained F: xlators/features/changelog/ Distributed Hashing Table (DHT) M: Mohit Agrawal M: Barak Sason Rofman M: Tamar Shacked P: Csaba Henk S: Maintained F: xlators/cluster/dht/ Erasure Coding M: Pranith Karampuri M: Xavier Hernandez P: Ashish Pandey P: Sheetal Pamecha S: Maintained F: xlators/cluster/ec/ Error-gen M: Raghavendra Talur S: Maintained F: xlators/debug/error-gen/ FUSE Bridge M: Csaba Henk P: Niels de Vos S: Maintained F: xlators/mount/ Index M: Pranith Karampuri P: Ravishankar N S: Maintained F: xlators/features/index/ IO Cache P: Mohammed Rafi KC S: Maintained F: xlators/performance/io-cache/ IO Statistics M: Krutika Dhananjay M: Shyam Ranganathan S: Maintained F: xlators/debug/io-stats/ IO threads M: Pranith Karampuri P: Ravishankar N S: Maintained F: xlators/performance/io-threads/ Leases M: Poornima G P: Niels de Vos P: Soumya Koduri S: Maintained F: xlators/features/leases/ Locks M: Krutika Dhananjay P: Xavier Hernandez S: Maintained F: xlators/features/locks/ Marker M: Kotresh HR S: Maintained F: xlators/features/marker/ Meta M: Mohammed Rafi KC S: Maintained F: xlators/features/meta/ Metadata-cache M: Poornima G P: Soumya Koduri S: Maintained F: xlators/performance/md-cache/ Negative-lookup Cache M: Poornima G P: Pranith Karampuri S: Maintained F: xlators/performance/nl-cache/ gNFS M: Jiffin Tony Thottan P: Xie Changlong P: Amar Tumballi S: Odd Fixes F: xlators/nfs/server/ Open-behind S: Maintained F: xlators/performance/open-behind/ Posix: M: Raghavendra Bhat P: Kotresh HR P: Krutika Dhananjay S: Maintained F: xlators/storage/posix/ Quick-read S: Maintained F: xlators/performance/quick-read/ Quota M: Shyamsundar Ranganathan P: Hari Gowtham S: Maintained F: xlators/features/quota/ Read-ahead P: Csaba Henk S: Maintained F: xlators/performance/read-ahead/ Readdir-ahead S: Maintained F: xlators/performance/readdir-ahead/ Sharding M: Krutika Dhananjay P: Xavier Hernandez S: Maintained F: xlators/features/shard/ Trash M: Anoop C S M: Jiffin Tony Thottan S: Maintained F: xlators/features/trash/ Upcall M: Poornima G M: Soumya Koduri P: Niels de Vos S: Maintained F: xlators/features/upcall/ Write-behind P: Csaba Henk S: Maintained F: xlators/performance/write-behind/ Write Once Read Many P: Karthik US S: Maintained F: xlators/features/read-only/ Cloudsync M: Susant Kumar Palai S: Maintained F: xlators/features/cloudsync/ Other bits of code: ------------------- Doc M: Humble Chirammal M: Raghavendra Talur S: Maintained F: doc/ Geo Replication M: Aravinda Vishwanathapura M: Kotresh HR M: Sunny Kumar P: Shwetha Acharya S: Maintained F: geo-replication/ Glusterfind M: Aravinda Vishwanathapura P: Shwetha Acharya S: Maintained F: tools/glusterfind/ libgfapi M: Niels de Vos P: Poornima G P: Shyamsundar Ranganathan P: Soumya Koduri S: Maintained F: api/ libglusterfs M: Amar Tumballi M: Xavier Hernandez M: Jeff Darcy P: Kaleb Keithley P: Niels de Vos P: Pranith Karampuri P: Shyamsundar Ranganathan S: Maintained F: libglusterfs/ xxhash M: Aravinda Vishwanathapura M: Kotresh HR P: Yaniv Kaul S: Maintained F: contrib/xxhash/ T: https://github.com/Cyan4973/xxHash.git Management Daemon - glusterd M: Atin Mukherjee M: Mohit Agrawal M: Sanju Rakonde P: Nikhil Ladha S: Maintained F: cli/ F: xlators/mgmt/glusterd/ Protocol M: Niels de Vos P: Mohammed Rafi KC S: Maintained F: xlators/protocol/ Remote Procedure Call subsystem P: Mohit Agrawal S: Maintained F: rpc/rpc-lib/ F: rpc/xdr/ Snapshot M: Raghavendra Bhat P: Mohammed Rafi KC P: Sunny Kumar S: Maintained F: xlators/mgmt/glusterd/src/glusterd-snap* F: extras/snap-scheduler.py Socket subsystem P: Krutika Dhananjay P: Milind Changire P: Mohammed Rafi KC P: Mohit Agrawal S: Maintained F: rpc/rpc-transport/socket/ Testing - .t framework M: Raghavendra Talur S: Maintained F: tests/ Utilities M: Aravinda Vishwanathapura P: Niels de Vos P: Raghavendra Talur P: Sachidanda Urs S: Maintained F: extras/ Events APIs M: Aravinda Vishwanathapura P: Srijan Sivakumar S: Maintained F: events/ F: libglusterfs/src/events* F: libglusterfs/src/eventtypes* F: extras/systemd/glustereventsd* Distribution Specific: ---------------------- Build: M: Niels de Vos M: Hari Gowtham P: Anoop C S P: Raghavendra Talur P: Rinku Kothiya S: Maintained Debian packages on download.gluster.org M: packaging@gluster.org M: Kaleb Keithley P: Sheetal Pamecha P: Shwetha Acharya S: Maintained W: http://download.gluster.org/pub/gluster/glusterfs/LATEST/Debian/Debian.README T: https://github.com/gluster/glusterfs-debian.git OpenSuSE M: packaging@gluster.org M: Kaleb Keithley P: Sheetal Pamecha P: Shwetha Acharya S: Maintained W: https://build.opensuse.org/repositories/home:glusterfs W: https://download.gluster.org/pub/gluster/glusterfs/LATEST/SuSE/SuSE.README T: https://github.com/gluster/glusterfs-suse.git Packages for the CentOS Storage SIG M: centos-devel@centos.org M: Niels de Vos P: Kaleb Keithley S: Maintained W: https://wiki.centos.org/SpecialInterestGroup/Storage/Gluster T: https://github.com/CentOS-Storage-SIG/glusterfs.git Ubuntu PPA M: packaging@gluster.org M: Kaleb Keithley P: Sheetal Pamecha P: Shwetha Acharya S: Maintained W: https://launchpad.net/~gluster W: http://download.gluster.org/pub/gluster/glusterfs/LATEST/Ubuntu/Ubuntu.README T: https://github.com/gluster/glusterfs-debian.git Related projects ---------------- Gluster Block M: Prasanna Kumar Kalever M: Xiubo Li S: Maintained T: https://github.com/gluster/gluster-block.git GlusterFS core-utils M: Anoop C S S: Maintained T: https://github.com/gluster/glusterfs-coreutils.git NFS-Ganesha FSAL plugin M: Jiffin Tony Thottan M: Kaleb Keithley M: Soumya Koduri S: Maintained T: git://github.com/nfs-ganesha/nfs-ganesha.git F: src/nfs-ganesha~/src/FSAL/FSAL_GLUSTER/ QEMU integration M: Niels de Vos M: Prasanna Kumar Kalever S: Maintained T: git://git.qemu.org/qemu.git F: block/gluster.c Samba VFS plugin M: Anoop C S M: Raghavendra Talur M: Michael Adam M: Poornima G S: Maintained T: git://git.samba.org/samba.git F: source3/modules/vfs_glusterfs.c Storhaug M: Jose A. Rivera P: Kaleb Keithley S: Maintained T: https://github.com/linux-ha-storage/storhaug.git Testing - Glusto-Tests M: Jonathan Holloway M: Vijay Bhaskar Reddy Avuthu M: Akarsha Rai S: Maintained T: https://github.com/gluster/glusto-tests.git Wireshark dissectors M: Niels de Vos S: Maintained W: https://forge.gluster.org/wireshark T: http://code.wireshark.org/git/wireshark F: epan/dissectors/packet-gluster* Infrastructure -------------- Platform M: Michael Scherer P: Deepshika Khandelwal P: Amar Tumballi Continuous Integration M: Michael Scherer M: Deepshikha Khandelwal P: Niels de Vos Special Thanks -------------- GlusterFS would not be possible without the contributions of: M: Vijay Bellur M: Jeff Darcy M: Shreyas Siravara M: Kaushal M M: Nigel Babu M: Prashanth Pai P: Sanoj Unnikrishnan P: Milind Changire P: Sunil Kumar Acharya M: Samikshan Bairagya M: Chris Hertel M: M. Mohan Kumar M: Shishir Gowda M: Brian Foster M: Anand Avati M: Dennis Schafroth M: Harshavardhana M: Krishnan Parthasarathi M: Justin Clift M: Venky Shankar M: Shravan Chandrashekar M: Joseph Fernandes M: Vijaikumar Mallikarjuna M: Anand Subramanian M: Bharata B Rao M: Rajesh Joseph M: Dan Lambright M: Jay Vyas M: Luis Pabon M: Ira Cooper M: Shwetha Panduranga M: Nithya Balachandran M: Raghavendra Gowdappa M: Susant Palai glusterfs-11.2/Makefile.am000066400000000000000000000036311503123251100155030ustar00rootroot00000000000000SOURCES = site.h EXTRA_DIST = autogen.sh \ COPYING-GPLV2 COPYING-LGPLV3 COMMITMENT \ INSTALL README.md AUTHORS THANKS NEWS \ glusterfs.spec glusterfs-api.pc.in libgfchangelog.pc.in \ run-tests.sh \ build-aux/pkg-version \ contrib/umountd \ $(shell find $(top_srcdir)/tests -type f -print) SUBDIRS = $(ARGP_STANDALONE_DIR) libglusterfs rpc api \ glusterfsd xlators $(FUSERMOUNT_SUBDIR) doc extras cli heal \ @SYNCDAEMON_SUBDIR@ @UMOUNTD_SUBDIR@ tools events pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = glusterfs-api.pc libgfchangelog.pc CLEANFILES = glusterfs-api.pc libgfchangelog.pc contrib/umountd/Makefile clean-local: find . -name '*.o' -o -name '*.lo' -o -name '.Po' | xargs rm -f gitclean: distclean find . -name Makefile.in -exec rm -f {} \; find . -name mount.glusterfs -exec rm -f {} \; find . -name .deps -o -name .libs | xargs rm -rf rm -fr autom4te.cache rm -f missing aclocal.m4 config.h.in config.guess config.sub ltmain.sh install-sh configure depcomp # dist-hook gets executed with 'make dist', this is the only target getting # executed, a dist-hook in other Makefile.am files seem to get ignored. dist-hook: gen-VERSION gen-ChangeLog -rm -fr $(distdir)/contrib/umountd/.deps -rm -f $(distdir)/events/src/eventtypes.py -rm -f $(distdir)/tests/env.rc -cp -f $(top_srcdir)/build-aux/config.sub.dist $(distdir)/config.sub -cp -f $(top_srcdir)/build-aux/config.guess.dist $(distdir)/config.guess .PHONY: gen-VERSION gen-ChangeLog clang-check clang-check: @$(top_srcdir)/extras/clang-checker.sh gen-ChangeLog: if test -d $(srcdir)/.git; then \ (cd $(srcdir) && git diff && echo ===== git log ==== && git log) > $(distdir)/ChangeLog; \ fi .PHONY : gen-VERSION gen-VERSION: if test -d $(top_srcdir)/.git; then \ cd $(top_srcdir); \ ./build-aux/pkg-version --full \ > $(abs_top_builddir)/$(distdir)/VERSION; \ fi glusterfs-11.2/NEWS000066400000000000000000000000311503123251100141350ustar00rootroot00000000000000Gluster moves to Gerrit. glusterfs-11.2/README.md000066400000000000000000000104501503123251100147230ustar00rootroot00000000000000

Gluster is a free and open source software scalable network filesystem.





---

Build Status Coverage Status

# Gluster Gluster is a software defined distributed storage that can scale to several petabytes. It provides interfaces for object, block and file storage. ## Development The development workflow is documented in [Contributors guide](CONTRIBUTING.md) ## Documentation The Gluster documentation can be found at [Gluster Docs](http://docs.gluster.org). ## Deployment Quick instructions to build and install can be found in [INSTALL](INSTALL) file. ## Testing GlusterFS source contains some functional tests under `tests/` directory. All these tests are run against every patch submitted for review. If you want your patch to be tested, please add a `.t` test file as part of your patch submission. You can also submit a patch to only add a `.t` file for the test case you are aware of. To run these tests, on your test-machine, just run `./run-tests.sh`. Don't run this on a machine where you have 'production' glusterfs is running, as it would blindly kill all gluster processes in each runs. If you are sending a patch, and want to validate one or few specific tests, then run a single test by running the below command. ``` bash# /bin/bash ${path_to_gluster}/tests/basic/rpc-coverage.t ``` You can also use `prove` tool if available in your machine, as follows. ``` bash# prove -vmfe '/bin/bash' ${path_to_gluster}/tests/basic/rpc-coverage.t ``` ## Maintainers The list of Gluster maintainers is available in [MAINTAINERS](MAINTAINERS) file. ## License Gluster is dual licensed under [GPLV2](COPYING-GPLV2) and [LGPLV3+](COPYING-LGPLV3). Please visit the [Gluster Home Page](http://www.gluster.org/) to find out more about Gluster. glusterfs-11.2/THANKS000066400000000000000000000001321503123251100143530ustar00rootroot00000000000000For all of you, who use the product and help us make it more robust, useful, and popular. glusterfs-11.2/VERSION000066400000000000000000000000101503123251100145030ustar00rootroot00000000000000v11.2-0 glusterfs-11.2/api/000077500000000000000000000000001503123251100142155ustar00rootroot00000000000000glusterfs-11.2/api/Makefile.am000066400000000000000000000000271503123251100162500ustar00rootroot00000000000000SUBDIRS = src examples glusterfs-11.2/api/examples/000077500000000000000000000000001503123251100160335ustar00rootroot00000000000000glusterfs-11.2/api/examples/Makefile.am000066400000000000000000000002341503123251100200660ustar00rootroot00000000000000# The bits needed for glfsxmp EXTRA_PROGRAMS = glfsxmp glfsxmp_SOURCES = glfsxmp.c glfsxmp_CFLAGS = $(GLFS_CFLAGS) -Wall glfsxmp_LDADD = $(GLFS_LIBS) -lrt glusterfs-11.2/api/examples/README000066400000000000000000000024431503123251100167160ustar00rootroot00000000000000This is an example application which uses libgfapi. It is a complete autotools based build system which demonstrates the required changes in configure.ac, Makefile.am etc to successfuly detect for and build an application against libgfapi. There are two approaches to building a libgfapi based application: 1. In the presence of pkg-config in your build system. This is the recommended approach which is also used in this example. For this approach to work, you need to build glusterfs by passing --pkgconfigdir=/usr/lib64/pkgconfig (or the appropriate directory) in your distro. This already happens if you build RPMs with the glusterfs.spec provided in glusterfs.git. You will also need to install glusterfs-api RPM. 2. In the absence of pkg-config in your build system. Make sure your LDFLAGS includes -L/path/to/lib where libgfapi.so is installed and -I/path/to/include/glusterfs where the 'api' directory containing the headers are available. glfsxmp.c ========= glfsxmp.c is an example application which uses libgfapi Compilation Steps For glfsxmp.c =============================== 1. $./autogen.sh 2. $./configure Note: Before running ./configure , as mentioned above, you need to take care of #1 or #2 i.e. pkg-config path or LDFLAGS and -I/ with correct values. 3. $make glfsxmp glusterfs-11.2/api/examples/autogen.sh000077500000000000000000000000571503123251100200360ustar00rootroot00000000000000#!/bin/sh aclocal autoconf automake --foreign glusterfs-11.2/api/examples/configure.ac000066400000000000000000000002551503123251100203230ustar00rootroot00000000000000 AC_INIT([glfs-test],[0.1],[gluster-devel@nongu.org]) AM_INIT_AUTOMAKE AC_CONFIG_FILES([Makefile]) AC_PROG_CC PKG_CHECK_MODULES([GLFS], [glusterfs-api >= 3]) AC_OUTPUT glusterfs-11.2/api/examples/getvolfile.py000077500000000000000000000024241503123251100205520ustar00rootroot00000000000000#!/usr/bin/python3 from __future__ import print_function import ctypes import ctypes.util api = ctypes.CDLL("libgfapi.so") api.glfs_get_volfile.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_ulong] api.glfs_get_volfile.restype = ctypes.c_long def get_volfile(host, volume): # This is set to a large value to exercise the "buffer not big enough" # path. More realistically, you'd just start with a huge buffer. BUF_LEN = 0 fs = api.glfs_new(volume) # api.glfs_set_logging(fs,"/dev/stderr",7) api.glfs_set_volfile_server(fs, "tcp", host, 24007) api.glfs_init(fs) vbuf = ctypes.create_string_buffer(BUF_LEN) vlen = api.glfs_get_volfile(fs, vbuf, BUF_LEN) if vlen < 0: vlen = BUF_LEN - vlen vbuf = ctypes.create_string_buffer(vlen) vlen = api.glfs_get_volfile(fs, vbuf, vlen) api.glfs_fini(fs) if vlen <= 0: return vlen return vbuf.value[:vlen] if __name__ == "__main__": import sys try: res = get_volfile(*sys.argv[1:3]) except: print("fetching volfile failed (volume not started?)") try: for line in res.split('\n'): print(line) except: print("bad return value %s" % res) glusterfs-11.2/api/examples/glfsxmp.c000066400000000000000000001507031503123251100176650ustar00rootroot00000000000000#include #include #include #include #include #include #define TEST_STR_LEN 2048 int test_dirops(glfs_t *fs) { glfs_fd_t *fd = NULL; char buf[512]; struct dirent *entry = NULL; fd = glfs_opendir(fs, "/"); if (!fd) { fprintf(stderr, "/: %s\n", strerror(errno)); return -1; } fprintf(stderr, "Entries:\n"); while (glfs_readdir_r(fd, (struct dirent *)buf, &entry), entry) { fprintf(stderr, "%s: %lu\n", entry->d_name, glfs_telldir(fd)); } glfs_closedir(fd); return 0; } int test_xattr(glfs_t *fs) { char *filename = "/filename2"; char *linkfile = "/linkfile"; glfs_fd_t *fd = NULL; char buf[512]; char *ptr; int ret; ret = glfs_setxattr(fs, filename, "user.testkey", "testval", 8, 0); fprintf(stderr, "setxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); ret = glfs_setxattr(fs, filename, "user.testkey2", "testval", 8, 0); fprintf(stderr, "setxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); ret = glfs_getxattr(fs, filename, "user.testkey", buf, 512); fprintf(stderr, "getxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); if (ret < 0) return -1; ret = glfs_listxattr(fs, filename, buf, 512); fprintf(stderr, "listxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); if (ret < 0) return -1; ret = glfs_symlink(fs, "filename", linkfile); fprintf(stderr, "symlink(%s %s): %s\n", filename, linkfile, strerror(errno)); if (ret < 0) return -1; ret = glfs_readlink(fs, linkfile, buf, 512); fprintf(stderr, "readlink(%s) : %d (%s)\n", filename, ret, strerror(errno)); if (ret < 0) return -1; ret = glfs_lsetxattr(fs, filename, "user.testkey3", "testval", 8, 0); fprintf(stderr, "lsetxattr(%s) : %d (%s)\n", linkfile, ret, strerror(errno)); if (ret < 0) return -1; ret = glfs_llistxattr(fs, linkfile, buf, 512); fprintf(stderr, "llistxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); if (ret < 0) return -1; ret = glfs_lgetxattr(fs, filename, "user.testkey3", buf, 512); fprintf(stderr, "lgetxattr(%s): %d (%s)\n", linkfile, ret, strerror(errno)); if (ret < 0) return -1; for (ptr = buf; ptr < buf + ret; ptr++) { printf("key=%s\n", ptr); ptr += strlen(ptr); } ret = glfs_removexattr(fs, filename, "user.testkey2"); fprintf(stderr, "removexattr(%s): %d (%s)\n", filename, ret, strerror(errno)); fd = glfs_open(fs, filename, O_RDWR); fprintf(stderr, "open(%s): (%p) %s\n", filename, fd, strerror(errno)); ret = glfs_fsetxattr(fd, "user.testkey2", "testval", 8, 0); fprintf(stderr, "fsetxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); ret = glfs_fgetxattr(fd, "user.testkey2", buf, 512); fprintf(stderr, "fgetxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); ret = glfs_flistxattr(fd, buf, 512); fprintf(stderr, "flistxattr(%s): %d (%s)\n", filename, ret, strerror(errno)); if (ret < 0) return -1; for (ptr = buf; ptr < buf + ret; ptr++) { printf("key=%s\n", ptr); ptr += strlen(ptr); } ret = glfs_fremovexattr(fd, "user.testkey2"); fprintf(stderr, "fremovexattr(%s): %d (%s)\n", filename, ret, strerror(errno)); glfs_close(fd); return 0; } int test_chdir(glfs_t *fs) { int ret = -1; char *dir = "/dir"; char *topdir = "/topdir"; char *linkdir = "/linkdir"; char *linkdir2 = "/linkdir2"; char *subdir = "./subdir"; char *respath = NULL; char pathbuf[4096]; ret = glfs_mkdir(fs, topdir, 0755); fprintf(stderr, "mkdir(%s): %s\n", topdir, strerror(errno)); if (ret) return -1; ret = glfs_mkdir(fs, dir, 0755); fprintf(stderr, "mkdir(%s): %s\n", dir, strerror(errno)); if (ret) return -1; respath = glfs_getcwd(fs, pathbuf, 4096); fprintf(stdout, "getcwd() = %s\n", respath); ret = glfs_symlink(fs, "topdir", linkdir); if (ret) { fprintf(stderr, "symlink(%s, %s): %s\n", topdir, linkdir, strerror(errno)); return -1; } ret = glfs_chdir(fs, linkdir); if (ret) { fprintf(stderr, "chdir(%s): %s\n", linkdir, strerror(errno)); return -1; } respath = glfs_getcwd(fs, pathbuf, 4096); fprintf(stdout, "getcwd() = %s\n", respath); respath = glfs_realpath(fs, subdir, pathbuf); if (respath) { fprintf(stderr, "realpath(%s) worked unexpectedly: %s\n", subdir, respath); return -1; } ret = glfs_mkdir(fs, subdir, 0755); if (ret) { fprintf(stderr, "mkdir(%s): %s\n", subdir, strerror(errno)); return -1; } respath = glfs_realpath(fs, subdir, pathbuf); if (!respath) { fprintf(stderr, "realpath(%s): %s\n", subdir, strerror(errno)); } else { fprintf(stdout, "realpath(%s) = %s\n", subdir, respath); } ret = glfs_chdir(fs, subdir); if (ret) { fprintf(stderr, "chdir(%s): %s\n", subdir, strerror(errno)); return -1; } respath = glfs_getcwd(fs, pathbuf, 4096); fprintf(stdout, "getcwd() = %s\n", respath); respath = glfs_realpath(fs, "/linkdir/subdir", pathbuf); if (!respath) { fprintf(stderr, "realpath(/linkdir/subdir): %s\n", strerror(errno)); } else { fprintf(stdout, "realpath(/linkdir/subdir) = %s\n", respath); } return 0; } #ifdef DEBUG static void peek_stat(struct stat *sb) { printf("Dumping stat information:\n"); printf("File type: "); switch (sb->st_mode & S_IFMT) { case S_IFBLK: printf("block device\n"); break; case S_IFCHR: printf("character device\n"); break; case S_IFDIR: printf("directory\n"); break; case S_IFIFO: printf("FIFO/pipe\n"); break; case S_IFLNK: printf("symlink\n"); break; case S_IFREG: printf("regular file\n"); break; case S_IFSOCK: printf("socket\n"); break; default: printf("unknown?\n"); break; } printf("I-node number: %ld\n", (long)sb->st_ino); printf("Mode: %lo (octal)\n", (unsigned long)sb->st_mode); printf("Link count: %ld\n", (long)sb->st_nlink); printf("Ownership: UID=%ld GID=%ld\n", (long)sb->st_uid, (long)sb->st_gid); printf("Preferred I/O block size: %ld bytes\n", (long)sb->st_blksize); printf("File size: %lld bytes\n", (long long)sb->st_size); printf("Blocks allocated: %lld\n", (long long)sb->st_blocks); printf("Last status change: %s", ctime(&sb->st_ctime)); printf("Last file access: %s", ctime(&sb->st_atime)); printf("Last file modification: %s", ctime(&sb->st_mtime)); return; } static void peek_handle(unsigned char *glid) { int i; for (i = 0; i < GFAPI_HANDLE_LENGTH; i++) { printf(":%02x:", glid[i]); } printf("\n"); } #else /* DEBUG */ static void peek_stat(struct stat *sb) { return; } static void peek_handle(unsigned char *id) { return; } #endif /* DEBUG */ glfs_t *fs = NULL; char *full_parent_name = "/testdir", *parent_name = "testdir"; void test_h_unlink(void) { char *my_dir = "unlinkdir"; char *my_file = "file.txt"; char *my_subdir = "dir1"; struct glfs_object *parent = NULL, *leaf = NULL, *dir = NULL, *subdir = NULL, *subleaf = NULL; struct stat sb; int ret; printf("glfs_h_unlink tests: In Progress\n"); /* Prepare tests */ parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); if (parent == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_parent_name, NULL, strerror(errno)); printf("glfs_h_lookupat tests: FAILED\n"); goto out; } peek_stat(&sb); dir = glfs_h_mkdir(fs, parent, my_dir, 0755, &sb); if (dir == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", my_dir, parent, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } leaf = glfs_h_creat(fs, dir, my_file, O_CREAT, 0644, &sb); if (leaf == NULL) { fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", my_file, dir, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } subdir = glfs_h_mkdir(fs, dir, my_subdir, 0755, &sb); if (subdir == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", my_subdir, dir, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } subleaf = glfs_h_creat(fs, subdir, my_file, O_CREAT, 0644, &sb); if (subleaf == NULL) { fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", my_file, subdir, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } /* unlink non empty directory */ ret = glfs_h_unlink(fs, dir, my_subdir); if ((ret && errno != ENOTEMPTY) || (ret == 0)) { fprintf(stderr, "glfs_h_unlink: error unlinking %s: it is non empty: %s\n", my_subdir, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } /* unlink regular file */ ret = glfs_h_unlink(fs, subdir, my_file); if (ret) { fprintf(stderr, "glfs_h_unlink: error unlinking %s: from (%p),%s\n", my_file, subdir, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } /* unlink directory */ ret = glfs_h_unlink(fs, dir, my_subdir); if (ret) { fprintf(stderr, "glfs_h_unlink: error unlinking %s: from (%p),%s\n", my_subdir, dir, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } /* unlink regular file */ ret = glfs_h_unlink(fs, dir, my_file); if (ret) { fprintf(stderr, "glfs_h_unlink: error unlinking %s: from (%p),%s\n", my_file, dir, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } /* unlink non-existent regular file */ ret = glfs_h_unlink(fs, dir, my_file); if ((ret && errno != ENOENT) || (ret == 0)) { fprintf(stderr, "glfs_h_unlink: error unlinking non-existent %s: invalid errno " ",%d, %s\n", my_file, ret, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } /* unlink non-existent directory */ ret = glfs_h_unlink(fs, dir, my_subdir); if ((ret && errno != ENOENT) || (ret == 0)) { fprintf(stderr, "glfs_h_unlink: error unlinking non-existent %s: invalid " "errno ,%d, %s\n", my_subdir, ret, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } /* unlink directory */ ret = glfs_h_unlink(fs, parent, my_dir); if (ret) { fprintf(stderr, "glfs_h_unlink: error unlinking %s: from (%p),%s\n", my_dir, dir, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } printf("glfs_h_unlink tests: PASSED\n"); out: if (dir) glfs_h_close(dir); if (leaf) glfs_h_close(leaf); if (subdir) glfs_h_close(subdir); if (subleaf) glfs_h_close(subleaf); if (parent) glfs_h_close(parent); return; } void test_h_getsetattrs(void) { char *my_dir = "attrdir"; char *my_file = "attrfile.txt"; struct glfs_object *parent = NULL, *leaf = NULL, *dir = NULL; struct stat sb, retsb; int ret, valid; struct timespec timestamp; printf("glfs_h_getattrs and setattrs tests: In Progress\n"); /* Prepare tests */ parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); if (parent == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_parent_name, NULL, strerror(errno)); printf("glfs_h_lookupat tests: FAILED\n"); goto out; } peek_stat(&sb); dir = glfs_h_mkdir(fs, parent, my_dir, 0755, &sb); if (dir == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", my_dir, parent, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } peek_stat(&sb); leaf = glfs_h_creat(fs, dir, my_file, O_CREAT, 0644, &sb); if (leaf == NULL) { fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", my_file, dir, strerror(errno)); printf("glfs_h_unlink tests: FAILED\n"); goto out; } peek_stat(&sb); ret = glfs_h_getattrs(fs, dir, &retsb); if (ret != 0) { fprintf(stderr, "glfs_h_getattrs: error %s: from (%p),%s\n", my_dir, dir, strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } peek_stat(&retsb); /* TODO: Compare stat information */ retsb.st_mode = 00666; retsb.st_uid = 1000; retsb.st_gid = 1001; ret = clock_gettime(CLOCK_REALTIME, ×tamp); if (ret != 0) { fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } retsb.st_atim = timestamp; retsb.st_mtim = timestamp; valid = GFAPI_SET_ATTR_MODE | GFAPI_SET_ATTR_UID | GFAPI_SET_ATTR_GID | GFAPI_SET_ATTR_ATIME | GFAPI_SET_ATTR_MTIME; peek_stat(&retsb); ret = glfs_h_setattrs(fs, dir, &retsb, valid); if (ret != 0) { fprintf(stderr, "glfs_h_setattrs: error %s: from (%p),%s\n", my_dir, dir, strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } memset(&retsb, 0, sizeof(struct stat)); ret = glfs_h_stat(fs, dir, &retsb); if (ret != 0) { fprintf(stderr, "glfs_h_stat: error %s: from (%p),%s\n", my_dir, dir, strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } peek_stat(&retsb); printf("glfs_h_getattrs and setattrs tests: PASSED\n"); out: if (parent) glfs_h_close(parent); if (leaf) glfs_h_close(leaf); if (dir) glfs_h_close(dir); return; } void test_h_truncate(void) { char *my_dir = "truncatedir"; char *my_file = "file.txt"; struct glfs_object *root = NULL, *parent = NULL, *leaf = NULL; struct stat sb; glfs_fd_t *fd = NULL; char buf[32]; off_t offset = 0; int ret = 0; printf("glfs_h_truncate tests: In Progress\n"); /* Prepare tests */ root = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); if (root == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_parent_name, NULL, strerror(errno)); printf("glfs_h_truncate tests: FAILED\n"); goto out; } peek_stat(&sb); parent = glfs_h_mkdir(fs, root, my_dir, 0755, &sb); if (parent == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", my_dir, root, strerror(errno)); printf("glfs_h_truncate tests: FAILED\n"); goto out; } peek_stat(&sb); leaf = glfs_h_creat(fs, parent, my_file, O_CREAT, 0644, &sb); if (leaf == NULL) { fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", my_file, parent, strerror(errno)); printf("glfs_h_truncate tests: FAILED\n"); goto out; } peek_stat(&sb); fd = glfs_h_open(fs, leaf, O_RDWR); if (fd == NULL) { fprintf(stderr, "glfs_h_open: error on open of %s: %s\n", my_file, strerror(errno)); printf("glfs_h_truncate tests: FAILED\n"); goto out; } memcpy(buf, "abcdefghijklmnopqrstuvwxyz012345", 32); ret = glfs_write(fd, buf, 32, 0); /* run tests */ /* truncate lower */ offset = 30; ret = glfs_h_truncate(fs, leaf, offset); if (ret != 0) { fprintf(stderr, "glfs_h_truncate: error creating %s: from (%p),%s\n", my_file, parent, strerror(errno)); printf("glfs_h_truncate tests: FAILED\n"); goto out; } ret = glfs_h_getattrs(fs, leaf, &sb); if (ret != 0) { fprintf(stderr, "glfs_h_getattrs: error for %s (%p),%s\n", my_file, leaf, strerror(errno)); printf("glfs_h_truncate tests: FAILED\n"); goto out; } if (sb.st_size != offset) { fprintf(stderr, "glfs_h_truncate: post size mismatch\n"); printf("glfs_h_truncate tests: FAILED\n"); goto out; } /* truncate higher */ offset = 32; ret = glfs_h_truncate(fs, leaf, offset); if (ret != 0) { fprintf(stderr, "glfs_h_truncate: error creating %s: from (%p),%s\n", my_file, parent, strerror(errno)); printf("glfs_h_truncate tests: FAILED\n"); goto out; } ret = glfs_h_getattrs(fs, leaf, &sb); if (ret != 0) { fprintf(stderr, "glfs_h_getattrs: error for %s (%p),%s\n", my_file, leaf, strerror(errno)); printf("glfs_h_truncate tests: FAILED\n"); goto out; } if (sb.st_size != offset) { fprintf(stderr, "glfs_h_truncate: post size mismatch\n"); printf("glfs_h_truncate tests: FAILED\n"); goto out; } /* truncate equal */ offset = 30; ret = glfs_h_truncate(fs, leaf, offset); if (ret != 0) { fprintf(stderr, "glfs_h_truncate: error creating %s: from (%p),%s\n", my_file, parent, strerror(errno)); printf("glfs_h_truncate tests: FAILED\n"); goto out; } ret = glfs_h_getattrs(fs, leaf, &sb); if (ret != 0) { fprintf(stderr, "glfs_h_getattrs: error for %s (%p),%s\n", my_file, leaf, strerror(errno)); printf("glfs_h_truncate tests: FAILED\n"); goto out; } if (sb.st_size != offset) { fprintf(stderr, "glfs_h_truncate: post size mismatch\n"); printf("glfs_h_truncate tests: FAILED\n"); goto out; } printf("glfs_h_truncate tests: PASSED\n"); out: if (fd) glfs_close(fd); if (root) glfs_h_close(root); if (parent) glfs_h_close(parent); if (leaf) glfs_h_close(leaf); return; } void test_h_links(void) { char *my_dir = "linkdir"; char *my_file = "file.txt"; char *my_symlnk = "slnk.txt"; char *my_lnk = "lnk.txt"; char *linksrc_dir = "dir1"; char *linktgt_dir = "dir2"; struct glfs_object *root = NULL, *parent = NULL, *leaf = NULL, *dirsrc = NULL, *dirtgt = NULL, *dleaf = NULL; struct glfs_object *ln1 = NULL; struct stat sb; int ret; char *buf = NULL; printf("glfs_h_link(s) tests: In Progress\n"); /* Prepare tests */ root = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); if (root == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_parent_name, NULL, strerror(errno)); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } peek_stat(&sb); parent = glfs_h_mkdir(fs, root, my_dir, 0755, &sb); if (parent == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", my_dir, root, strerror(errno)); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } peek_stat(&sb); leaf = glfs_h_creat(fs, parent, my_file, O_CREAT, 0644, &sb); if (leaf == NULL) { fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", my_file, parent, strerror(errno)); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } peek_stat(&sb); dirsrc = glfs_h_mkdir(fs, parent, linksrc_dir, 0755, &sb); if (dirsrc == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", linksrc_dir, parent, strerror(errno)); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } peek_stat(&sb); dirtgt = glfs_h_mkdir(fs, parent, linktgt_dir, 0755, &sb); if (dirtgt == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", linktgt_dir, parent, strerror(errno)); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } peek_stat(&sb); dleaf = glfs_h_creat(fs, dirsrc, my_file, O_CREAT, 0644, &sb); if (dleaf == NULL) { fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", my_file, dirsrc, strerror(errno)); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } peek_stat(&sb); /* run tests */ /* sym link: /testdir/linkdir/file.txt to ./slnk.txt */ ln1 = glfs_h_symlink(fs, parent, my_symlnk, "./file.txt", &sb); if (ln1 == NULL) { fprintf(stderr, "glfs_h_symlink: error creating %s: from (%p),%s\n", my_symlnk, parent, strerror(errno)); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } peek_stat(&sb); buf = calloc(1024, sizeof(char)); if (buf == NULL) { fprintf(stderr, "Error allocating memory\n"); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } ret = glfs_h_readlink(fs, ln1, buf, 1024); if (ret <= 0) { fprintf(stderr, "glfs_h_readlink: error reading %s: from (%p),%s\n", my_symlnk, ln1, strerror(errno)); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } if (!(strncmp(buf, my_symlnk, strlen(my_symlnk)))) { fprintf(stderr, "glfs_h_readlink: error mismatch in link name: actual %s: " "retrieved %s\n", my_symlnk, buf); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } /* link: /testdir/linkdir/file.txt to ./lnk.txt */ ret = glfs_h_link(fs, leaf, parent, my_lnk); if (ret != 0) { fprintf(stderr, "glfs_h_link: error creating %s: from (%p),%s\n", my_lnk, parent, strerror(errno)); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } /* TODO: Should write content to a file and read from the link */ /* link: /testdir/linkdir/dir1/file.txt to ../dir2/slnk.txt */ ret = glfs_h_link(fs, dleaf, dirtgt, my_lnk); if (ret != 0) { fprintf(stderr, "glfs_h_link: error creating %s: from (%p),%s\n", my_lnk, dirtgt, strerror(errno)); printf("glfs_h_link(s) tests: FAILED\n"); goto out; } /* TODO: Should write content to a file and read from the link */ printf("glfs_h_link(s) tests: PASSED\n"); out: if (root) glfs_h_close(root); if (parent) glfs_h_close(parent); if (leaf) glfs_h_close(leaf); if (dirsrc) glfs_h_close(dirsrc); if (dirtgt) glfs_h_close(dirtgt); if (dleaf) glfs_h_close(dleaf); if (ln1) glfs_h_close(ln1); if (buf) free(buf); return; } void test_h_rename(void) { char *my_dir = "renamedir"; char *my_file = "file.txt"; char *src_dir = "dir1"; char *tgt_dir = "dir2"; struct glfs_object *root = NULL, *parent = NULL, *leaf = NULL, *dirsrc = NULL, *dirtgt = NULL, *dleaf = NULL; struct stat sb; int ret; printf("glfs_h_rename tests: In Progress\n"); /* Prepare tests */ root = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); if (root == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_parent_name, NULL, strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } peek_stat(&sb); parent = glfs_h_mkdir(fs, root, my_dir, 0755, &sb); if (parent == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", my_dir, root, strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } peek_stat(&sb); leaf = glfs_h_creat(fs, parent, my_file, O_CREAT, 0644, &sb); if (leaf == NULL) { fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", my_file, parent, strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } peek_stat(&sb); dirsrc = glfs_h_mkdir(fs, parent, src_dir, 0755, &sb); if (dirsrc == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", src_dir, parent, strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } peek_stat(&sb); dirtgt = glfs_h_mkdir(fs, parent, tgt_dir, 0755, &sb); if (dirtgt == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", tgt_dir, parent, strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } peek_stat(&sb); dleaf = glfs_h_creat(fs, dirsrc, my_file, O_CREAT, 0644, &sb); if (dleaf == NULL) { fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", my_file, dirsrc, strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } peek_stat(&sb); /* run tests */ /* Rename file.txt -> file1.txt */ ret = glfs_h_rename(fs, parent, "file.txt", parent, "file1.txt"); if (ret != 0) { fprintf(stderr, "glfs_h_rename: error renaming %s to %s (%s)\n", "file.txt", "file1.txt", strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } /* rename dir1/file.txt -> file.txt */ ret = glfs_h_rename(fs, dirsrc, "file.txt", parent, "file.txt"); if (ret != 0) { fprintf(stderr, "glfs_h_rename: error renaming %s/%s to %s (%s)\n", src_dir, "file.txt", "file.txt", strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } /* rename file1.txt -> file.txt (exists) */ ret = glfs_h_rename(fs, parent, "file1.txt", parent, "file.txt"); if (ret != 0) { fprintf(stderr, "glfs_h_rename: error renaming %s to %s (%s)\n", "file.txt", "file.txt", strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } /* rename dir1 -> dir3 */ ret = glfs_h_rename(fs, parent, "dir1", parent, "dir3"); if (ret != 0) { fprintf(stderr, "glfs_h_rename: error renaming %s to %s (%s)\n", "dir1", "dir3", strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } /* rename dir2 ->dir3 (exists) */ ret = glfs_h_rename(fs, parent, "dir2", parent, "dir3"); if (ret != 0) { fprintf(stderr, "glfs_h_rename: error renaming %s to %s (%s)\n", "dir2", "dir3", strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } /* rename file.txt -> dir3 (fail) */ ret = glfs_h_rename(fs, parent, "file.txt", parent, "dir3"); if (ret == 0) { fprintf(stderr, "glfs_h_rename: NO error renaming %s to %s (%s)\n", "file.txt", "dir3", strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } /* rename dir3 -> file.txt (fail) */ ret = glfs_h_rename(fs, parent, "dir3", parent, "file.txt"); if (ret == 0) { fprintf(stderr, "glfs_h_rename: NO error renaming %s to %s (%s)\n", "dir3", "file.txt", strerror(errno)); printf("glfs_h_rename tests: FAILED\n"); goto out; } printf("glfs_h_rename tests: PASSED\n"); out: if (root) glfs_h_close(root); if (parent) glfs_h_close(parent); if (leaf) glfs_h_close(leaf); if (dirsrc) glfs_h_close(dirsrc); if (dirtgt) glfs_h_close(dirtgt); if (dleaf) glfs_h_close(dleaf); return; } void assimilatetime(struct timespec *ts, struct timespec ts_st, struct timespec ts_ed) { if ((ts_ed.tv_nsec - ts_st.tv_nsec) < 0) { ts->tv_sec += ts_ed.tv_sec - ts_st.tv_sec - 1; ts->tv_nsec += 1000000000 + ts_ed.tv_nsec - ts_st.tv_nsec; } else { ts->tv_sec += ts_ed.tv_sec - ts_st.tv_sec; ts->tv_nsec += ts_ed.tv_nsec - ts_st.tv_nsec; } if (ts->tv_nsec > 1000000000) { ts->tv_nsec = ts->tv_nsec - 1000000000; ts->tv_sec += 1; } return; } #define MAX_FILES_CREATE 10 #define MAXPATHNAME 512 void test_h_performance(void) { char *my_dir = "perftest", *full_dir_path = "/testdir/perftest"; char *my_file = "file_", my_file_name[MAXPATHNAME]; struct glfs_object *parent = NULL, *leaf = NULL, *dir = NULL; struct stat sb; int ret, i; struct glfs_fd *fd; struct timespec c_ts = {0, 0}, c_ts_st, c_ts_ed; struct timespec o_ts = {0, 0}, o_ts_st, o_ts_ed; printf("glfs_h_performance tests: In Progress\n"); /* Prepare tests */ parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); if (parent == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_parent_name, NULL, strerror(errno)); printf("glfs_h_performance tests: FAILED\n"); goto out; } dir = glfs_h_mkdir(fs, parent, my_dir, 0755, &sb); if (dir == NULL) { fprintf(stderr, "glfs_h_mkdir: error creating %s: from (%p),%s\n", my_dir, parent, strerror(errno)); printf("glfs_h_performance tests: FAILED\n"); goto out; } peek_stat(&sb); /* create performance */ ret = clock_gettime(CLOCK_REALTIME, &o_ts_st); if (ret != 0) { fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } for (i = 0; i < MAX_FILES_CREATE; i++) { sprintf(my_file_name, "%s%d", my_file, i); ret = clock_gettime(CLOCK_REALTIME, &c_ts_st); if (ret != 0) { fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } leaf = glfs_h_lookupat(fs, dir, my_file_name, &sb, 0); if (leaf != NULL) { fprintf(stderr, "glfs_h_lookup: exists %s\n", my_file_name); printf("glfs_h_performance tests: FAILED\n"); goto out; } leaf = glfs_h_creat(fs, dir, my_file_name, O_CREAT, 0644, &sb); if (leaf == NULL) { fprintf(stderr, "glfs_h_creat: error creating %s: from (%p),%s\n", my_file, dir, strerror(errno)); printf("glfs_h_performance tests: FAILED\n"); goto out; } ret = clock_gettime(CLOCK_REALTIME, &c_ts_ed); if (ret != 0) { fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } assimilatetime(&c_ts, c_ts_st, c_ts_ed); glfs_h_close(leaf); leaf = NULL; } ret = clock_gettime(CLOCK_REALTIME, &o_ts_ed); if (ret != 0) { fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } assimilatetime(&o_ts, o_ts_st, o_ts_ed); printf("Creation performance (handle based):\n\t# empty files:%d\n", MAX_FILES_CREATE); printf("\tOverall time:\n\t\tSecs:%ld\n\t\tnSecs:%ld\n", o_ts.tv_sec, o_ts.tv_nsec); printf("\tcreate call time time:\n\t\tSecs:%ld\n\t\tnSecs:%ld\n", c_ts.tv_sec, c_ts.tv_nsec); /* create using path */ c_ts.tv_sec = o_ts.tv_sec = 0; c_ts.tv_nsec = o_ts.tv_nsec = 0; sprintf(my_file_name, "%s1", full_dir_path); ret = glfs_mkdir(fs, my_file_name, 0755); if (ret != 0) { fprintf(stderr, "glfs_mkdir: error creating %s: from (%p),%s\n", my_dir, parent, strerror(errno)); printf("glfs_h_performance tests: FAILED\n"); goto out; } peek_stat(&sb); ret = clock_gettime(CLOCK_REALTIME, &o_ts_st); if (ret != 0) { fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } for (i = 0; i < MAX_FILES_CREATE; i++) { sprintf(my_file_name, "%s1/%sn%d", full_dir_path, my_file, i); ret = clock_gettime(CLOCK_REALTIME, &c_ts_st); if (ret != 0) { fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } ret = glfs_stat(fs, my_file_name, &sb); if (ret == 0) { fprintf(stderr, "glfs_stat: exists %s\n", my_file_name); printf("glfs_h_performance tests: FAILED\n"); goto out; } fd = glfs_creat(fs, my_file_name, O_CREAT, 0644); if (fd == NULL) { fprintf(stderr, "glfs_creat: error creating %s: from (%p),%s\n", my_file, dir, strerror(errno)); printf("glfs_h_performance tests: FAILED\n"); goto out; } ret = clock_gettime(CLOCK_REALTIME, &c_ts_ed); if (ret != 0) { fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } assimilatetime(&c_ts, c_ts_st, c_ts_ed); glfs_close(fd); } ret = clock_gettime(CLOCK_REALTIME, &o_ts_ed); if (ret != 0) { fprintf(stderr, "clock_gettime: error %s\n", strerror(errno)); printf("glfs_h_getattrs and setattrs tests: FAILED\n"); goto out; } assimilatetime(&o_ts, o_ts_st, o_ts_ed); printf("Creation performance (path based):\n\t# empty files:%d\n", MAX_FILES_CREATE); printf("\tOverall time:\n\t\tSecs:%ld\n\t\tnSecs:%ld\n", o_ts.tv_sec, o_ts.tv_nsec); printf("\tcreate call time time:\n\t\tSecs:%ld\n\t\tnSecs:%ld\n", c_ts.tv_sec, c_ts.tv_nsec); out: return; } int test_handleops(int argc, char *argv[]) { int ret = 0; glfs_fd_t *fd = NULL; struct stat sb = { 0, }; struct glfs_object *root = NULL, *parent = NULL, *leaf = NULL, *tmp = NULL; char readbuf[32], writebuf[32]; unsigned char leaf_handle[GFAPI_HANDLE_LENGTH]; char *full_leaf_name = "/testdir/testfile.txt", *leaf_name = "testfile.txt", *relative_leaf_name = "testdir/testfile.txt"; char *leaf_name1 = "testfile1.txt"; char *full_newparent_name = "/testdir/dir1", *newparent_name = "dir1"; char *full_newnod_name = "/testdir/nod1", *newnod_name = "nod1"; /* Initialize test area */ ret = glfs_mkdir(fs, full_parent_name, 0755); if (ret != 0 && errno != EEXIST) { fprintf(stderr, "%s: (%p) %s\n", full_parent_name, fd, strerror(errno)); printf("Test initialization failed on volume %s\n", argv[1]); goto out; } else if (ret != 0) { printf("Found test directory %s to be existing\n", full_parent_name); printf("Cleanup test directory and restart tests\n"); goto out; } fd = glfs_creat(fs, full_leaf_name, O_CREAT, 0644); if (fd == NULL) { fprintf(stderr, "%s: (%p) %s\n", full_leaf_name, fd, strerror(errno)); printf("Test initialization failed on volume %s\n", argv[1]); goto out; } glfs_close(fd); printf("Initialized the test area, within volume %s\n", argv[1]); /* Handle based APIs test area */ /* glfs_lookupat test */ printf("glfs_h_lookupat tests: In Progress\n"); /* start at root of the volume */ root = glfs_h_lookupat(fs, NULL, "/", &sb, 0); if (root == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", "/", NULL, strerror(errno)); printf("glfs_h_lookupat tests: FAILED\n"); goto out; } peek_stat(&sb); /* lookup a parent within root */ parent = glfs_h_lookupat(fs, root, parent_name, &sb, 0); if (parent == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", parent_name, root, strerror(errno)); printf("glfs_h_lookupat tests: FAILED\n"); goto out; } peek_stat(&sb); /* lookup a leaf/child within the parent */ leaf = glfs_h_lookupat(fs, parent, leaf_name, &sb, 0); if (leaf == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", leaf_name, parent, strerror(errno)); printf("glfs_h_lookupat tests: FAILED\n"); goto out; } peek_stat(&sb); /* reset */ glfs_h_close(root); root = NULL; glfs_h_close(leaf); leaf = NULL; glfs_h_close(parent); parent = NULL; /* check absolute paths */ root = glfs_h_lookupat(fs, NULL, "/", &sb, 0); if (root == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", "/", NULL, strerror(errno)); printf("glfs_h_lookupat tests: FAILED\n"); goto out; } peek_stat(&sb); parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); if (parent == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_parent_name, root, strerror(errno)); printf("glfs_h_lookupat tests: FAILED\n"); goto out; } peek_stat(&sb); leaf = glfs_h_lookupat(fs, NULL, full_leaf_name, &sb, 0); if (leaf == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_leaf_name, parent, strerror(errno)); printf("glfs_h_lookupat tests: FAILED\n"); goto out; } peek_stat(&sb); /* reset */ glfs_h_close(leaf); leaf = NULL; /* check multiple component paths */ leaf = glfs_h_lookupat(fs, root, relative_leaf_name, &sb, 0); if (leaf == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", relative_leaf_name, parent, strerror(errno)); goto out; } peek_stat(&sb); /* reset */ glfs_h_close(root); root = NULL; glfs_h_close(parent); parent = NULL; /* check symlinks in path */ /* TODO: -ve test cases */ /* parent invalid * path invalid * path does not exist after some components * no parent, but relative path * parent and full path? -ve? */ printf("glfs_h_lookupat tests: PASSED\n"); /* glfs_openat test */ printf("glfs_h_open tests: In Progress\n"); fd = glfs_h_open(fs, leaf, O_RDWR); if (fd == NULL) { fprintf(stderr, "glfs_h_open: error on open of %s: %s\n", full_leaf_name, strerror(errno)); printf("glfs_h_open tests: FAILED\n"); goto out; } /* test read/write based on fd */ memcpy(writebuf, "abcdefghijklmnopqrstuvwxyz012345", 32); ret = glfs_write(fd, writebuf, 32, 0); glfs_lseek(fd, 0, SEEK_SET); ret = glfs_read(fd, readbuf, 32, 0); if (memcmp(readbuf, writebuf, 32)) { printf("Failed to read what I wrote: %s %s\n", readbuf, writebuf); glfs_close(fd); printf("glfs_h_open tests: FAILED\n"); goto out; } glfs_h_close(leaf); leaf = NULL; glfs_close(fd); printf("glfs_h_open tests: PASSED\n"); /* Create tests */ printf("glfs_h_creat tests: In Progress\n"); parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); if (parent == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_parent_name, root, strerror(errno)); printf("glfs_h_creat tests: FAILED\n"); goto out; } peek_stat(&sb); leaf = glfs_h_creat(fs, parent, leaf_name1, O_CREAT, 0644, &sb); if (leaf == NULL) { fprintf(stderr, "glfs_h_creat: error on create of %s: from (%p),%s\n", leaf_name1, parent, strerror(errno)); printf("glfs_h_creat tests: FAILED\n"); goto out; } peek_stat(&sb); glfs_h_close(leaf); leaf = NULL; leaf = glfs_h_creat(fs, parent, leaf_name1, O_CREAT | O_EXCL, 0644, &sb); if (leaf != NULL || errno != EEXIST) { fprintf(stderr, "glfs_h_creat: existing file, leaf = (%p), errno = %s\n", leaf, strerror(errno)); printf("glfs_h_creat tests: FAILED\n"); if (leaf != NULL) { glfs_h_close(leaf); leaf = NULL; } } tmp = glfs_h_creat(fs, root, parent_name, O_CREAT, 0644, &sb); if (tmp != NULL || !(errno == EISDIR || errno == EINVAL)) { fprintf(stderr, "glfs_h_creat: dir create, tmp = (%p), errno = %s\n", leaf, strerror(errno)); printf("glfs_h_creat tests: FAILED\n"); if (tmp != NULL) { glfs_h_close(tmp); tmp = NULL; } } /* TODO: Other combinations and -ve cases as applicable */ printf("glfs_h_creat tests: PASSED\n"); /* extract handle and create from handle test */ printf( "glfs_h_extract_handle and glfs_h_create_from_handle tests: In " "Progress\n"); /* TODO: Change the lookup to create below for a GIFD recovery failure, * that needs to be fixed */ leaf = glfs_h_lookupat(fs, parent, leaf_name1, &sb, 0); if (leaf == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", leaf_name1, parent, strerror(errno)); printf("glfs_h_extract_handle tests: FAILED\n"); goto out; } peek_stat(&sb); ret = glfs_h_extract_handle(leaf, leaf_handle, GFAPI_HANDLE_LENGTH); if (ret < 0) { fprintf(stderr, "glfs_h_extract_handle: error extracting handle of %s: %s\n", full_leaf_name, strerror(errno)); printf("glfs_h_extract_handle tests: FAILED\n"); goto out; } peek_handle(leaf_handle); glfs_h_close(leaf); leaf = NULL; leaf = glfs_h_create_from_handle(fs, leaf_handle, GFAPI_HANDLE_LENGTH, &sb); if (leaf == NULL) { fprintf( stderr, "glfs_h_create_from_handle: error on create of %s: from (%p),%s\n", leaf_name1, leaf_handle, strerror(errno)); printf("glfs_h_create_from_handle tests: FAILED\n"); goto out; } peek_stat(&sb); fd = glfs_h_open(fs, leaf, O_RDWR); if (fd == NULL) { fprintf(stderr, "glfs_h_open: error on open of %s: %s\n", full_leaf_name, strerror(errno)); printf("glfs_h_create_from_handle tests: FAILED\n"); goto out; } /* test read/write based on fd */ memcpy(writebuf, "abcdefghijklmnopqrstuvwxyz012345", 32); ret = glfs_write(fd, writebuf, 32, 0); glfs_lseek(fd, 0, SEEK_SET); ret = glfs_read(fd, readbuf, 32, 0); if (memcmp(readbuf, writebuf, 32)) { printf("Failed to read what I wrote: %s %s\n", writebuf, writebuf); printf("glfs_h_create_from_handle tests: FAILED\n"); glfs_close(fd); goto out; } glfs_close(fd); glfs_h_close(leaf); leaf = NULL; glfs_h_close(parent); parent = NULL; printf( "glfs_h_extract_handle and glfs_h_create_from_handle tests: PASSED\n"); /* Mkdir tests */ printf("glfs_h_mkdir tests: In Progress\n"); ret = glfs_rmdir(fs, full_newparent_name); if (ret && errno != ENOENT) { fprintf(stderr, "glfs_rmdir: Failed for %s: %s\n", full_newparent_name, strerror(errno)); printf("glfs_h_mkdir tests: FAILED\n"); goto out; } parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); if (parent == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_parent_name, root, strerror(errno)); printf("glfs_h_mkdir tests: FAILED\n"); goto out; } peek_stat(&sb); leaf = glfs_h_mkdir(fs, parent, newparent_name, 0755, &sb); if (leaf == NULL) { fprintf(stderr, "glfs_h_mkdir: error on mkdir of %s: from (%p),%s\n", newparent_name, parent, strerror(errno)); printf("glfs_h_mkdir tests: FAILED\n"); goto out; } peek_stat(&sb); glfs_h_close(leaf); leaf = NULL; leaf = glfs_h_mkdir(fs, parent, newparent_name, 0755, &sb); if (leaf != NULL || errno != EEXIST) { fprintf(stderr, "glfs_h_mkdir: existing directory, leaf = (%p), errno = %s\n", leaf, strerror(errno)); printf("glfs_h_mkdir tests: FAILED\n"); if (leaf != NULL) { glfs_h_close(leaf); leaf = NULL; } } glfs_h_close(parent); parent = NULL; printf("glfs_h_mkdir tests: PASSED\n"); /* Mknod tests */ printf("glfs_h_mknod tests: In Progress\n"); ret = glfs_unlink(fs, full_newnod_name); if (ret && errno != ENOENT) { fprintf(stderr, "glfs_unlink: Failed for %s: %s\n", full_newnod_name, strerror(errno)); printf("glfs_h_mknod tests: FAILED\n"); goto out; } parent = glfs_h_lookupat(fs, NULL, full_parent_name, &sb, 0); if (parent == NULL) { fprintf(stderr, "glfs_h_lookupat: error on lookup of %s: from (%p),%s\n", full_parent_name, root, strerror(errno)); printf("glfs_h_mknod tests: FAILED\n"); goto out; } peek_stat(&sb); leaf = glfs_h_mknod(fs, parent, newnod_name, S_IFIFO, 0, &sb); if (leaf == NULL) { fprintf(stderr, "glfs_h_mkdir: error on mkdir of %s: from (%p),%s\n", newnod_name, parent, strerror(errno)); printf("glfs_h_mknod tests: FAILED\n"); goto out; } peek_stat(&sb); /* TODO: create op on a FIFO node hangs, need to check and fix tmp = glfs_h_creat (fs, parent, newnod_name, O_CREAT, 0644, &sb); if (tmp != NULL || errno != EINVAL) { fprintf (stderr, "glfs_h_creat: node create, tmp = (%p), errno = %s\n", tmp, strerror (errno)); printf ("glfs_h_creat/mknod tests: FAILED\n"); if (tmp != NULL) { glfs_h_close(tmp); tmp = NULL; } } */ glfs_h_close(leaf); leaf = NULL; leaf = glfs_h_mknod(fs, parent, newnod_name, 0644, 0, &sb); if (leaf != NULL || errno != EEXIST) { fprintf(stderr, "glfs_h_mknod: existing node, leaf = (%p), errno = %s\n", leaf, strerror(errno)); printf("glfs_h_mknod tests: FAILED\n"); if (leaf != NULL) { glfs_h_close(leaf); leaf = NULL; } } glfs_h_close(parent); parent = NULL; printf("glfs_h_mknod tests: PASSED\n"); /* unlink tests */ test_h_unlink(); /* TODO: opendir tests */ /* getattr tests */ test_h_getsetattrs(); /* TODO: setattr tests */ /* truncate tests */ test_h_truncate(); /* link tests */ test_h_links(); /* rename tests */ test_h_rename(); /* performance tests */ test_h_performance(); /* END: New APIs test area */ out: /* Cleanup glfs handles */ if (root) glfs_h_close(root); if (parent) glfs_h_close(parent); if (leaf) glfs_h_close(leaf); return ret; } int test_write_apis(glfs_t *fs) { /* Add more content here */ /* Some apis we can get are */ /* 0. glfs_set_xlator_option() Read/Write combinations: . glfs_{p,}readv/{p,}writev . glfs_pread/pwrite tests/basic/gfapi/gfapi-async-calls-test.c . glfs_read_async/write_async . glfs_pread_async/pwrite_async . glfs_readv_async/writev_async . glfs_preadv_async/pwritev_async . ftruncate/ftruncate_async . fsync/fsync_async . fdatasync/fdatasync_async */ glfs_fd_t *fd = NULL; char *filename = "/filename2"; int flags = O_RDWR; char *buf = "some bytes!"; char writestr[TEST_STR_LEN]; struct iovec iov = {&writestr, TEST_STR_LEN}; int ret, i; for (i = 0; i < TEST_STR_LEN; i++) writestr[i] = 0x11; fd = glfs_open(fs, filename, flags); if (!fd) fprintf(stderr, "open(%s): (%p) %s\n", filename, fd, strerror(errno)); ret = glfs_writev(fd, &iov, 1, flags); if (ret < 0) { fprintf(stderr, "writev(%s): %d (%s)\n", filename, ret, strerror(errno)); } ret = glfs_pwrite(fd, buf, 10, 4, flags, NULL, NULL); if (ret < 0) { fprintf(stderr, "pwrite(%s): %d (%s)\n", filename, ret, strerror(errno)); } ret = glfs_pwritev(fd, &iov, 1, 4, flags); if (ret < 0) { fprintf(stderr, "pwritev(%s): %d (%s)\n", filename, ret, strerror(errno)); } return 0; } int test_metadata_ops(glfs_t *fs, glfs_t *fs2) { glfs_fd_t *fd = NULL; glfs_fd_t *fd2 = NULL; struct stat sb = { 0, }; struct glfs_stat gsb = { 0, }; struct statvfs sfs; char readbuf[32]; char writebuf[32]; char *filename = "/filename2"; int ret; ret = glfs_lstat(fs, filename, &sb); fprintf(stderr, "lstat(%s): (%d) %s\n", filename, ret, strerror(errno)); fd = glfs_creat(fs, filename, O_RDWR, 0644); fprintf(stderr, "creat(%s): (%p) %s\n", filename, fd, strerror(errno)); fd2 = glfs_open(fs2, filename, O_RDWR); fprintf(stderr, "open(%s): (%p) %s\n", filename, fd, strerror(errno)); glfs_lseek(fd2, 0, SEEK_SET); ret = glfs_read(fd2, readbuf, 32, 0); printf("read %d, %s", ret, readbuf); /* get stat */ ret = glfs_fstat(fd2, &sb); ret = glfs_access(fs, filename, R_OK); /* set stat */ /* TODO: got some errors, need to fix */ /* ret = glfs_fsetattr(fd2, &gsb); */ glfs_close(fd); glfs_close(fd2); filename = "/filename3"; ret = glfs_mknod(fs, filename, S_IFIFO, 0); fprintf(stderr, "%s: (%d) %s\n", filename, ret, strerror(errno)); ret = glfs_lstat(fs, filename, &sb); fprintf(stderr, "%s: (%d) %s\n", filename, ret, strerror(errno)); ret = glfs_rename(fs, filename, "/filename4"); fprintf(stderr, "rename(%s): (%d) %s\n", filename, ret, strerror(errno)); ret = glfs_unlink(fs, "/filename4"); fprintf(stderr, "unlink(%s): (%d) %s\n", "/filename4", ret, strerror(errno)); filename = "/dirname2"; ret = glfs_mkdir(fs, filename, 0); fprintf(stderr, "%s: (%d) %s\n", filename, ret, strerror(errno)); ret = glfs_lstat(fs, filename, &sb); fprintf(stderr, "lstat(%s): (%d) %s\n", filename, ret, strerror(errno)); ret = glfs_rmdir(fs, filename); fprintf(stderr, "rmdir(%s): (%d) %s\n", filename, ret, strerror(errno)); } int main(int argc, char *argv[]) { glfs_t *fs2 = NULL; int ret = 0; glfs_fd_t *fd = NULL; glfs_fd_t *fd2 = NULL; struct stat sb = { 0, }; struct glfs_stat gsb = { 0, }; struct statvfs sfs; char readbuf[32]; char writebuf[32]; char *filename = "/filename2"; if (argc != 3) { printf("Expect following args\n\t%s \n", argv[0]); return -1; } fs = glfs_new(argv[1]); if (!fs) { fprintf(stderr, "glfs_new: returned NULL\n"); return 1; } // ret = glfs_set_volfile (fs, "/tmp/posix.vol"); ret = glfs_set_volfile_server(fs, "tcp", argv[2], 24007); // ret = glfs_set_volfile_server (fs, "unix", "/tmp/gluster.sock", 0); ret = glfs_set_logging(fs, "/dev/stderr", 7); ret = glfs_init(fs); fprintf(stderr, "glfs_init: returned %d\n", ret); if (ret) goto out; sleep(2); fs2 = glfs_new(argv[1]); if (!fs2) { fprintf(stderr, "glfs_new: returned NULL\n"); return 1; } // ret = glfs_set_volfile (fs2, "/tmp/posix.vol"); ret = glfs_set_volfile_server(fs2, "tcp", argv[2], 24007); ret = glfs_set_logging(fs2, "/dev/stderr", 7); ret = glfs_init(fs2); fprintf(stderr, "glfs_init: returned %d\n", ret); test_metadata_ops(fs, fs2); test_dirops(fs); test_xattr(fs); test_chdir(fs); test_handleops(argc, argv); // done /* Test some extra apis */ test_write_apis(fs); glfs_statvfs(fs, "/", &sfs); glfs_fini(fs); glfs_fini(fs2); ret = 0; out: return ret; } glusterfs-11.2/api/src/000077500000000000000000000000001503123251100150045ustar00rootroot00000000000000glusterfs-11.2/api/src/Makefile.am000066400000000000000000000033111503123251100170360ustar00rootroot00000000000000lib_LTLIBRARIES = libgfapi.la noinst_HEADERS = glfs-mem-types.h glfs-internal.h gfapi-messages.h libgfapi_HEADERS = glfs.h glfs-handles.h libgfapidir = $(includedir)/glusterfs/api EXTRA_DIST = gfapi.map gfapi.aliases libgfapi_la_SOURCES = glfs.c glfs-mgmt.c glfs-fops.c glfs-resolve.c \ glfs-handleops.c libgfapi_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ $(top_builddir)/rpc/rpc-lib/src/libgfrpc.la \ $(top_builddir)/rpc/xdr/src/libgfxdr.la libgfapi_la_LDFLAGS = -version-info $(GFAPI_LT_VERSION) $(GF_LDFLAGS) \ $(GFAPI_EXTRA_LDFLAGS) $(ACL_LIBS) libgfapi_la_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ -I$(top_srcdir)/rpc/rpc-lib/src \ -I$(top_srcdir)/rpc/xdr/src \ -I$(top_builddir)/rpc/xdr/src \ -DDATADIR=\"$(localstatedir)\" \ -D__USE_FILE_OFFSET64 -D__USE_LARGEFILE64 AM_CFLAGS = -Wall $(GF_CFLAGS) xlator_LTLIBRARIES = api.la xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/mount # workaround for broken parallel install support in automake with LTLIBRARIES # http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328 install_xlatorLTLIBRARIES = install-xlatorLTLIBRARIES $(install_xlatorLTLIBRARIES): install-libLTLIBRARIES api_la_SOURCES = glfs-primary.c api_la_DEPENDENCIES = libgfapi.la api_la_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ -I$(top_srcdir)/rpc/xdr/src \ -I$(top_builddir)/rpc/xdr/src api_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) #api_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) $(GF_LDFLAGS) api_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ $(top_builddir)/rpc/rpc-lib/src/libgfrpc.la \ $(top_builddir)/rpc/xdr/src/libgfxdr.la \ $(top_builddir)/api/src/libgfapi.la glusterfs-11.2/api/src/README.Symbol_Versions000066400000000000000000000000711503123251100210160ustar00rootroot00000000000000 See ../../doc/developer-guide/gfapi-symbol-versions.md glusterfs-11.2/api/src/gfapi-messages.h000066400000000000000000000211071503123251100200510ustar00rootroot00000000000000/* * Copyright (c) 2015-2018 Red Hat, Inc. * This file is part of GlusterFS. * * This file is licensed to you under your choice of the GNU Lesser * General Public License, version 3 or any later version (LGPLv3 or * later), or the GNU General Public License, version 2 (GPLv2), in all * cases as published by the Free Software Foundation. * */ #ifndef _GFAPI_MESSAGES_H__ #define _GFAPI_MESSAGES_H__ #include /* To add new message IDs, append new identifiers at the end of the list. * * Never remove a message ID. If it's not used anymore, you can rename it or * leave it as it is, but not delete it. This is to prevent reutilization of * IDs by other messages. * * The component name must match one of the entries defined in * glfs-message-id.h. */ GLFS_MSGID(API, API_MSG_MEM_ACCT_INIT_FAILED, API_MSG_PRIMARY_XLATOR_INIT_FAILED, API_MSG_GFAPI_XLATOR_INIT_FAILED, API_MSG_VOLFILE_OPEN_FAILED, API_MSG_VOL_SPEC_FILE_ERROR, API_MSG_GLFS_FSOBJ_NULL, API_MSG_INVALID_ENTRY, API_MSG_FSMUTEX_LOCK_FAILED, API_MSG_COND_WAIT_FAILED, API_MSG_FSMUTEX_UNLOCK_FAILED, API_MSG_INODE_REFRESH_FAILED, API_MSG_GRAPH_CONSTRUCT_FAILED, API_MSG_API_XLATOR_ERROR, API_MSG_XDR_PAYLOAD_FAILED, API_MSG_GET_VOLINFO_CBK_FAILED, API_MSG_FETCH_VOLUUID_FAILED, API_MSG_INSUFF_SIZE, API_MSG_FRAME_CREAT_FAILED, API_MSG_DICT_SET_FAILED, API_MSG_XDR_DECODE_FAILED, API_MSG_GET_VOLFILE_FAILED, API_MSG_WRONG_OPVERSION, API_MSG_DICT_SERIALIZE_FAILED, API_MSG_REMOTE_HOST_CONN_FAILED, API_MSG_VOLFILE_SERVER_EXHAUST, API_MSG_CREATE_RPC_CLIENT_FAILED, API_MSG_REG_NOTIFY_FUNC_FAILED, API_MSG_REG_CBK_FUNC_FAILED, API_MSG_GET_CWD_FAILED, API_MSG_FGETXATTR_FAILED, API_MSG_LOCKINFO_KEY_MISSING, API_MSG_FSETXATTR_FAILED, API_MSG_FSYNC_FAILED, API_MSG_FDCREATE_FAILED, API_MSG_INODE_PATH_FAILED, API_MSG_SYNCOP_OPEN_FAILED, API_MSG_LOCK_MIGRATE_FAILED, API_MSG_OPENFD_SKIPPED, API_MSG_FIRST_LOOKUP_GRAPH_FAILED, API_MSG_CWD_GRAPH_REF_FAILED, API_MSG_SWITCHED_GRAPH, API_MSG_XDR_RESPONSE_DECODE_FAILED, API_MSG_VOLFILE_INFO, API_MSG_VOLFILE_CONNECTING, API_MSG_NEW_GRAPH, API_MSG_ALLOC_FAILED, API_MSG_CREATE_HANDLE_FAILED, API_MSG_INODE_LINK_FAILED, API_MSG_STATEDUMP_FAILED, API_MSG_XREADDIRP_R_FAILED, API_MSG_LOCK_INSERT_MERGE_FAILED, API_MSG_SETTING_LOCK_TYPE_FAILED, API_MSG_INODE_FIND_FAILED, API_MSG_FDCTX_SET_FAILED, API_MSG_UPCALL_SYNCOP_FAILED, API_MSG_INVALID_ARG, API_MSG_UPCALL_EVENT_NULL_RECEIVED, API_MSG_FLAGS_HANDLE, API_MSG_FDCREATE_FAILED_ON_GRAPH, API_MSG_TRANS_RDMA_DEP, API_MSG_TRANS_NOT_SUPPORTED, API_MSG_FS_NOT_INIT, API_MSG_INVALID_SYSRQ, API_MSG_DECODE_XDR_FAILED, API_MSG_NULL, API_MSG_CALL_NOT_SUCCESSFUL, API_MSG_CALL_NOT_VALID, API_MSG_UNABLE_TO_DEL, API_MSG_REMOTE_HOST_DISCONN, API_MSG_HANDLE_NOT_SET); #define API_MSG_ALLOC_FAILED_STR "Upcall allocation failed" #define API_MSG_LOCK_INSERT_MERGE_FAILED_STR \ "Lock insertion and splitting/merging failed" #define API_MSG_SETTING_LOCK_TYPE_FAILED_STR "Setting lock type failed" #define API_MSG_INVALID_ARG_STR "Invalid" #define API_MSG_INVALID_ENTRY_STR "Upcall entry validation failed" #define API_MSG_INODE_FIND_FAILED_STR "Unable to find inode entry" #define API_MSG_CREATE_HANDLE_FAILED_STR "handle creation failed" #define API_MSG_UPCALL_EVENT_NULL_RECEIVED_STR \ "Upcall_EVENT_NULL received. Skipping it" #define API_MSG_UPCALL_SYNCOP_FAILED_STR "Synctask for upcall failed" #define API_MSG_FDCREATE_FAILED_STR "Allocating anonymous fd failed" #define API_MSG_XREADDIRP_R_FAILED_STR "glfs_x_readdirp_r failed" #define API_MSG_FDCTX_SET_FAILED_STR "Setting fd ctx failed" #define API_MSG_FLAGS_HANDLE_STR "arg not set. Flags handled are" #define API_MSG_INODE_REFRESH_FAILED_STR "inode refresh failed" #define API_MSG_INODE_LINK_FAILED_STR "inode linking failed" #define API_MSG_GET_CWD_FAILED_STR "Failed to get cwd" #define API_MSG_FGETXATTR_FAILED_STR "fgetxattr failed" #define API_MSG_LOCKINFO_KEY_MISSING_STR "missing lockinfo key" #define API_MSG_FSYNC_FAILED_STR "fsync() failed" #define API_MSG_FDCREATE_FAILED_ON_GRAPH_STR "fd_create failed on graph" #define API_MSG_INODE_PATH_FAILED_STR "inode_path failed" #define API_MSG_SYNCOP_OPEN_FAILED_STR "syncop_open failed" #define API_MSG_LOCK_MIGRATE_FAILED_STR "lock migration failed on graph" #define API_MSG_OPENFD_SKIPPED_STR "skipping openfd in graph" #define API_MSG_FIRST_LOOKUP_GRAPH_FAILED_STR "first lookup on graph failed" #define API_MSG_CWD_GRAPH_REF_FAILED_STR "cwd refresh of graph failed" #define API_MSG_SWITCHED_GRAPH_STR "switched to graph" #define API_MSG_FSETXATTR_FAILED_STR "fsetxattr failed" #define API_MSG_MEM_ACCT_INIT_FAILED_STR "Memory accounting init failed" #define API_MSG_PRIMARY_XLATOR_INIT_FAILED_STR \ "primary xlator for initialization failed" #define API_MSG_GFAPI_XLATOR_INIT_FAILED_STR \ "failed to initialize gfapi translator" #define API_MSG_VOLFILE_OPEN_FAILED_STR "volume file open failed" #define API_MSG_VOL_SPEC_FILE_ERROR_STR "Cannot reach volume specification file" #define API_MSG_TRANS_RDMA_DEP_STR \ "transport RDMA is deprecated, falling back to tcp" #define API_MSG_TRANS_NOT_SUPPORTED_STR \ "transport is not supported, possible values tcp|unix" #define API_MSG_GLFS_FSOBJ_NULL_STR "fs is NULL" #define API_MSG_FS_NOT_INIT_STR "fs is not properly initialized" #define API_MSG_FSMUTEX_LOCK_FAILED_STR \ "pthread lock on glfs mutex, returned error" #define API_MSG_FSMUTEX_UNLOCK_FAILED_STR \ "pthread unlock on glfs mutex, returned error" #define API_MSG_COND_WAIT_FAILED_STR "cond wait failed" #define API_MSG_INVALID_SYSRQ_STR "not a valid sysrq" #define API_MSG_GRAPH_CONSTRUCT_FAILED_STR "failed to construct the graph" #define API_MSG_API_XLATOR_ERROR_STR \ "api primary xlator cannot be specified in volume file" #define API_MSG_STATEDUMP_FAILED_STR "statedump failed" #define API_MSG_DECODE_XDR_FAILED_STR \ "Failed to decode xdr response for GF_CBK_STATEDUMP" #define API_MSG_NULL_STR "NULL" #define API_MSG_XDR_PAYLOAD_FAILED_STR "failed to create XDR payload" #define API_MSG_CALL_NOT_SUCCESSFUL_STR \ "GET_VOLUME_INFO RPC call is not successful" #define API_MSG_XDR_RESPONSE_DECODE_FAILED_STR \ "Failed to decode xdr response for GET_VOLUME_INFO" #define API_MSG_CALL_NOT_VALID_STR \ "Response received for GET_VOLUME_INFO RPC is not valid" #define API_MSG_GET_VOLINFO_CBK_FAILED_STR \ "In GET_VOLUME_INFO cbk, received error" #define API_MSG_FETCH_VOLUUID_FAILED_STR "Unable to fetch volume UUID" #define API_MSG_INSUFF_SIZE_STR "Insufficient size passed" #define API_MSG_FRAME_CREAT_FAILED_STR "failed to create the frame" #define API_MSG_DICT_SET_FAILED_STR "failed to set" #define API_MSG_XDR_DECODE_FAILED_STR "XDR decoding error" #define API_MSG_GET_VOLFILE_FAILED_STR "failed to get the volume file" #define API_MSG_VOLFILE_INFO_STR "No change in volfile, continuing" #define API_MSG_UNABLE_TO_DEL_STR "unable to delete file" #define API_MSG_WRONG_OPVERSION_STR \ "Server is operating at an op-version which is not supported" #define API_MSG_DICT_SERIALIZE_FAILED_STR "Failed to serialize dictionary" #define API_MSG_REMOTE_HOST_CONN_FAILED_STR "Failed to connect to remote-host" #define API_MSG_REMOTE_HOST_DISCONN_STR "disconnected from remote-host" #define API_MSG_VOLFILE_SERVER_EXHAUST_STR "Exhausted all volfile servers" #define API_MSG_VOLFILE_CONNECTING_STR "connecting to next volfile server" #define API_MSG_CREATE_RPC_CLIENT_FAILED_STR "failed to create rpc clnt" #define API_MSG_REG_NOTIFY_FUNC_FAILED_STR "failed to register notify function" #define API_MSG_REG_CBK_FUNC_FAILED_STR "failed to register callback function" #define API_MSG_NEW_GRAPH_STR "New graph coming up" #define API_MSG_HANDLE_NOT_SET_STR "handle not set. Flags handled for xstat are" #endif /* !_GFAPI_MESSAGES_H__ */ glusterfs-11.2/api/src/gfapi.aliases000066400000000000000000000230221503123251100174340ustar00rootroot00000000000000 _priv_glfs_loc_touchup _glfs_loc_touchup$GFAPI_PRIVATE_3.4.0 _priv_glfs_active_subvol _glfs_active_subvol$GFAPI_PRIVATE_3.4.0 _priv_glfs_subvol_done _glfs_subvol_done$GFAPI_PRIVATE_3.4.0 _priv_glfs_init_done _glfs_init_done$GFAPI_PRIVATE_3.4.0 _priv_glfs_resolve_at _glfs_resolve_at$GFAPI_PRIVATE_3.4.0 _pub_glfs_new _glfs_new$GFAPI_3.4.0 _pub_glfs_set_volfile _glfs_set_volfile$GFAPI_3.4.0 _pub_glfs_set_volfile_server _glfs_set_volfile_server$GFAPI_3.4.0 _pub_glfs_set_logging _glfs_set_logging$GFAPI_3.4.0 _pub_glfs_init _glfs_init$GFAPI_3.4.0 _pub_glfs_fini _glfs_fini$GFAPI_3.4.0 _pub_glfs_open _glfs_open$GFAPI_3.4.0 _pub_glfs_creat _glfs_creat$GFAPI_3.4.0 _pub_glfs_close _glfs_close$GFAPI_3.4.0 _pub_glfs_from_glfd _glfs_from_glfd$GFAPI_3.4.0 _pub_glfs_set_xlator_option _glfs_set_xlator_option$GFAPI_3.4.0 _pub_glfs_read _glfs_read$GFAPI_3.4.0 _pub_glfs_write _glfs_write$GFAPI_3.4.0 _pub_glfs_readv _glfs_readv$GFAPI_3.4.0 _pub_glfs_writev _glfs_writev$GFAPI_3.4.0 _pub_glfs_pread34 _glfs_pread$GFAPI_3.4.0 _pub_glfs_pwrite34 _glfs_pwrite$GFAPI_3.4.0 _pub_glfs_pread_async34 _glfs_pread_async$GFAPI_3.4.0 _pub_glfs_pwrite_async34 _glfs_pwrite_async$GFAPI_3.4.0 _pub_glfs_preadv _glfs_preadv$GFAPI_3.4.0 _pub_glfs_pwritev _glfs_pwritev$GFAPI_3.4.0 _pub_glfs_lseek _glfs_lseek$GFAPI_3.4.0 _pub_glfs_ftruncate34 _glfs_ftruncate$GFAPI_3.4.0 _pub_glfs_ftruncate_async34 _glfs_ftruncate_async$GFAPI_3.4.0 _pub_glfs_lstat _glfs_lstat$GFAPI_3.4.0 _pub_glfs_stat _glfs_stat$GFAPI_3.4.0 _pub_glfs_fstat _glfs_fstat$GFAPI_3.4.0 _pub_glfs_fsync34 _glfs_fsync$GFAPI_3.4.0 _pub_glfs_fsync_async34 _glfs_fsync_async$GFAPI_3.4.0 _pub_glfs_fdatasync34 _glfs_fdatasync$GFAPI_3.4.0 _pub_glfs_fdatasync_async34 _glfs_fdatasync_async$GFAPI_3.4.0 _pub_glfs_access _glfs_access$GFAPI_3.4.0 _pub_glfs_symlink _glfs_symlink$GFAPI_3.4.0 _pub_glfs_readlink _glfs_readlink$GFAPI_3.4.0 _pub_glfs_mknod _glfs_mknod$GFAPI_3.4.0 _pub_glfs_mkdir _glfs_mkdir$GFAPI_3.4.0 _pub_glfs_unlink _glfs_unlink$GFAPI_3.4.0 _pub_glfs_rmdir _glfs_rmdir$GFAPI_3.4.0 _pub_glfs_rename _glfs_rename$GFAPI_3.4.0 _pub_glfs_link _glfs_link$GFAPI_3.4.0 _pub_glfs_opendir _glfs_opendir$GFAPI_3.4.0 _pub_glfs_readdir_r _glfs_readdir_r$GFAPI_3.4.0 _pub_glfs_readdirplus_r _glfs_readdirplus_r$GFAPI_3.4.0 _pub_glfs_telldir _glfs_telldir$GFAPI_3.4.0 _pub_glfs_seekdir _glfs_seekdir$GFAPI_3.4.0 _pub_glfs_closedir _glfs_closedir$GFAPI_3.4.0 _pub_glfs_statvfs _glfs_statvfs$GFAPI_3.4.0 _pub_glfs_chmod _glfs_chmod$GFAPI_3.4.0 _pub_glfs_fchmod _glfs_fchmod$GFAPI_3.4.0 _pub_glfs_chown _glfs_chown$GFAPI_3.4.0 _pub_glfs_lchown _glfs_lchown$GFAPI_3.4.0 _pub_glfs_fchown _glfs_fchown$GFAPI_3.4.0 _pub_glfs_utimens _glfs_utimens$GFAPI_3.4.0 _pub_glfs_lutimens _glfs_lutimens$GFAPI_3.4.0 _pub_glfs_futimens _glfs_futimens$GFAPI_3.4.0 _pub_glfs_getxattr _glfs_getxattr$GFAPI_3.4.0 _pub_glfs_lgetxattr _glfs_lgetxattr$GFAPI_3.4.0 _pub_glfs_fgetxattr _glfs_fgetxattr$GFAPI_3.4.0 _pub_glfs_listxattr _glfs_listxattr$GFAPI_3.4.0 _pub_glfs_llistxattr _glfs_llistxattr$GFAPI_3.4.0 _pub_glfs_flistxattr _glfs_flistxattr$GFAPI_3.4.0 _pub_glfs_setxattr _glfs_setxattr$GFAPI_3.4.0 _pub_glfs_lsetxattr _glfs_lsetxattr$GFAPI_3.4.0 _pub_glfs_fsetxattr _glfs_fsetxattr$GFAPI_3.4.0 _pub_glfs_removexattr _glfs_removexattr$GFAPI_3.4.0 _pub_glfs_lremovexattr _glfs_lremovexattr$GFAPI_3.4.0 _pub_glfs_fremovexattr _glfs_fremovexattr$GFAPI_3.4.0 _pub_glfs_getcwd _glfs_getcwd$GFAPI_3.4.0 _pub_glfs_chdir _glfs_chdir$GFAPI_3.4.0 _pub_glfs_fchdir _glfs_fchdir$GFAPI_3.4.0 _pub_glfs_realpath34 _glfs_realpath$GFAPI_3.4.0 _pub_glfs_posix_lock _glfs_posix_lock$GFAPI_3.4.0 _pub_glfs_dup _glfs_dup$GFAPI_3.4.0 _pub_glfs_setfsuid _glfs_setfsuid$GFAPI_3.4.2 _pub_glfs_setfsgid _glfs_setfsgid$GFAPI_3.4.2 _pub_glfs_setfsgroups _glfs_setfsgroups$GFAPI_3.4.2 _pub_glfs_h_lookupat34 _glfs_h_lookupat$GFAPI_3.4.2 _pub_glfs_h_creat _glfs_h_creat$GFAPI_3.4.2 _pub_glfs_h_mkdir _glfs_h_mkdir$GFAPI_3.4.2 _pub_glfs_h_mknod _glfs_h_mknod$GFAPI_3.4.2 _pub_glfs_h_symlink _glfs_h_symlink$GFAPI_3.4.2 _pub_glfs_h_unlink _glfs_h_unlink$GFAPI_3.4.2 _pub_glfs_h_close _glfs_h_close$GFAPI_3.4.2 _pub_glfs_h_truncate _glfs_h_truncate$GFAPI_3.4.2 _pub_glfs_h_stat _glfs_h_stat$GFAPI_3.4.2 _pub_glfs_h_getattrs _glfs_h_getattrs$GFAPI_3.4.2 _pub_glfs_h_setattrs _glfs_h_setattrs$GFAPI_3.4.2 _pub_glfs_h_readlink _glfs_h_readlink$GFAPI_3.4.2 _pub_glfs_h_link _glfs_h_link$GFAPI_3.4.2 _pub_glfs_h_rename _glfs_h_rename$GFAPI_3.4.2 _pub_glfs_h_extract_handle _glfs_h_extract_handle$GFAPI_3.4.2 _pub_glfs_h_create_from_handle _glfs_h_create_from_handle$GFAPI_3.4.2 _pub_glfs_h_opendir _glfs_h_opendir$GFAPI_3.4.2 _pub_glfs_h_open _glfs_h_open$GFAPI_3.4.2 _pub_glfs_get_volumeid _glfs_get_volumeid$GFAPI_3.5.0 _pub_glfs_readdir _glfs_readdir$GFAPI_3.5.0 _pub_glfs_readdirplus _glfs_readdirplus$GFAPI_3.5.0 _pub_glfs_fallocate _glfs_fallocate$GFAPI_3.5.0 _pub_glfs_discard _glfs_discard$GFAPI_3.5.0 _pub_glfs_zerofill _glfs_zerofill$GFAPI_3.5.0 _pub_glfs_caller_specific_init _glfs_caller_specific_init$GFAPI_3.5.0 _pub_glfs_h_setxattrs _glfs_h_setxattrs$GFAPI_3.5.0 _pub_glfs_unset_volfile_server _glfs_unset_volfile_server$GFAPI_3.5.1 _pub_glfs_h_getxattrs _glfs_h_getxattrs$GFAPI_3.5.1 _pub_glfs_h_removexattrs _glfs_h_removexattrs$GFAPI_3.5.1 _pub_glfs_get_volfile _glfs_get_volfile$GFAPI_3.6.0 _pub_glfs_h_access _glfs_h_access$GFAPI_3.6.0 _pub_glfs_h_poll_upcall370 _glfs_h_poll_upcall$GFAPI_3.7.0 _pub_glfs_h_acl_set _glfs_h_acl_set$GFAPI_3.7.0 _pub_glfs_h_acl_get _glfs_h_acl_get$GFAPI_3.7.0 _pub_glfs_h_statfs _glfs_h_statfs$GFAPI_3.7.0 _pub_glfs_h_anonymous_read _glfs_h_anonymous_read$GFAPI_3.7.0 _pub_glfs_h_anonymous_write _glfs_h_anonymous_write$GFAPI_3.7.0 _priv_glfs_free_from_ctx _glfs_free_from_ctx$GFAPI_PRIVATE_3.7.0 _priv_glfs_new_from_ctx _glfs_new_from_ctx$GFAPI_PRIVATE_3.7.0 _priv_glfs_resolve _glfs_resolve$GFAPI_PRIVATE_3.7.0 _priv_glfs_process_upcall_event _glfs_process_upcall_event$GFAPI_PRIVATE_3.7.0 _pub_glfs_h_lookupat _glfs_h_lookupat$GFAPI_3.7.4 _pub_glfs_truncate _glfs_truncate$GFAPI_3.7.15 _pub_glfs_free _glfs_free$GFAPI_3.7.16 _pub_glfs_h_poll_upcall _glfs_h_poll_upcall$GFAPI_3.7.16 _pub_glfs_upcall_get_fs _glfs_upcall_get_fs$GFAPI_3.7.16 _pub_glfs_upcall_get_reason _glfs_upcall_get_reason$GFAPI_3.7.16 _pub_glfs_upcall_get_event _glfs_upcall_get_event$GFAPI_3.7.16 _pub_glfs_upcall_inode_get_object _glfs_upcall_inode_get_object$GFAPI_3.7.16 _pub_glfs_upcall_inode_get_flags _glfs_upcall_inode_get_flags$GFAPI_3.7.16 _pub_glfs_upcall_inode_get_stat _glfs_upcall_inode_get_stat$GFAPI_3.7.16 _pub_glfs_upcall_inode_get_expire _glfs_upcall_inode_get_expire$GFAPI_3.7.16 _pub_glfs_upcall_inode_get_pobject _glfs_upcall_inode_get_pobject$GFAPI_3.7.16 _pub_glfs_upcall_inode_get_pstat _glfs_upcall_inode_get_pstat$GFAPI_3.7.16 _pub_glfs_upcall_inode_get_oldpobject _glfs_upcall_inode_get_oldpobject$GFAPI_3.7.16 _pub_glfs_upcall_inode_get_oldpstat _glfs_upcall_inode_get_oldpstat$GFAPI_3.7.16 _pub_glfs_realpath _glfs_realpath$GFAPI_3.7.17 _pub_glfs_sysrq _glfs_sysrq$GFAPI_3.10.0 _pub_glfs_fd_set_lkowner _glfs_fd_set_lkowner$GFAPI_3.10.7 _pub_glfs_xreaddirplus_r _glfs_xreaddirplus_r$GFAPI_3.11.0 _pub_glfs_xreaddirplus_r_get_stat _glfs_xreaddirplus_r_get_stat$GFAPI_3.11.0 _pub_glfs_xreaddirplus_r_get_object _glfs_xreaddirplus_r_get_object$GFAPI_3.11.0 _pub_glfs_object_copy _glfs_object_copy$GFAPI_3.11.0 _priv_glfs_ipc _glfs_ipc$GFAPI_3.12.0 _pub_glfs_upcall_register _glfs_upcall_register$GFAPI_3.13.0 _pub_glfs_upcall_unregister _glfs_upcall_unregister$GFAPI_3.13.0 _pub_glfs_setfsleaseid _glfs_setfsleaseid$GFAPI_4.0.0 _pub_glfs_file_lock _glfs_file_lock$GFAPI_4.0.0 _pub_glfs_lease _glfs_lease$GFAPI_4.0.0 _pub_glfs_h_lease _glfs_h_lease$GFAPI_4.0.0 _pub_glfs_upcall_lease_get_object _glfs_upcall_lease_get_object$GFAPI_4.1.6 _pub_glfs_upcall_lease_get_lease_type _glfs_upcall_lease_get_lease_type$GFAPI_4.1.6 _priv_glfs_statx _glfs_statx$GFAPI_6.0 _priv_glfs_iatt_from_statx _glfs_iatt_from_statx$GFAPI_6.0 _priv_glfs_setfspid _glfs_setfspid$GFAPI_6.1 _pub_glfs_read_async _glfs_read_async$GFAPI_6.0 _pub_glfs_write_async _glfs_write_async$GFAPI_6.0 _pub_glfs_readv_async _glfs_readv_async$GFAPI_6.0 _pub_glfs_writev_async _glfs_writev_async$GFAPI_6.0 _pub_glfs_pread _glfs_pread$GFAPI_6.0 _pub_glfs_pwrite _glfs_pwrite$GFAPI_6.0 _pub_glfs_pread_async _glfs_pread_async$GFAPI_6.0 _pub_glfs_pwrite_async _glfs_pwrite_async$GFAPI_6.0 _pub_glfs_preadv_async _glfs_preadv_async$GFAPI_6.0 _pub_glfs_pwritev_async _glfs_pwritev_async$GFAPI_6.0 _pub_glfs_fsync _glfs_fsync$GFAPI_6.0 _pub_glfs_fsync_async _glfs_fsync_async$GFAPI_6.0 _pub_glfs_fdatasync _glfs_fdatasync$GFAPI_6.0 _pub_glfs_fdatasync_async _glfs_fdatasync_async$GFAPI_6.0 _pub_glfs_ftruncate _glfs_ftruncate$GFAPI_6.0 _pub_glfs_ftruncate_async _glfs_ftruncate_async$GFAPI_6.0 _pub_glfs_discard_async _glfs_discard_async$GFAPI_6.0 _pub_glfs_zerofill_async _glfs_zerofill_async$GFAPI_6.0 _pub_glfs_copy_file_range _glfs_copy_file_range$GFAPI_6.0 _pub_glfs_fsetattr _glfs_fsetattr$GFAPI_6.0 _pub_glfs_setattr _glfs_setattr$GFAPI_6.0 _pub_glfs_set_statedump_path _glfs_set_statedump_path@GFAPI_7.0 _pub_glfs_h_creat_open _glfs_h_creat_open@GFAPI_6.6 _pub_glfs_openat _glfs_openat@GFAPI_11.0 _pub_glfs_fstatat _glfs_fstatat@GFAPI_11.0 _pub_glfs_mkdirat _glfs_mkdirat@GFAPI_11.0 _pub_glfs_faccessat _glfs_faccessat@GFAPI_11.0 _pub_glfs_fchmodat _glfs_fchmodat@GFAPI_11.0 _pub_glfs_fchownat _glfs_fchownat@GFAPI_11.0 _pub_glfs_linkat _glfs_linkat@GFAPI_11.0 _pub_glfs_mknodat _glfs_mknodat@GFAPI_11.0 _pub_glfs_readlinkat _glfs_readlinkat@GFAPI_11.0 _pub_glfs_renameat _glfs_renameat@GFAPI_11.0 _pub_glfs_renameat2 _glfs_renameaat2@GFAPI_11.0 _pub_glfs_symlinkat _glfs_symlinkat@GFAPI_11.0 _pub_glfs_unlinkat _glfs_unlinkat@GFAPI_11.0 glusterfs-11.2/api/src/gfapi.map000066400000000000000000000114221503123251100165710ustar00rootroot00000000000000 GFAPI_PRIVATE_3.4.0 { global: glfs_loc_touchup; glfs_active_subvol; glfs_subvol_done; glfs_init_done; glfs_resolve_at; local: *; }; GFAPI_3.4.0 { global: glfs_new; glfs_set_volfile; glfs_set_volfile_server; glfs_set_logging; glfs_init; glfs_fini; glfs_open; glfs_creat; glfs_close; glfs_from_glfd; glfs_set_xlator_option; glfs_read; glfs_write; glfs_read_async; glfs_write_async; glfs_readv; glfs_writev; glfs_readv_async; glfs_writev_async; glfs_pread; glfs_pwrite; glfs_pread_async; glfs_pwrite_async; glfs_preadv; glfs_pwritev; glfs_preadv_async; glfs_pwritev_async; glfs_lseek; glfs_ftruncate; glfs_ftruncate_async; glfs_lstat; glfs_stat; glfs_fstat; glfs_fsync; glfs_fsync_async; glfs_fdatasync; glfs_fdatasync_async; glfs_access; glfs_symlink; glfs_readlink; glfs_mknod; glfs_mkdir; glfs_unlink; glfs_rmdir; glfs_rename; glfs_link; glfs_opendir; glfs_readdir_r; glfs_readdirplus_r; glfs_telldir; glfs_seekdir; glfs_closedir; glfs_statvfs; glfs_chmod; glfs_fchmod; glfs_chown; glfs_lchown; glfs_fchown; glfs_utimens; glfs_lutimens; glfs_futimens; glfs_getxattr; glfs_lgetxattr; glfs_fgetxattr; glfs_listxattr; glfs_llistxattr; glfs_flistxattr; glfs_setxattr; glfs_lsetxattr; glfs_fsetxattr; glfs_removexattr; glfs_lremovexattr; glfs_fremovexattr; glfs_getcwd; glfs_chdir; glfs_fchdir; glfs_realpath; glfs_posix_lock; glfs_dup; } GFAPI_PRIVATE_3.4.0; GFAPI_3.4.2 { global: glfs_setfsuid; glfs_setfsgid; glfs_setfsgroups; glfs_h_creat; glfs_h_mkdir; glfs_h_mknod; glfs_h_symlink; glfs_h_unlink; glfs_h_close; glfs_h_truncate; glfs_h_stat; glfs_h_getattrs; glfs_h_setattrs; glfs_h_readlink; glfs_h_link; glfs_h_rename; glfs_h_extract_handle; glfs_h_create_from_handle; glfs_h_opendir; glfs_h_open; glfs_h_lookupat; } GFAPI_3.4.0; GFAPI_3.5.0 { global: glfs_get_volumeid; glfs_readdir; glfs_readdirplus; glfs_fallocate; glfs_discard; glfs_discard_async; glfs_zerofill; glfs_zerofill_async; glfs_caller_specific_init; glfs_h_setxattrs; } GFAPI_3.4.2; GFAPI_3.5.1 { global: glfs_unset_volfile_server; glfs_h_getxattrs; glfs_h_removexattrs; } GFAPI_3.5.0; GFAPI_3.6.0 { global: glfs_get_volfile; glfs_h_access; } GFAPI_3.5.1; GFAPI_3.7.0 { global: glfs_h_poll_upcall; glfs_h_acl_set; glfs_h_acl_get; glfs_h_statfs; glfs_h_anonymous_read; glfs_h_anonymous_write; } GFAPI_3.6.0; GFAPI_PRIVATE_3.7.0 { global: glfs_free_from_ctx; glfs_new_from_ctx; glfs_resolve; glfs_process_upcall_event; } GFAPI_3.7.0; GFAPI_3.7.4 { global: glfs_h_lookupat; } GFAPI_PRIVATE_3.7.0; GFAPI_3.7.15 { global: glfs_truncate; } GFAPI_3.7.4; GFAPI_3.7.16 { global: glfs_free; glfs_upcall_get_fs; glfs_upcall_get_reason; glfs_upcall_get_event; glfs_upcall_inode_get_object; glfs_upcall_inode_get_flags; glfs_upcall_inode_get_stat; glfs_upcall_inode_get_expire; glfs_upcall_inode_get_pobject; glfs_upcall_inode_get_pstat; glfs_upcall_inode_get_oldpobject; glfs_upcall_inode_get_oldpstat; glfs_h_poll_upcall; } GFAPI_3.7.15; GFAPI_3.7.17 { global: glfs_realpath; } GFAPI_3.7.16; GFAPI_3.10.0 { global: glfs_sysrq; } GFAPI_3.7.17; GFAPI_3.10.7 { global: glfs_fd_set_lkowner; } GFAPI_3.10.0; GFAPI_3.11.0 { glfs_xreaddirplus_r; glfs_xreaddirplus_r_get_stat; glfs_xreaddirplus_r_get_object; glfs_object_copy; } GFAPI_3.10.7; GFAPI_PRIVATE_3.12.0 { global: glfs_ipc; } GFAPI_3.11.0; GFAPI_3.13.0 { global: glfs_upcall_register; glfs_upcall_unregister; } GFAPI_PRIVATE_3.12.0; GFAPI_4.0.0 { global: glfs_setfsleaseid; glfs_file_lock; glfs_lease; glfs_h_lease; } GFAPI_3.13.0; GFAPI_4.1.6 { global: glfs_upcall_lease_get_object; glfs_upcall_lease_get_lease_type; } GFAPI_4.0.0; GFAPI_PRIVATE_6.0 { global: glfs_statx; glfs_iatt_from_statx; } GFAPI_4.1.6; GFAPI_6.0 { global: glfs_read_async; glfs_write_async; glfs_readv_async; glfs_writev_async; glfs_pread; glfs_pwrite; glfs_pread_async; glfs_pwrite_async; glfs_preadv_async; glfs_pwritev_async; glfs_fsync; glfs_fsync_async; glfs_fdatasync; glfs_fdatasync_async; glfs_ftruncate; glfs_ftruncate_async; glfs_discard_async; glfs_zerofill_async; glfs_copy_file_range; glfs_setattr; glfs_fsetattr; } GFAPI_PRIVATE_6.0; GFAPI_PRIVATE_6.1 { global: glfs_setfspid; } GFAPI_6.0; GFAPI_6.6 { global: glfs_h_creat_open; } GFAPI_PRIVATE_6.1; GFAPI_7.0 { global: glfs_set_statedump_path; } GFAPI_6.6; GFAPI_11.0 { global: glfs_openat; glfs_fstatat; glfs_mkdirat; glfs_faccessat; glfs_fchmodat; glfs_fchownat; glfs_linkat; glfs_mknodat; glfs_readlinkat; glfs_renameat; glfs_renameat2; glfs_symlinkat; glfs_unlinkat; } GFAPI_7.0; glusterfs-11.2/api/src/glfs-fops.c000066400000000000000000005204771503123251100170670ustar00rootroot00000000000000/* Copyright (c) 2012-2018 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ /* for SEEK_HOLE and SEEK_DATA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include "glfs-internal.h" #include "glfs-mem-types.h" #include "glfs.h" #include "gfapi-messages.h" #include #include #include #include "glusterfs3.h" #ifdef NAME_MAX #define GF_NAME_MAX NAME_MAX #else #define GF_NAME_MAX 255 #endif struct upcall_syncop_args { struct glfs *fs; struct gf_upcall upcall_data; }; #define READDIRBUF_SIZE (sizeof(struct dirent) + GF_NAME_MAX + 1) typedef void (*glfs_io_cbk34)(glfs_fd_t *fd, ssize_t ret, void *data); /* * This function will mark glfd for deletion and decrement its refcount. */ int glfs_mark_glfd_for_deletion(struct glfs_fd *glfd) { LOCK(&glfd->lock); { glfd->state = GLFD_CLOSE; glfd->next = NULL; } UNLOCK(&glfd->lock); GF_REF_PUT(glfd); return 0; } /* This function is useful for all async fops. There is chance that glfd is * closed before async fop is completed. When glfd is closed we change the * state to GLFD_CLOSE. * * This function will return _gf_true if the glfd is still valid else return * _gf_false. */ gf_boolean_t glfs_is_glfd_still_valid(struct glfs_fd *glfd) { gf_boolean_t ret = _gf_false; LOCK(&glfd->lock); { if (glfd->state != GLFD_CLOSE) ret = _gf_true; } UNLOCK(&glfd->lock); return ret; } void glfd_set_state_bind(struct glfs_fd *glfd) { LOCK(&glfd->lock); { glfd->state = GLFD_OPEN; } UNLOCK(&glfd->lock); fd_bind(glfd->fd); glfs_fd_bind(glfd); return; } /* * This routine is called when an upcall event of type * 'GF_UPCALL_CACHE_INVALIDATION' is received. * It makes a copy of the contents of the upcall cache-invalidation * data received into an entry which is stored in the upcall list * maintained by gfapi. */ int glfs_get_upcall_cache_invalidation(struct gf_upcall *to_up_data, struct gf_upcall *from_up_data) { struct gf_upcall_cache_invalidation *ca_data = NULL; struct gf_upcall_cache_invalidation *f_ca_data = NULL; int ret = -1; GF_VALIDATE_OR_GOTO(THIS->name, to_up_data, out); GF_VALIDATE_OR_GOTO(THIS->name, from_up_data, out); f_ca_data = from_up_data->data; GF_VALIDATE_OR_GOTO(THIS->name, f_ca_data, out); ca_data = GF_CALLOC(1, sizeof(*ca_data), glfs_mt_upcall_entry_t); if (!ca_data) { gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_ALLOC_FAILED, "entry", NULL); goto out; } to_up_data->data = ca_data; ca_data->flags = f_ca_data->flags; ca_data->expire_time_attr = f_ca_data->expire_time_attr; ca_data->stat = f_ca_data->stat; ca_data->p_stat = f_ca_data->p_stat; ca_data->oldp_stat = f_ca_data->oldp_stat; ret = 0; out: return ret; } int glfs_get_upcall_lease(struct gf_upcall *to_up_data, struct gf_upcall *from_up_data) { struct gf_upcall_recall_lease *ca_data = NULL; struct gf_upcall_recall_lease *f_ca_data = NULL; int ret = -1; GF_VALIDATE_OR_GOTO(THIS->name, to_up_data, out); GF_VALIDATE_OR_GOTO(THIS->name, from_up_data, out); f_ca_data = from_up_data->data; GF_VALIDATE_OR_GOTO(THIS->name, f_ca_data, out); ca_data = GF_CALLOC(1, sizeof(*ca_data), glfs_mt_upcall_entry_t); if (!ca_data) { gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_ALLOC_FAILED, "entry", NULL); goto out; } to_up_data->data = ca_data; ca_data->lease_type = f_ca_data->lease_type; gf_uuid_copy(ca_data->tid, f_ca_data->tid); ca_data->dict = f_ca_data->dict; ret = 0; out: return ret; } int glfs_loc_link(loc_t *loc, struct iatt *iatt) { int ret = -1; inode_t *old_inode = NULL; uint64_t ctx_value = LOOKUP_NOT_NEEDED; if (!loc->inode) { errno = EINVAL; return -1; } old_inode = loc->inode; /* If the inode already exists in the cache, the inode * returned here points to the existing one. We need * to update loc.inode accordingly. */ loc->inode = inode_link(loc->inode, loc->parent, loc->name, iatt); if (loc->inode) { inode_ctx_set(loc->inode, THIS, &ctx_value); inode_lookup(loc->inode); inode_unref(old_inode); ret = 0; } else { ret = -1; } return ret; } void glfs_iatt_to_stat(struct glfs *fs, struct iatt *iatt, struct stat *stat) { iatt_to_stat(iatt, stat); stat->st_dev = fs->dev_id; } void glfs_iatt_to_statx(struct glfs *fs, const struct iatt *iatt, struct glfs_stat *statx) { statx->glfs_st_mask = 0; statx->glfs_st_mode = 0; if (IATT_TYPE_VALID(iatt->ia_flags)) { statx->glfs_st_mode |= st_mode_type_from_ia(iatt->ia_type); statx->glfs_st_mask |= GLFS_STAT_TYPE; } if (IATT_MODE_VALID(iatt->ia_flags)) { statx->glfs_st_mode |= st_mode_prot_from_ia(iatt->ia_prot); statx->glfs_st_mask |= GLFS_STAT_MODE; } if (IATT_NLINK_VALID(iatt->ia_flags)) { statx->glfs_st_nlink = iatt->ia_nlink; statx->glfs_st_mask |= GLFS_STAT_NLINK; } if (IATT_UID_VALID(iatt->ia_flags)) { statx->glfs_st_uid = iatt->ia_uid; statx->glfs_st_mask |= GLFS_STAT_UID; } if (IATT_GID_VALID(iatt->ia_flags)) { statx->glfs_st_gid = iatt->ia_gid; statx->glfs_st_mask |= GLFS_STAT_GID; } if (IATT_ATIME_VALID(iatt->ia_flags)) { statx->glfs_st_atime.tv_sec = iatt->ia_atime; statx->glfs_st_atime.tv_nsec = iatt->ia_atime_nsec; statx->glfs_st_mask |= GLFS_STAT_ATIME; } if (IATT_MTIME_VALID(iatt->ia_flags)) { statx->glfs_st_mtime.tv_sec = iatt->ia_mtime; statx->glfs_st_mtime.tv_nsec = iatt->ia_mtime_nsec; statx->glfs_st_mask |= GLFS_STAT_MTIME; } if (IATT_CTIME_VALID(iatt->ia_flags)) { statx->glfs_st_ctime.tv_sec = iatt->ia_ctime; statx->glfs_st_ctime.tv_nsec = iatt->ia_ctime_nsec; statx->glfs_st_mask |= GLFS_STAT_CTIME; } if (IATT_BTIME_VALID(iatt->ia_flags)) { statx->glfs_st_btime.tv_sec = iatt->ia_btime; statx->glfs_st_btime.tv_nsec = iatt->ia_btime_nsec; statx->glfs_st_mask |= GLFS_STAT_BTIME; } if (IATT_INO_VALID(iatt->ia_flags)) { statx->glfs_st_ino = iatt->ia_ino; statx->glfs_st_mask |= GLFS_STAT_INO; } if (IATT_SIZE_VALID(iatt->ia_flags)) { statx->glfs_st_size = iatt->ia_size; statx->glfs_st_mask |= GLFS_STAT_SIZE; } if (IATT_BLOCKS_VALID(iatt->ia_flags)) { statx->glfs_st_blocks = iatt->ia_blocks; statx->glfs_st_mask |= GLFS_STAT_BLOCKS; } /* unconditionally present, encode as is */ statx->glfs_st_blksize = iatt->ia_blksize; statx->glfs_st_rdev_major = ia_major(iatt->ia_rdev); statx->glfs_st_rdev_minor = ia_minor(iatt->ia_rdev); statx->glfs_st_dev_major = ia_major(fs->dev_id); statx->glfs_st_dev_minor = ia_minor(fs->dev_id); /* At present we do not read any localFS attributes and pass them along, * so setting this to 0. As we start supporting file attributes we can * populate the same here as well */ statx->glfs_st_attributes = 0; statx->glfs_st_attributes_mask = 0; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_iatt_from_statx, 6.0) void priv_glfs_iatt_from_statx(struct iatt *iatt, const struct glfs_stat *statx) { /* Most code in xlators are not checking validity flags before accessing the items. Hence zero everything before setting valid items */ memset(iatt, 0, sizeof(struct iatt)); if (GLFS_STAT_TYPE_VALID(statx->glfs_st_mask)) { iatt->ia_type = ia_type_from_st_mode(statx->glfs_st_mode); iatt->ia_flags |= IATT_TYPE; } if (GLFS_STAT_MODE_VALID(statx->glfs_st_mask)) { iatt->ia_prot = ia_prot_from_st_mode(statx->glfs_st_mode); iatt->ia_flags |= IATT_MODE; } if (GLFS_STAT_NLINK_VALID(statx->glfs_st_mask)) { iatt->ia_nlink = statx->glfs_st_nlink; iatt->ia_flags |= IATT_NLINK; } if (GLFS_STAT_UID_VALID(statx->glfs_st_mask)) { iatt->ia_uid = statx->glfs_st_uid; iatt->ia_flags |= IATT_UID; } if (GLFS_STAT_GID_VALID(statx->glfs_st_mask)) { iatt->ia_gid = statx->glfs_st_gid; iatt->ia_flags |= IATT_GID; } if (GLFS_STAT_ATIME_VALID(statx->glfs_st_mask)) { iatt->ia_atime = statx->glfs_st_atime.tv_sec; iatt->ia_atime_nsec = statx->glfs_st_atime.tv_nsec; iatt->ia_flags |= IATT_ATIME; } if (GLFS_STAT_MTIME_VALID(statx->glfs_st_mask)) { iatt->ia_mtime = statx->glfs_st_mtime.tv_sec; iatt->ia_mtime_nsec = statx->glfs_st_mtime.tv_nsec; iatt->ia_flags |= IATT_MTIME; } if (GLFS_STAT_CTIME_VALID(statx->glfs_st_mask)) { iatt->ia_ctime = statx->glfs_st_ctime.tv_sec; iatt->ia_ctime_nsec = statx->glfs_st_ctime.tv_nsec; iatt->ia_flags |= IATT_CTIME; } if (GLFS_STAT_BTIME_VALID(statx->glfs_st_mask)) { iatt->ia_btime = statx->glfs_st_btime.tv_sec; iatt->ia_btime_nsec = statx->glfs_st_btime.tv_nsec; iatt->ia_flags |= IATT_BTIME; } if (GLFS_STAT_INO_VALID(statx->glfs_st_mask)) { iatt->ia_ino = statx->glfs_st_ino; iatt->ia_flags |= IATT_INO; } if (GLFS_STAT_SIZE_VALID(statx->glfs_st_mask)) { iatt->ia_size = statx->glfs_st_size; iatt->ia_flags |= IATT_SIZE; } if (GLFS_STAT_BLOCKS_VALID(statx->glfs_st_mask)) { iatt->ia_blocks = statx->glfs_st_blocks; iatt->ia_flags |= IATT_BLOCKS; } /* unconditionally present, encode as is */ iatt->ia_blksize = statx->glfs_st_blksize; iatt->ia_rdev = makedev(statx->glfs_st_rdev_major, statx->glfs_st_rdev_minor); iatt->ia_dev = makedev(statx->glfs_st_dev_major, statx->glfs_st_dev_minor); iatt->ia_attributes = statx->glfs_st_attributes; iatt->ia_attributes_mask = statx->glfs_st_attributes_mask; } void glfsflags_from_gfapiflags(struct glfs_stat *stat, int *glvalid) { *glvalid = 0; if (stat->glfs_st_mask & GLFS_STAT_MODE) { *glvalid |= GF_SET_ATTR_MODE; } if (stat->glfs_st_mask & GLFS_STAT_SIZE) { *glvalid |= GF_SET_ATTR_SIZE; } if (stat->glfs_st_mask & GLFS_STAT_UID) { *glvalid |= GF_SET_ATTR_UID; } if (stat->glfs_st_mask & GLFS_STAT_GID) { *glvalid |= GF_SET_ATTR_GID; } if (stat->glfs_st_mask & GLFS_STAT_ATIME) { *glvalid |= GF_SET_ATTR_ATIME; } if (stat->glfs_st_mask & GLFS_STAT_MTIME) { *glvalid |= GF_SET_ATTR_MTIME; } } int glfs_loc_unlink(loc_t *loc) { inode_unlink(loc->inode, loc->parent, loc->name); /* since glfs_h_* objects hold a reference to inode * it is safe to keep lookup count to '0' */ if (!inode_has_dentry(loc->inode)) inode_forget(loc->inode, 0); return 0; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_open, 3.4.0) struct glfs_fd * pub_glfs_open(struct glfs *fs, const char *path, int flags) { int ret = -1; struct glfs_fd *glfd = NULL; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } glfd = glfs_fd_new(fs); if (!glfd) goto out; retry: ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; ret = validate_open_flags(flags, iatt.ia_type); if (ret) goto out; if (glfd->fd) { /* Retry. Safe to touch glfd->fd as we still have not glfs_fd_bind() yet. */ fd_unref(glfd->fd); glfd->fd = NULL; } glfd->fd = fd_create(loc.inode, getpid()); if (!glfd->fd) { ret = -1; errno = ENOMEM; goto out; } glfd->fd->flags = flags; ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); if (IA_ISDIR(iatt.ia_type)) ret = syncop_opendir(subvol, &loc, glfd->fd, NULL, NULL); else ret = syncop_open(subvol, &loc, flags, glfd->fd, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); out: loc_wipe(&loc); if (fop_attr) dict_unref(fop_attr); if (ret && glfd) { GF_REF_PUT(glfd); glfd = NULL; } else if (glfd) { glfd_set_state_bind(glfd); } glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return glfd; } static void cleanup_fopat_args(struct glfs_fd *pglfd, xlator_t *subvol, int ret, loc_t *loc) { if (loc) loc_wipe(loc); if (subvol) glfs_subvol_done(pglfd->fs, subvol); GF_REF_PUT(pglfd); } static xlator_t * setup_fopat_args(struct glfs_fd *pglfd, const char *path, gf_boolean_t follow, loc_t *loc, struct iatt *iatt, int reval) { int ret = 0; xlator_t *subvol = NULL; GF_REF_GET(pglfd); subvol = glfs_active_subvol(pglfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } glfs_lock(pglfd->fs, _gf_true); { ret = glfs_resolve_at(pglfd->fs, subvol, pglfd->fd->inode, path, loc, iatt, follow, reval); } glfs_unlock(pglfd->fs); if (ret < 0) { goto out; } ret = 0; out: if (ret < 0 && errno != ENOENT) { cleanup_fopat_args(pglfd, subvol, ret, loc); subvol = NULL; } return subvol; } static int setup_entry_fopat_args(uuid_t gfid, dict_t **xattr_req, loc_t *loc) { int ret = 0; if (loc->inode) { errno = EEXIST; ret = -1; goto out; } /* errno from setup_fopat_args */ if (errno != ENOENT) /* Any other type of error is fatal */ goto out; /* errno == ENOENT */ if (!loc->parent) /* The parent directory or an ancestor even higher does not exist */ goto out; loc->inode = inode_new(loc->parent->table); if (!loc->inode) { ret = -1; errno = ENOMEM; goto out; } *xattr_req = dict_new(); if (!*xattr_req) { ret = -1; errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(*xattr_req, "gfid-req", gfid, true); if (ret) { ret = -1; errno = ENOMEM; goto out; } ret = 0; out: if (ret) { if (*xattr_req) dict_unref(*xattr_req); } return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_openat, 11.0) struct glfs_fd * pub_glfs_openat(struct glfs_fd *pglfd, const char *path, int flags, mode_t mode) { int ret = -1; struct glfs_fd *glfd = NULL; xlator_t *subvol = NULL; loc_t loc = { 0, }; dict_t *fop_attr = NULL; struct iatt iatt = {0}; uuid_t gfid; gf_boolean_t is_create = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(pglfd, invalid_fs); is_create = !!(flags & O_CREAT); subvol = setup_fopat_args(pglfd, path, !(flags & O_NOFOLLOW), &loc, &iatt, 0); if (!subvol) { goto out; } if (is_create && !loc.inode) { ret = setup_entry_fopat_args(gfid, &fop_attr, &loc); if (ret) { goto out; } } /* Error is ENOENT but O_CREAT flag is not set */ if (!loc.inode) { errno = ENOENT; ret = -1; goto out; } glfd = glfs_fd_new(pglfd->fs); if (!glfd) { ret = -1; goto out; } glfd->fd = fd_create(loc.inode, getpid()); if (!glfd->fd) { ret = -1; errno = ENOMEM; goto out; } glfd->fd->flags = flags; ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); if (!is_create) { if (IA_ISDIR(iatt.ia_type)) ret = syncop_opendir(subvol, &loc, glfd->fd, NULL, NULL); else ret = syncop_open(subvol, &loc, flags, glfd->fd, fop_attr, NULL); } else ret = syncop_create(subvol, &loc, flags, mode, glfd->fd, &iatt, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); if (is_create && ret == 0) ret = glfs_loc_link(&loc, &iatt); /* Because it is openat(), no ESTALE expected */ out: if (ret && glfd) { GF_REF_PUT(glfd); glfd = NULL; } else if (glfd) { glfd_set_state_bind(glfd); } if (fop_attr) dict_unref(fop_attr); cleanup_fopat_args(pglfd, subvol, ret, &loc); __GLFS_EXIT_FS; invalid_fs: return glfd; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_close, 3.4.0) int pub_glfs_close(struct glfs_fd *glfd) { xlator_t *subvol = NULL; int ret = -1; fd_t *fd = NULL; struct glfs *fs = NULL; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); gf_dirent_free(list_entry(&glfd->entries, gf_dirent_t, list)); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } if (glfd->lk_owner.len != 0) { ret = syncopctx_setfslkowner(&glfd->lk_owner); if (ret) goto out; } ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); ret = syncop_flush(subvol, fd, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); out: fs = glfd->fs; if (fd) fd_unref(fd); if (fop_attr) dict_unref(fop_attr); glfs_mark_glfd_for_deletion(glfd); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lstat, 3.4.0) int pub_glfs_lstat(struct glfs *fs, const char *path, struct stat *stat) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0 && stat) glfs_iatt_to_stat(fs, &iatt, stat); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_stat, 3.4.0) int pub_glfs_stat(struct glfs *fs, const char *path, struct stat *stat) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0 && stat) glfs_iatt_to_stat(fs, &iatt, stat); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fstatat, 11.0) int pub_glfs_fstatat(struct glfs_fd *pglfd, const char *path, struct stat *stat, int flags) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; int is_path_empty = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(pglfd, invalid_fs); fd_t *fd = NULL; is_path_empty = (flags & AT_EMPTY_PATH) == AT_EMPTY_PATH; retry: /* Retry case */ if (subvol) { cleanup_fopat_args(pglfd, subvol, ret, &loc); } if (is_path_empty && path[0] == '\0') { GF_REF_GET(pglfd); subvol = glfs_active_subvol(pglfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(pglfd->fs, subvol, pglfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } ret = syncop_fstat(subvol, fd, &iatt, NULL, NULL); DECODE_SYNCOP_ERR(ret); } else { subvol = setup_fopat_args(pglfd, path, !(flags & AT_SYMLINK_NOFOLLOW), &loc, &iatt, reval); } if (!subvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &loc, retry); if (!subvol || !stat) { ret = -1; goto out; } if (!loc.inode && !is_path_empty) { ret = -1; errno = ENOENT; goto out; } glfs_iatt_to_stat(pglfd->fs, &iatt, stat); ret = 0; out: cleanup_fopat_args(pglfd, subvol, ret, &loc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_statx, 6.0) int priv_glfs_statx(struct glfs *fs, const char *path, const unsigned int mask, struct glfs_stat *statxbuf) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); if (path == NULL) { ret = -1; errno = EINVAL; goto out; } if (mask & ~GLFS_STAT_ALL) { ret = -1; errno = EINVAL; goto out; } subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0 && statxbuf) glfs_iatt_to_statx(fs, &iatt, statxbuf); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fstat, 3.4.0) int pub_glfs_fstat(struct glfs_fd *glfd, struct stat *stat) { int ret = -1; xlator_t *subvol = NULL; struct iatt iatt = { 0, }; fd_t *fd = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } if (stat) { ret = syncop_fstat(subvol, fd, &iatt, NULL, NULL); if (ret == 0) glfs_iatt_to_stat(glfd->fs, &iatt, stat); } else ret = syncop_fstat(subvol, fd, NULL, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_creat, 3.4.0) struct glfs_fd * pub_glfs_creat(struct glfs *fs, const char *path, int flags, mode_t mode) { int ret = -1; struct glfs_fd *glfd = NULL; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } xattr_req = dict_new(); if (!xattr_req) { ret = -1; errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { ret = -1; errno = ENOMEM; goto out; } glfd = glfs_fd_new(fs); if (!glfd) goto out; /* This must be glfs_resolve() and NOT glfs_lresolve(). That is because open("name", O_CREAT) where "name" is a danging symlink must create the dangling destination. */ retry: ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == -1 && errno != ENOENT) /* Any other type of error is fatal */ goto out; if (ret == -1 && errno == ENOENT && !loc.parent) /* The parent directory or an ancestor even higher does not exist */ goto out; if (loc.inode) { if (flags & O_EXCL) { ret = -1; errno = EEXIST; goto out; } if (IA_ISDIR(iatt.ia_type)) { ret = -1; errno = EISDIR; goto out; } if (!IA_ISREG(iatt.ia_type)) { ret = -1; errno = EINVAL; goto out; } } if (ret == -1 && errno == ENOENT) { loc.inode = inode_new(loc.parent->table); if (!loc.inode) { ret = -1; errno = ENOMEM; goto out; } } if (glfd->fd) { /* Retry. Safe to touch glfd->fd as we still have not glfs_fd_bind() yet. */ fd_unref(glfd->fd); glfd->fd = NULL; } glfd->fd = fd_create(loc.inode, getpid()); if (!glfd->fd) { ret = -1; errno = ENOMEM; goto out; } glfd->fd->flags = flags; if (get_fop_attr_thrd_key(&xattr_req)) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); if (ret == 0) { ret = syncop_open(subvol, &loc, flags, glfd->fd, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); } else { ret = syncop_create(subvol, &loc, flags, mode, glfd->fd, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); } ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0) ret = glfs_loc_link(&loc, &iatt); out: loc_wipe(&loc); if (xattr_req) dict_unref(xattr_req); if (ret && glfd) { GF_REF_PUT(glfd); glfd = NULL; } else if (glfd) { glfd_set_state_bind(glfd); } glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return glfd; } #ifdef HAVE_SEEK_HOLE static int glfs_seek(struct glfs_fd *glfd, off_t offset, int whence) { int ret = -1; xlator_t *subvol = NULL; fd_t *fd = NULL; gf_seek_what_t what = 0; off_t off = -1; switch (whence) { case SEEK_DATA: what = GF_SEEK_DATA; break; case SEEK_HOLE: what = GF_SEEK_HOLE; break; default: /* other SEEK_* do not make sense, all operations get an offset * and the position in the fd is not tracked */ errno = EINVAL; goto out; } subvol = glfs_active_subvol(glfd->fs); if (!subvol) { errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { errno = EBADFD; goto done; } ret = syncop_seek(subvol, fd, offset, what, NULL, &off); DECODE_SYNCOP_ERR(ret); if (ret != -1) glfd->offset = off; done: if (fd) fd_unref(fd); glfs_subvol_done(glfd->fs, subvol); out: return ret; } #endif GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lseek, 3.4.0) off_t pub_glfs_lseek(struct glfs_fd *glfd, off_t offset, int whence) { struct stat sb = { 0, }; int ret = -1; off_t off = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); switch (whence) { case SEEK_SET: glfd->offset = offset; ret = 0; break; case SEEK_CUR: glfd->offset += offset; ret = 0; break; case SEEK_END: ret = pub_glfs_fstat(glfd, &sb); if (ret) { /* seek cannot fail :O */ break; } glfd->offset = sb.st_size + offset; break; #ifdef HAVE_SEEK_HOLE case SEEK_DATA: case SEEK_HOLE: ret = glfs_seek(glfd, offset, whence); break; #endif default: errno = EINVAL; } if (glfd) GF_REF_PUT(glfd); __GLFS_EXIT_FS; if (ret != -1) off = glfd->offset; return off; invalid_fs: return -1; } static ssize_t glfs_preadv_common(struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt, off_t offset, int flags, struct glfs_stat *poststat) { xlator_t *subvol = NULL; ssize_t ret = -1; ssize_t size = -1; struct iovec *iov = NULL; int cnt = 0; struct iobref *iobref = NULL; fd_t *fd = NULL; struct iatt iatt = { 0, }; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } size = iov_length(iovec, iovcnt); ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); if (poststat) { ret = syncop_readv(subvol, fd, size, offset, 0, &iov, &cnt, &iobref, &iatt, fop_attr, NULL); if (ret >= 0) glfs_iatt_to_statx(glfd->fs, &iatt, poststat); } else ret = syncop_readv(subvol, fd, size, offset, 0, &iov, &cnt, &iobref, NULL, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); if (ret <= 0) goto out; size = iov_copy(iovec, iovcnt, iov, cnt); /* FIXME!!! */ glfd->offset = (offset + size); ret = size; out: if (iov) GF_FREE(iov); if (iobref) iobref_unref(iobref); if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (fop_attr) dict_unref(fop_attr); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_preadv, 3.4.0) ssize_t pub_glfs_preadv(struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt, off_t offset, int flags) { return glfs_preadv_common(glfd, iovec, iovcnt, offset, flags, NULL); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_read, 3.4.0) ssize_t pub_glfs_read(struct glfs_fd *glfd, void *buf, size_t count, int flags) { struct iovec iov = { 0, }; ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } iov.iov_base = buf; iov.iov_len = count; ret = pub_glfs_preadv(glfd, &iov, 1, glfd->offset, flags); return ret; } GFAPI_SYMVER_PUBLIC(glfs_pread34, glfs_pread, 3.4.0) ssize_t pub_glfs_pread34(struct glfs_fd *glfd, void *buf, size_t count, off_t offset, int flags) { struct iovec iov = { 0, }; ssize_t ret = 0; iov.iov_base = buf; iov.iov_len = count; ret = pub_glfs_preadv(glfd, &iov, 1, offset, flags); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pread, 6.0) ssize_t pub_glfs_pread(struct glfs_fd *glfd, void *buf, size_t count, off_t offset, int flags, struct glfs_stat *poststat) { struct iovec iov = { 0, }; ssize_t ret = 0; iov.iov_base = buf; iov.iov_len = count; ret = glfs_preadv_common(glfd, &iov, 1, offset, flags, poststat); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readv, 3.4.0) ssize_t pub_glfs_readv(struct glfs_fd *glfd, const struct iovec *iov, int count, int flags) { ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } ret = pub_glfs_preadv(glfd, iov, count, glfd->offset, flags); return ret; } struct glfs_io { struct glfs_fd *glfd; int op; off_t offset; int count; int flags; gf_boolean_t oldcb; union { glfs_io_cbk34 fn34; glfs_io_cbk fn; }; void *data; struct iovec iov[]; }; static int glfs_io_async_cbk(int op_ret, int op_errno, call_frame_t *frame, void *cookie, struct iovec *iovec, int count, struct iatt *prebuf, struct iatt *postbuf) { struct glfs_io *gio = NULL; xlator_t *subvol = NULL; struct glfs *fs = NULL; struct glfs_fd *glfd = NULL; int ret = -1; struct glfs_stat prestat = {}, *prestatp = NULL; struct glfs_stat poststat = {}, *poststatp = NULL; GF_VALIDATE_OR_GOTO("gfapi", frame, inval); GF_VALIDATE_OR_GOTO("gfapi", cookie, inval); gio = frame->local; frame->local = NULL; subvol = cookie; glfd = gio->glfd; fs = glfd->fs; if (!glfs_is_glfd_still_valid(glfd)) goto err; if (op_ret <= 0) { goto out; } else if (gio->op == GF_FOP_READ) { if (!iovec) { op_ret = -1; op_errno = EINVAL; goto out; } op_ret = iov_copy(gio->iov, gio->count, iovec, count); glfd->offset = gio->offset + op_ret; } else if (gio->op == GF_FOP_WRITE) { glfd->offset = gio->offset + gio->iov->iov_len; } out: errno = op_errno; if (gio->oldcb) { gio->fn34(gio->glfd, op_ret, gio->data); } else { if (prebuf) { prestatp = &prestat; glfs_iatt_to_statx(fs, prebuf, prestatp); } if (postbuf) { poststatp = &poststat; glfs_iatt_to_statx(fs, postbuf, poststatp); } gio->fn(gio->glfd, op_ret, prestatp, poststatp, gio->data); } err: fd_unref(glfd->fd); /* Since the async operation is complete * release the ref taken during the start * of async operation */ GF_REF_PUT(glfd); GF_FREE(gio); STACK_DESTROY(frame->root); glfs_subvol_done(fs, subvol); ret = 0; inval: return ret; } static int glfs_preadv_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, struct iovec *iovec, int count, struct iatt *stbuf, struct iobref *iobref, dict_t *xdata) { glfs_io_async_cbk(op_ret, op_errno, frame, cookie, iovec, count, NULL, stbuf); return 0; } static int glfs_preadv_async_common(struct glfs_fd *glfd, const struct iovec *iovec, int count, off_t offset, int flags, gf_boolean_t oldcb, glfs_io_cbk fn, void *data) { struct glfs_io *gio = NULL; int ret = 0; call_frame_t *frame = NULL; xlator_t *subvol = NULL; struct glfs *fs = NULL; fd_t *fd = NULL; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } fs = glfd->fs; frame = syncop_create_frame(THIS); if (!frame) { ret = -1; errno = ENOMEM; goto out; } gio = GF_MALLOC(sizeof(*gio) + (count * sizeof(struct iovec)), glfs_mt_glfs_io_t); if (!gio) { ret = -1; errno = ENOMEM; goto out; } gio->glfd = glfd; gio->op = GF_FOP_READ; gio->offset = offset; gio->count = count; gio->flags = flags; gio->oldcb = oldcb; gio->fn = fn; gio->data = data; memcpy(gio->iov, iovec, sizeof(struct iovec) * count); frame->local = gio; ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); STACK_WIND_COOKIE(frame, glfs_preadv_async_cbk, subvol, subvol, subvol->fops->readv, fd, iov_length(iovec, count), offset, flags, fop_attr); out: if (ret) { if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (gio) { GF_FREE(gio); } if (frame) { STACK_DESTROY(frame->root); } glfs_subvol_done(fs, subvol); } if (fop_attr) dict_unref(fop_attr); __GLFS_EXIT_FS; return ret; invalid_fs: return -1; } GFAPI_SYMVER_PUBLIC(glfs_preadv_async34, glfs_preadv_async, 3.4.0) int pub_glfs_preadv_async34(struct glfs_fd *glfd, const struct iovec *iovec, int count, off_t offset, int flags, glfs_io_cbk34 fn, void *data) { return glfs_preadv_async_common(glfd, iovec, count, offset, flags, _gf_true, (void *)fn, data); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_preadv_async, 6.0) int pub_glfs_preadv_async(struct glfs_fd *glfd, const struct iovec *iovec, int count, off_t offset, int flags, glfs_io_cbk fn, void *data) { return glfs_preadv_async_common(glfd, iovec, count, offset, flags, _gf_false, fn, data); } GFAPI_SYMVER_PUBLIC(glfs_read_async34, glfs_read_async, 3.4.0) int pub_glfs_read_async34(struct glfs_fd *glfd, void *buf, size_t count, int flags, glfs_io_cbk34 fn, void *data) { struct iovec iov = { 0, }; ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } iov.iov_base = buf; iov.iov_len = count; ret = glfs_preadv_async_common(glfd, &iov, 1, glfd->offset, flags, _gf_true, (void *)fn, data); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_read_async, 6.0) int pub_glfs_read_async(struct glfs_fd *glfd, void *buf, size_t count, int flags, glfs_io_cbk fn, void *data) { struct iovec iov = { 0, }; ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } iov.iov_base = buf; iov.iov_len = count; ret = glfs_preadv_async_common(glfd, &iov, 1, glfd->offset, flags, _gf_false, fn, data); return ret; } GFAPI_SYMVER_PUBLIC(glfs_pread_async34, glfs_pread_async, 3.4.0) int pub_glfs_pread_async34(struct glfs_fd *glfd, void *buf, size_t count, off_t offset, int flags, glfs_io_cbk34 fn, void *data) { struct iovec iov = { 0, }; ssize_t ret = 0; iov.iov_base = buf; iov.iov_len = count; ret = glfs_preadv_async_common(glfd, &iov, 1, offset, flags, _gf_true, (void *)fn, data); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pread_async, 6.0) int pub_glfs_pread_async(struct glfs_fd *glfd, void *buf, size_t count, off_t offset, int flags, glfs_io_cbk fn, void *data) { struct iovec iov = { 0, }; ssize_t ret = 0; iov.iov_base = buf; iov.iov_len = count; ret = glfs_preadv_async_common(glfd, &iov, 1, offset, flags, _gf_false, fn, data); return ret; } GFAPI_SYMVER_PUBLIC(glfs_readv_async34, glfs_readv_async, 3.4.0) int pub_glfs_readv_async34(struct glfs_fd *glfd, const struct iovec *iov, int count, int flags, glfs_io_cbk34 fn, void *data) { ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } ret = glfs_preadv_async_common(glfd, iov, count, glfd->offset, flags, _gf_true, (void *)fn, data); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readv_async, 6.0) int pub_glfs_readv_async(struct glfs_fd *glfd, const struct iovec *iov, int count, int flags, glfs_io_cbk fn, void *data) { ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } ret = glfs_preadv_async_common(glfd, iov, count, glfd->offset, flags, _gf_false, fn, data); return ret; } static ssize_t glfs_pwritev_common(struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt, off_t offset, int flags, struct glfs_stat *prestat, struct glfs_stat *poststat) { xlator_t *subvol = NULL; int ret = -1; struct iobref *iobref = NULL; struct iobuf *iobuf = NULL; struct iovec iov = { 0, }; fd_t *fd = NULL; struct iatt preiatt = { 0, }, postiatt = { 0, }; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); if (iovec->iov_len >= GF_UNIT_GB) { ret = -1; errno = EINVAL; gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ARG, "Data size too large", "size = %llu", GF_UNIT_GB, NULL); goto out; } subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } ret = iobuf_copy(subvol->ctx->iobuf_pool, iovec, iovcnt, &iobref, &iobuf, &iov); if (ret) goto out; ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); ret = syncop_writev(subvol, fd, &iov, 1, offset, iobref, flags, &preiatt, &postiatt, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); if (ret >= 0) { if (prestat) glfs_iatt_to_statx(glfd->fs, &preiatt, prestat); if (poststat) glfs_iatt_to_statx(glfd->fs, &postiatt, poststat); } if (ret <= 0) goto out; glfd->offset = (offset + iov.iov_len); out: if (iobuf) iobuf_unref(iobuf); if (iobref) iobref_unref(iobref); if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (fop_attr) dict_unref(fop_attr); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_copy_file_range, 6.0) ssize_t pub_glfs_copy_file_range(struct glfs_fd *glfd_in, off64_t *off_in, struct glfs_fd *glfd_out, off64_t *off_out, size_t len, unsigned int flags, struct glfs_stat *statbuf, struct glfs_stat *prestat, struct glfs_stat *poststat) { xlator_t *subvol = NULL; int ret = -1; fd_t *fd_in = NULL; fd_t *fd_out = NULL; struct iatt preiatt = { 0, }, iattbuf = { 0, }, postiatt = { 0, }; dict_t *fop_attr = NULL; off64_t pos_in; off64_t pos_out; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd_in, invalid_fs); __GLFS_ENTRY_VALIDATE_FD(glfd_out, invalid_fs); GF_REF_GET(glfd_in); GF_REF_GET(glfd_out); if (glfd_in->fs != glfd_out->fs) { ret = -1; errno = EXDEV; goto out; } subvol = glfs_active_subvol(glfd_in->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd_in = glfs_resolve_fd(glfd_in->fs, subvol, glfd_in); if (!fd_in) { ret = -1; errno = EBADFD; goto out; } fd_out = glfs_resolve_fd(glfd_out->fs, subvol, glfd_out); if (!fd_out) { ret = -1; errno = EBADFD; goto out; } /* * This is based on how the vfs layer in the kernel handles * copy_file_range call. Upon receiving it follows the * below method to consider the offset. * if (off_in != NULL) * use the value off_in to perform the op * else if off_in == NULL * use the current file offset position to perform the op * * For gfapi, glfd->offset is used. For a freshly opened * fd, the offset is set to 0. */ if (off_in) pos_in = *off_in; else pos_in = glfd_in->offset; if (off_out) pos_out = *off_out; else pos_out = glfd_out->offset; ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); ret = syncop_copy_file_range(subvol, fd_in, pos_in, fd_out, pos_out, len, flags, &iattbuf, &preiatt, &postiatt, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); if (ret >= 0) { pos_in += ret; pos_out += ret; if (off_in) *off_in = pos_in; if (off_out) *off_out = pos_out; if (statbuf) glfs_iatt_to_statx(glfd_in->fs, &iattbuf, statbuf); if (prestat) glfs_iatt_to_statx(glfd_in->fs, &preiatt, prestat); if (poststat) glfs_iatt_to_statx(glfd_in->fs, &postiatt, poststat); } if (ret <= 0) goto out; /* * If *off_in is NULL, then there is no offset info that can * obtained from the input argument. Hence follow below method. * If *off_in is NULL, then * glfd->offset = offset + ret; * else * do nothing. * * According to the man page of copy_file_range, if off_in is * NULL, then the offset of the source file is advanced by * the return value of the fop. The same applies to off_out as * well. Otherwise, if *off_in is not NULL, then the offset * is not advanced by the filesystem. The entity which sends * the copy_file_range call is supposed to advance the offset * value in its buffer (pointed to by *off_in or *off_out) * by the return value of copy_file_range. */ if (!off_in) glfd_in->offset += ret; if (!off_out) glfd_out->offset += ret; out: if (fd_in) fd_unref(fd_in); if (fd_out) fd_unref(fd_out); if (glfd_in) GF_REF_PUT(glfd_in); if (glfd_out) GF_REF_PUT(glfd_out); if (fop_attr) dict_unref(fop_attr); glfs_subvol_done(glfd_in->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pwritev, 3.4.0) ssize_t pub_glfs_pwritev(struct glfs_fd *glfd, const struct iovec *iovec, int iovcnt, off_t offset, int flags) { return glfs_pwritev_common(glfd, iovec, iovcnt, offset, flags, NULL, NULL); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_write, 3.4.0) ssize_t pub_glfs_write(struct glfs_fd *glfd, const void *buf, size_t count, int flags) { struct iovec iov = { 0, }; ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } iov.iov_base = (void *)buf; iov.iov_len = count; ret = pub_glfs_pwritev(glfd, &iov, 1, glfd->offset, flags); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_writev, 3.4.0) ssize_t pub_glfs_writev(struct glfs_fd *glfd, const struct iovec *iov, int count, int flags) { ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } ret = pub_glfs_pwritev(glfd, iov, count, glfd->offset, flags); return ret; } GFAPI_SYMVER_PUBLIC(glfs_pwrite34, glfs_pwrite, 3.4.0) ssize_t pub_glfs_pwrite34(struct glfs_fd *glfd, const void *buf, size_t count, off_t offset, int flags) { struct iovec iov = { 0, }; ssize_t ret = 0; iov.iov_base = (void *)buf; iov.iov_len = count; ret = pub_glfs_pwritev(glfd, &iov, 1, offset, flags); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pwrite, 6.0) ssize_t pub_glfs_pwrite(struct glfs_fd *glfd, const void *buf, size_t count, off_t offset, int flags, struct glfs_stat *prestat, struct glfs_stat *poststat) { struct iovec iov = { 0, }; ssize_t ret = 0; iov.iov_base = (void *)buf; iov.iov_len = count; ret = glfs_pwritev_common(glfd, &iov, 1, offset, flags, prestat, poststat); return ret; } extern glfs_t * pub_glfs_from_glfd(glfs_fd_t *); static int glfs_pwritev_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, struct iatt *prebuf, struct iatt *postbuf, dict_t *xdata) { glfs_io_async_cbk(op_ret, op_errno, frame, cookie, NULL, 0, prebuf, postbuf); return 0; } static int glfs_pwritev_async_common(struct glfs_fd *glfd, const struct iovec *iovec, int count, off_t offset, int flags, gf_boolean_t oldcb, glfs_io_cbk fn, void *data) { struct glfs_io *gio = NULL; int ret = -1; call_frame_t *frame = NULL; xlator_t *subvol = NULL; fd_t *fd = NULL; struct iobref *iobref = NULL; struct iobuf *iobuf = NULL; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); /* Need to take explicit ref so that the fd * is not destroyed before the fop is complete */ GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { errno = EBADFD; goto out; } gio = GF_MALLOC(sizeof(*gio) + (1 * (sizeof(struct iovec))), glfs_mt_glfs_io_t); if (caa_unlikely(!gio)) { errno = ENOMEM; goto out; } gio->glfd = glfd; gio->op = GF_FOP_WRITE; gio->offset = offset; gio->count = 1; gio->flags = flags; gio->oldcb = oldcb; gio->fn = fn; gio->data = data; ret = iobuf_copy(subvol->ctx->iobuf_pool, iovec, count, &iobref, &iobuf, gio->iov); if (ret) goto out; frame = syncop_create_frame(THIS); if (!frame) { errno = ENOMEM; ret = -1; goto out; } frame->local = gio; ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); STACK_WIND_COOKIE(frame, glfs_pwritev_async_cbk, subvol, subvol, subvol->fops->writev, fd, gio->iov, gio->count, offset, flags, iobref, fop_attr); ret = 0; out: if (ret) { if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); GF_FREE(gio); /* * If there is any error condition check after the frame * creation, we have to destroy the frame root. */ glfs_subvol_done(glfd->fs, subvol); } if (fop_attr) dict_unref(fop_attr); if (iobuf) iobuf_unref(iobuf); if (iobref) iobref_unref(iobref); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC(glfs_pwritev_async34, glfs_pwritev_async, 3.4.0) int pub_glfs_pwritev_async34(struct glfs_fd *glfd, const struct iovec *iovec, int count, off_t offset, int flags, glfs_io_cbk34 fn, void *data) { return glfs_pwritev_async_common(glfd, iovec, count, offset, flags, _gf_true, (void *)fn, data); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pwritev_async, 6.0) int pub_glfs_pwritev_async(struct glfs_fd *glfd, const struct iovec *iovec, int count, off_t offset, int flags, glfs_io_cbk fn, void *data) { return glfs_pwritev_async_common(glfd, iovec, count, offset, flags, _gf_false, fn, data); } GFAPI_SYMVER_PUBLIC(glfs_write_async34, glfs_write_async, 3.4.0) int pub_glfs_write_async34(struct glfs_fd *glfd, const void *buf, size_t count, int flags, glfs_io_cbk34 fn, void *data) { struct iovec iov = { 0, }; ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } iov.iov_base = (void *)buf; iov.iov_len = count; ret = glfs_pwritev_async_common(glfd, &iov, 1, glfd->offset, flags, _gf_true, (void *)fn, data); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_write_async, 6.0) int pub_glfs_write_async(struct glfs_fd *glfd, const void *buf, size_t count, int flags, glfs_io_cbk fn, void *data) { struct iovec iov = { 0, }; ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } iov.iov_base = (void *)buf; iov.iov_len = count; ret = glfs_pwritev_async_common(glfd, &iov, 1, glfd->offset, flags, _gf_false, fn, data); return ret; } GFAPI_SYMVER_PUBLIC(glfs_pwrite_async34, glfs_pwrite_async, 3.4.0) int pub_glfs_pwrite_async34(struct glfs_fd *glfd, const void *buf, int count, off_t offset, int flags, glfs_io_cbk34 fn, void *data) { struct iovec iov = { 0, }; ssize_t ret = 0; iov.iov_base = (void *)buf; iov.iov_len = count; ret = glfs_pwritev_async_common(glfd, &iov, 1, offset, flags, _gf_true, (void *)fn, data); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_pwrite_async, 6.0) int pub_glfs_pwrite_async(struct glfs_fd *glfd, const void *buf, int count, off_t offset, int flags, glfs_io_cbk fn, void *data) { struct iovec iov = { 0, }; ssize_t ret = 0; iov.iov_base = (void *)buf; iov.iov_len = count; ret = glfs_pwritev_async_common(glfd, &iov, 1, offset, flags, _gf_false, fn, data); return ret; } GFAPI_SYMVER_PUBLIC(glfs_writev_async34, glfs_writev_async, 3.4.0) int pub_glfs_writev_async34(struct glfs_fd *glfd, const struct iovec *iov, int count, int flags, glfs_io_cbk34 fn, void *data) { ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } ret = glfs_pwritev_async_common(glfd, iov, count, glfd->offset, flags, _gf_true, (void *)fn, data); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_writev_async, 6.0) int pub_glfs_writev_async(struct glfs_fd *glfd, const struct iovec *iov, int count, int flags, glfs_io_cbk fn, void *data) { ssize_t ret = 0; if (glfd == NULL) { errno = EBADF; return -1; } ret = glfs_pwritev_async_common(glfd, iov, count, glfd->offset, flags, _gf_false, fn, data); return ret; } static int glfs_fsync_common(struct glfs_fd *glfd, struct glfs_stat *prestat, struct glfs_stat *poststat) { int ret = -1; xlator_t *subvol = NULL; fd_t *fd = NULL; struct iatt preiatt = { 0, }, postiatt = { 0, }; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); ret = syncop_fsync(subvol, fd, 0, &preiatt, &postiatt, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); if (ret >= 0) { if (prestat) glfs_iatt_to_statx(glfd->fs, &preiatt, prestat); if (poststat) glfs_iatt_to_statx(glfd->fs, &postiatt, poststat); } out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (fop_attr) dict_unref(fop_attr); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC(glfs_fsync34, glfs_fsync, 3.4.0) int pub_glfs_fsync34(struct glfs_fd *glfd) { return glfs_fsync_common(glfd, NULL, NULL); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fsync, 6.0) int pub_glfs_fsync(struct glfs_fd *glfd, struct glfs_stat *prestat, struct glfs_stat *poststat) { return glfs_fsync_common(glfd, prestat, poststat); } static int glfs_fsync_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *prebuf, struct iatt *postbuf, dict_t *xdata) { glfs_io_async_cbk(op_ret, op_errno, frame, cookie, NULL, 0, prebuf, postbuf); return 0; } static int glfs_fsync_async_common(struct glfs_fd *glfd, gf_boolean_t oldcb, glfs_io_cbk fn, void *data, int dataonly) { struct glfs_io *gio = NULL; int ret = 0; call_frame_t *frame = NULL; xlator_t *subvol = NULL; fd_t *fd = NULL; /* Need to take explicit ref so that the fd * is not destroyed before the fop is complete */ GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } frame = syncop_create_frame(THIS); if (!frame) { ret = -1; errno = ENOMEM; goto out; } gio = GF_CALLOC(1, sizeof(*gio), glfs_mt_glfs_io_t); if (!gio) { errno = ENOMEM; ret = -1; goto out; } gio->op = GF_FOP_FSYNC; gio->glfd = glfd; gio->flags = dataonly; gio->oldcb = oldcb; gio->fn = fn; gio->data = data; frame->local = gio; STACK_WIND_COOKIE(frame, glfs_fsync_async_cbk, subvol, subvol, subvol->fops->fsync, fd, dataonly, NULL); out: if (ret) { if (fd) fd_unref(fd); GF_REF_PUT(glfd); GF_FREE(gio); if (frame) STACK_DESTROY(frame->root); glfs_subvol_done(glfd->fs, subvol); } return ret; } GFAPI_SYMVER_PUBLIC(glfs_fsync_async34, glfs_fsync_async, 3.4.0) int pub_glfs_fsync_async34(struct glfs_fd *glfd, glfs_io_cbk34 fn, void *data) { int ret = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); ret = glfs_fsync_async_common(glfd, _gf_true, (void *)fn, data, 0); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fsync_async, 6.0) int pub_glfs_fsync_async(struct glfs_fd *glfd, glfs_io_cbk fn, void *data) { int ret = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); ret = glfs_fsync_async_common(glfd, _gf_false, fn, data, 0); __GLFS_EXIT_FS; invalid_fs: return ret; } static int glfs_fdatasync_common(struct glfs_fd *glfd, struct glfs_stat *prestat, struct glfs_stat *poststat) { int ret = -1; xlator_t *subvol = NULL; fd_t *fd = NULL; struct iatt preiatt = { 0, }, postiatt = { 0, }; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); ret = syncop_fsync(subvol, fd, 1, &preiatt, &postiatt, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); if (ret >= 0) { if (prestat) glfs_iatt_to_statx(glfd->fs, &preiatt, prestat); if (poststat) glfs_iatt_to_statx(glfd->fs, &postiatt, poststat); } out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (fop_attr) dict_unref(fop_attr); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC(glfs_fdatasync34, glfs_fdatasync, 3.4.0) int pub_glfs_fdatasync34(struct glfs_fd *glfd) { return glfs_fdatasync_common(glfd, NULL, NULL); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fdatasync, 6.0) int pub_glfs_fdatasync(struct glfs_fd *glfd, struct glfs_stat *prestat, struct glfs_stat *poststat) { return glfs_fdatasync_common(glfd, prestat, poststat); } GFAPI_SYMVER_PUBLIC(glfs_fdatasync_async34, glfs_fdatasync_async, 3.4.0) int pub_glfs_fdatasync_async34(struct glfs_fd *glfd, glfs_io_cbk34 fn, void *data) { int ret = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); ret = glfs_fsync_async_common(glfd, _gf_true, (void *)fn, data, 1); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fdatasync_async, 6.0) int pub_glfs_fdatasync_async(struct glfs_fd *glfd, glfs_io_cbk fn, void *data) { int ret = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); ret = glfs_fsync_async_common(glfd, _gf_false, fn, data, 1); __GLFS_EXIT_FS; invalid_fs: return ret; } static int glfs_ftruncate_common(struct glfs_fd *glfd, off_t offset, struct glfs_stat *prestat, struct glfs_stat *poststat) { int ret = -1; xlator_t *subvol = NULL; fd_t *fd = NULL; struct iatt preiatt = { 0, }, postiatt = { 0, }; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); ret = syncop_ftruncate(subvol, fd, offset, &preiatt, &postiatt, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); if (ret >= 0) { if (prestat) glfs_iatt_to_statx(glfd->fs, &preiatt, prestat); if (poststat) glfs_iatt_to_statx(glfd->fs, &postiatt, poststat); } out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (fop_attr) dict_unref(fop_attr); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC(glfs_ftruncate34, glfs_ftruncate, 3.4.0) int pub_glfs_ftruncate34(struct glfs_fd *glfd, off_t offset) { return glfs_ftruncate_common(glfd, offset, NULL, NULL); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_ftruncate, 6.0) int pub_glfs_ftruncate(struct glfs_fd *glfd, off_t offset, struct glfs_stat *prestat, struct glfs_stat *poststat) { return glfs_ftruncate_common(glfd, offset, prestat, poststat); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_truncate, 3.7.15) int pub_glfs_truncate(struct glfs *fs, const char *path, off_t length) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; ret = syncop_truncate(subvol, &loc, length, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } static int glfs_ftruncate_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *prebuf, struct iatt *postbuf, dict_t *xdata) { glfs_io_async_cbk(op_ret, op_errno, frame, cookie, NULL, 0, prebuf, postbuf); return 0; } static int glfs_ftruncate_async_common(struct glfs_fd *glfd, off_t offset, gf_boolean_t oldcb, glfs_io_cbk fn, void *data) { struct glfs_io *gio = NULL; int ret = -1; call_frame_t *frame = NULL; xlator_t *subvol = NULL; fd_t *fd = NULL; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); /* Need to take explicit ref so that the fd * is not destroyed before the fop is complete */ GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { errno = EBADFD; goto out; } frame = syncop_create_frame(THIS); if (!frame) { errno = ENOMEM; goto out; } gio = GF_CALLOC(1, sizeof(*gio), glfs_mt_glfs_io_t); if (!gio) { errno = ENOMEM; goto out; } gio->op = GF_FOP_FTRUNCATE; gio->glfd = glfd; gio->offset = offset; gio->oldcb = oldcb; gio->fn = fn; gio->data = data; frame->local = gio; ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); STACK_WIND_COOKIE(frame, glfs_ftruncate_async_cbk, subvol, subvol, subvol->fops->ftruncate, fd, offset, fop_attr); ret = 0; out: if (ret) { if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); GF_FREE(gio); if (frame) STACK_DESTROY(frame->root); glfs_subvol_done(glfd->fs, subvol); } if (fop_attr) dict_unref(fop_attr); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC(glfs_ftruncate_async34, glfs_ftruncate_async, 3.4.0) int pub_glfs_ftruncate_async34(struct glfs_fd *glfd, off_t offset, glfs_io_cbk34 fn, void *data) { return glfs_ftruncate_async_common(glfd, offset, _gf_true, (void *)fn, data); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_ftruncate_async, 6.0) int pub_glfs_ftruncate_async(struct glfs_fd *glfd, off_t offset, glfs_io_cbk fn, void *data) { return glfs_ftruncate_async_common(glfd, offset, _gf_false, fn, data); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_access, 3.4.0) int pub_glfs_access(struct glfs *fs, const char *path, int mode) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; ret = syncop_access(subvol, &loc, mode, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_symlink, 3.4.0) int pub_glfs_symlink(struct glfs *fs, const char *data, const char *path) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } xattr_req = dict_new(); if (!xattr_req) { ret = -1; errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { ret = -1; errno = ENOMEM; goto out; } retry: ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (loc.inode) { errno = EEXIST; ret = -1; goto out; } if (ret == -1 && errno != ENOENT) /* Any other type of error is fatal */ goto out; if (ret == -1 && errno == ENOENT && !loc.parent) /* The parent directory or an ancestor even higher does not exist */ goto out; /* ret == -1 && errno == ENOENT */ loc.inode = inode_new(loc.parent->table); if (!loc.inode) { ret = -1; errno = ENOMEM; goto out; } ret = syncop_symlink(subvol, &loc, data, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0) ret = glfs_loc_link(&loc, &iatt); out: loc_wipe(&loc); if (xattr_req) dict_unref(xattr_req); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readlink, 3.4.0) int pub_glfs_readlink(struct glfs *fs, const char *path, char *buf, size_t bufsiz) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; char *linkval = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; if (iatt.ia_type != IA_IFLNK) { ret = -1; errno = EINVAL; goto out; } ret = syncop_readlink(subvol, &loc, &linkval, bufsiz, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret > 0) { memcpy(buf, linkval, ret); GF_FREE(linkval); } ESTALE_RETRY(ret, errno, reval, &loc, retry); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_mknod, 3.4.0) int pub_glfs_mknod(struct glfs *fs, const char *path, mode_t mode, dev_t dev) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } xattr_req = dict_new(); if (!xattr_req) { ret = -1; errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { ret = -1; errno = ENOMEM; goto out; } retry: ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (loc.inode) { errno = EEXIST; ret = -1; goto out; } if (ret == -1 && errno != ENOENT) /* Any other type of error is fatal */ goto out; if (ret == -1 && errno == ENOENT && !loc.parent) /* The parent directory or an ancestor even higher does not exist */ goto out; /* ret == -1 && errno == ENOENT */ loc.inode = inode_new(loc.parent->table); if (!loc.inode) { ret = -1; errno = ENOMEM; goto out; } ret = syncop_mknod(subvol, &loc, mode, dev, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0) ret = glfs_loc_link(&loc, &iatt); out: loc_wipe(&loc); if (xattr_req) dict_unref(xattr_req); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_mkdir, 3.4.0) int pub_glfs_mkdir(struct glfs *fs, const char *path, mode_t mode) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } xattr_req = dict_new(); if (!xattr_req) { ret = -1; errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { ret = -1; errno = ENOMEM; goto out; } retry: ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (loc.inode) { errno = EEXIST; ret = -1; goto out; } if (ret == -1 && errno != ENOENT) /* Any other type of error is fatal */ goto out; if (ret == -1 && errno == ENOENT && !loc.parent) /* The parent directory or an ancestor even higher does not exist */ goto out; /* ret == -1 && errno == ENOENT */ loc.inode = inode_new(loc.parent->table); if (!loc.inode) { ret = -1; errno = ENOMEM; goto out; } ret = syncop_mkdir(subvol, &loc, mode, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0) ret = glfs_loc_link(&loc, &iatt); out: loc_wipe(&loc); if (xattr_req) dict_unref(xattr_req); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_unlink, 3.4.0) int pub_glfs_unlink(struct glfs *fs, const char *path) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; if (iatt.ia_type == IA_IFDIR) { ret = -1; errno = EISDIR; goto out; } /* TODO: Add leaseid */ ret = syncop_unlink(subvol, &loc, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0) ret = glfs_loc_unlink(&loc); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_rmdir, 3.4.0) int pub_glfs_rmdir(struct glfs *fs, const char *path) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; if (iatt.ia_type != IA_IFDIR) { ret = -1; errno = ENOTDIR; goto out; } ret = syncop_rmdir(subvol, &loc, 0, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0) ret = glfs_loc_unlink(&loc); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_rename, 3.4.0) int pub_glfs_rename(struct glfs *fs, const char *oldpath, const char *newpath) { int ret = -1; xlator_t *subvol = NULL; loc_t oldloc = { 0, }; loc_t newloc = { 0, }; struct iatt oldiatt = { 0, }; struct iatt newiatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_lresolve(fs, subvol, oldpath, &oldloc, &oldiatt, reval); ESTALE_RETRY(ret, errno, reval, &oldloc, retry); if (ret) goto out; retrynew: ret = glfs_lresolve(fs, subvol, newpath, &newloc, &newiatt, reval); ESTALE_RETRY(ret, errno, reval, &newloc, retrynew); if (ret && errno != ENOENT && newloc.parent) goto out; if (newiatt.ia_type != IA_INVAL) { if ((oldiatt.ia_type == IA_IFDIR) != (newiatt.ia_type == IA_IFDIR)) { /* Either both old and new must be dirs, * or both must be non-dirs. Else, fail. */ ret = -1; errno = EISDIR; goto out; } } /* TODO: - check if new or old is a prefix of the other, and fail EINVAL * - Add leaseid */ ret = syncop_rename(subvol, &oldloc, &newloc, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret == -1 && errno == ESTALE) { if (reval < DEFAULT_REVAL_COUNT) { reval++; loc_wipe(&oldloc); loc_wipe(&newloc); goto retry; } } if (ret == 0) { inode_rename(oldloc.parent->table, oldloc.parent, oldloc.name, newloc.parent, newloc.name, oldloc.inode, &oldiatt); if (newloc.inode && !inode_has_dentry(newloc.inode)) inode_forget(newloc.inode, 0); } out: loc_wipe(&oldloc); loc_wipe(&newloc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_link, 3.4.0) int pub_glfs_link(struct glfs *fs, const char *oldpath, const char *newpath) { int ret = -1; xlator_t *subvol = NULL; loc_t oldloc = { 0, }; loc_t newloc = { 0, }; struct iatt oldiatt = { 0, }; struct iatt newiatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_lresolve(fs, subvol, oldpath, &oldloc, &oldiatt, reval); ESTALE_RETRY(ret, errno, reval, &oldloc, retry); if (ret) goto out; retrynew: ret = glfs_lresolve(fs, subvol, newpath, &newloc, &newiatt, reval); ESTALE_RETRY(ret, errno, reval, &newloc, retrynew); if (ret == 0) { ret = -1; errno = EEXIST; goto out; } if (oldiatt.ia_type == IA_IFDIR) { ret = -1; errno = EISDIR; goto out; } /* Filling the inode of the hard link to be same as that of the original file */ if (newloc.inode) { inode_unref(newloc.inode); newloc.inode = NULL; } newloc.inode = inode_ref(oldloc.inode); ret = syncop_link(subvol, &oldloc, &newloc, &newiatt, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret == -1 && errno == ESTALE) { loc_wipe(&oldloc); loc_wipe(&newloc); if (reval--) goto retry; } if (ret == 0) ret = glfs_loc_link(&newloc, &newiatt); out: loc_wipe(&oldloc); loc_wipe(&newloc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_opendir, 3.4.0) struct glfs_fd * pub_glfs_opendir(struct glfs *fs, const char *path) { int ret = -1; struct glfs_fd *glfd = NULL; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } glfd = glfs_fd_new(fs); if (!glfd) goto out; retry: ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; if (!IA_ISDIR(iatt.ia_type)) { ret = -1; errno = ENOTDIR; goto out; } if (glfd->fd) { /* Retry. Safe to touch glfd->fd as we still have not glfs_fd_bind() yet. */ fd_unref(glfd->fd); glfd->fd = NULL; } glfd->fd = fd_create(loc.inode, getpid()); if (!glfd->fd) { ret = -1; errno = ENOMEM; goto out; } ret = syncop_opendir(subvol, &loc, glfd->fd, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); out: loc_wipe(&loc); if (ret && glfd) { GF_REF_PUT(glfd); glfd = NULL; } else if (glfd) { glfd_set_state_bind(glfd); } glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return glfd; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_closedir, 3.4.0) int pub_glfs_closedir(struct glfs_fd *glfd) { int ret = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); gf_dirent_free(list_entry(&glfd->entries, gf_dirent_t, list)); glfs_mark_glfd_for_deletion(glfd); __GLFS_EXIT_FS; ret = 0; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_telldir, 3.4.0) long pub_glfs_telldir(struct glfs_fd *fd) { if (fd == NULL) { errno = EBADF; return -1; } return fd->offset; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_seekdir, 3.4.0) void pub_glfs_seekdir(struct glfs_fd *fd, long offset) { gf_dirent_t *entry = NULL; gf_dirent_t *tmp = NULL; if (fd == NULL) { errno = EBADF; return; } if (fd->offset == offset) return; fd->offset = offset; fd->next = NULL; list_for_each_entry_safe(entry, tmp, &fd->entries, list) { if (entry->d_off != offset) continue; if (&tmp->list != &fd->entries) { /* found! */ fd->next = tmp; return; } } /* could not find entry at requested offset in the cache. next readdir_r() will result in glfd_entry_refresh() */ } static int glfs_discard_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *preop_stbuf, struct iatt *postop_stbuf, dict_t *xdata) { glfs_io_async_cbk(op_ret, op_errno, frame, cookie, NULL, 0, preop_stbuf, postop_stbuf); return 0; } static int glfs_discard_async_common(struct glfs_fd *glfd, off_t offset, size_t len, gf_boolean_t oldcb, glfs_io_cbk fn, void *data) { struct glfs_io *gio = NULL; int ret = -1; call_frame_t *frame = NULL; xlator_t *subvol = NULL; fd_t *fd = NULL; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); /* Need to take explicit ref so that the fd * is not destroyed before the fop is complete */ GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { errno = EBADFD; goto out; } frame = syncop_create_frame(THIS); if (!frame) { errno = ENOMEM; goto out; } gio = GF_CALLOC(1, sizeof(*gio), glfs_mt_glfs_io_t); if (!gio) { errno = ENOMEM; goto out; } gio->op = GF_FOP_DISCARD; gio->glfd = glfd; gio->offset = offset; gio->count = len; gio->oldcb = oldcb; gio->fn = fn; gio->data = data; frame->local = gio; ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); STACK_WIND_COOKIE(frame, glfs_discard_async_cbk, subvol, subvol, subvol->fops->discard, fd, offset, len, fop_attr); ret = 0; out: if (ret) { if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); GF_FREE(gio); if (frame) STACK_DESTROY(frame->root); glfs_subvol_done(glfd->fs, subvol); } __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC(glfs_discard_async35, glfs_discard_async, 3.5.0) int pub_glfs_discard_async35(struct glfs_fd *glfd, off_t offset, size_t len, glfs_io_cbk34 fn, void *data) { return glfs_discard_async_common(glfd, offset, len, _gf_true, (void *)fn, data); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_discard_async, 6.0) int pub_glfs_discard_async(struct glfs_fd *glfd, off_t offset, size_t len, glfs_io_cbk fn, void *data) { return glfs_discard_async_common(glfd, offset, len, _gf_false, fn, data); } static int glfs_zerofill_async_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *preop_stbuf, struct iatt *postop_stbuf, dict_t *xdata) { glfs_io_async_cbk(op_ret, op_errno, frame, cookie, NULL, 0, preop_stbuf, postop_stbuf); return 0; } static int glfs_zerofill_async_common(struct glfs_fd *glfd, off_t offset, off_t len, gf_boolean_t oldcb, glfs_io_cbk fn, void *data) { struct glfs_io *gio = NULL; int ret = -1; call_frame_t *frame = NULL; xlator_t *subvol = NULL; fd_t *fd = NULL; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); /* Need to take explicit ref so that the fd * is not destroyed before the fop is complete */ GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { errno = EBADFD; goto out; } frame = syncop_create_frame(THIS); if (!frame) { errno = ENOMEM; goto out; } gio = GF_CALLOC(1, sizeof(*gio), glfs_mt_glfs_io_t); if (!gio) { errno = ENOMEM; goto out; } gio->op = GF_FOP_ZEROFILL; gio->glfd = glfd; gio->offset = offset; gio->count = len; gio->oldcb = oldcb; gio->fn = fn; gio->data = data; frame->local = gio; ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); STACK_WIND_COOKIE(frame, glfs_zerofill_async_cbk, subvol, subvol, subvol->fops->zerofill, fd, offset, len, fop_attr); ret = 0; out: if (ret) { if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); GF_FREE(gio); if (frame) STACK_DESTROY(frame->root); glfs_subvol_done(glfd->fs, subvol); } if (fop_attr) dict_unref(fop_attr); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC(glfs_zerofill_async35, glfs_zerofill_async, 3.5.0) int pub_glfs_zerofill_async35(struct glfs_fd *glfd, off_t offset, off_t len, glfs_io_cbk34 fn, void *data) { return glfs_zerofill_async_common(glfd, offset, len, _gf_true, (void *)fn, data); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_zerofill_async, 6.0) int pub_glfs_zerofill_async(struct glfs_fd *glfd, off_t offset, off_t len, glfs_io_cbk fn, void *data) { return glfs_zerofill_async_common(glfd, offset, len, _gf_false, fn, data); } void gf_dirent_to_dirent(gf_dirent_t *gf_dirent, struct dirent *dirent) { dirent->d_ino = gf_dirent->d_ino; #ifdef _DIRENT_HAVE_D_OFF dirent->d_off = gf_dirent->d_off; #endif #ifdef _DIRENT_HAVE_D_TYPE dirent->d_type = gf_dirent->d_type; #endif #ifdef _DIRENT_HAVE_D_NAMLEN dirent->d_namlen = strlen(gf_dirent->d_name); #endif snprintf(dirent->d_name, NAME_MAX + 1, "%s", gf_dirent->d_name); } int glfd_entry_refresh(struct glfs_fd *glfd, int plus) { xlator_t *subvol = NULL; gf_dirent_t entries; gf_dirent_t old; gf_dirent_t *entry = NULL; int ret = -1; fd_t *fd = NULL; subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } if (fd->inode->ia_type != IA_IFDIR) { ret = -1; errno = EBADF; goto out; } INIT_LIST_HEAD(&entries.list); INIT_LIST_HEAD(&old.list); if (plus) ret = syncop_readdirp(subvol, fd, 131072, glfd->offset, &entries, NULL, NULL); else ret = syncop_readdir(subvol, fd, 131072, glfd->offset, &entries, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret >= 0) { if (plus) { list_for_each_entry(entry, &entries.list, list) { if ((!entry->inode && (!IA_ISDIR(entry->d_stat.ia_type))) || ((entry->d_stat.ia_ctime == 0) && !inode_dir_or_parentdir(entry))) { /* entry->inode for directories will be * always set to null to force a lookup * on the dentry. Hence to not degrade * readdir performance, we skip lookups * for directory entries. Also we will have * proper stat if directory present on * hashed subvolume. * * In addition, if the stat is invalid, force * lookup to fetch proper stat. */ gf_fill_iatt_for_dirent(entry, fd->inode, subvol); } } gf_link_inodes_from_dirent(fd->inode, &entries); } list_splice_init(&glfd->entries, &old.list); /* If glfd->next is non null means, this entry is from the old * list, there is no point in keeping the free'ed data. Hence * set to NULL; */ glfd->next = NULL; list_splice_init(&entries.list, &glfd->entries); /* spurious errno is dangerous for glfd_entry_next() */ errno = 0; } if ((ret > 0) && !list_empty(&glfd->entries)) { glfd->next = list_entry(glfd->entries.next, gf_dirent_t, list); } gf_dirent_free(&old); out: if (fd) fd_unref(fd); glfs_subvol_done(glfd->fs, subvol); return ret; } gf_dirent_t * glfd_entry_next(struct glfs_fd *glfd, int plus) { gf_dirent_t *entry = NULL; int ret = -1; if (!glfd->offset || !glfd->next) { ret = glfd_entry_refresh(glfd, plus); if (ret < 0) return NULL; } entry = glfd->next; if (!entry) return NULL; if (&entry->next->list == &glfd->entries) glfd->next = NULL; else glfd->next = entry->next; glfd->offset = entry->d_off; return entry; } struct dirent * glfs_readdirbuf_get(struct glfs_fd *glfd) { struct dirent *buf = NULL; LOCK(&glfd->fd->lock); { buf = glfd->readdirbuf; if (buf) { memset(buf, 0, READDIRBUF_SIZE); goto unlock; } buf = GF_CALLOC(1, READDIRBUF_SIZE, glfs_mt_readdirbuf_t); if (!buf) { errno = ENOMEM; goto unlock; } glfd->readdirbuf = buf; } unlock: UNLOCK(&glfd->fd->lock); return buf; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readdirplus_r, 3.4.0) int pub_glfs_readdirplus_r(struct glfs_fd *glfd, struct stat *stat, struct dirent *ext, struct dirent **res) { int ret = 0; gf_dirent_t *entry = NULL; struct dirent *buf = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); errno = 0; if (ext) buf = ext; else buf = glfs_readdirbuf_get(glfd); if (!buf) { errno = ENOMEM; ret = -1; goto out; } entry = glfd_entry_next(glfd, !!stat); if (errno) ret = -1; if (res) { if (entry) *res = buf; else *res = NULL; } if (entry) { gf_dirent_to_dirent(entry, buf); if (stat) glfs_iatt_to_stat(glfd->fs, &entry->d_stat, stat); } out: if (glfd) GF_REF_PUT(glfd); __GLFS_EXIT_FS; return ret; invalid_fs: return -1; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readdir_r, 3.4.0) int pub_glfs_readdir_r(struct glfs_fd *glfd, struct dirent *buf, struct dirent **res) { return pub_glfs_readdirplus_r(glfd, 0, buf, res); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readdirplus, 3.5.0) struct dirent * pub_glfs_readdirplus(struct glfs_fd *glfd, struct stat *stat) { struct dirent *res = NULL; int ret = -1; ret = pub_glfs_readdirplus_r(glfd, stat, NULL, &res); if (ret) return NULL; return res; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readdir, 3.5.0) struct dirent * pub_glfs_readdir(struct glfs_fd *glfd) { return pub_glfs_readdirplus(glfd, NULL); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_statvfs, 3.4.0) int pub_glfs_statvfs(struct glfs *fs, const char *path, struct statvfs *buf) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; ret = syncop_statfs(subvol, &loc, buf, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setattr, 6.0) int pub_glfs_setattr(struct glfs *fs, const char *path, struct glfs_stat *stat, int follow) { int ret = -1; int glvalid; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt riatt = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); GF_VALIDATE_OR_GOTO("glfs_setattr", stat, out); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: if (follow) ret = glfs_resolve(fs, subvol, path, &loc, &riatt, reval); else ret = glfs_lresolve(fs, subvol, path, &loc, &riatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; glfs_iatt_from_statx(&iatt, stat); glfsflags_from_gfapiflags(stat, &glvalid); /* TODO : Add leaseid */ ret = syncop_setattr(subvol, &loc, &iatt, glvalid, 0, 0, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fsetattr, 6.0) int pub_glfs_fsetattr(struct glfs_fd *glfd, struct glfs_stat *stat) { int ret = -1; int glvalid; struct iatt iatt = { 0, }; xlator_t *subvol = NULL; fd_t *fd = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); GF_VALIDATE_OR_GOTO("glfs_fsetattr", stat, out); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } glfs_iatt_from_statx(&iatt, stat); glfsflags_from_gfapiflags(stat, &glvalid); /* TODO : Add leaseid */ ret = syncop_fsetattr(subvol, fd, &iatt, glvalid, 0, 0, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_chmod, 3.4.0) int pub_glfs_chmod(struct glfs *fs, const char *path, mode_t mode) { int ret = -1; struct glfs_stat stat = { 0, }; stat.glfs_st_mode = mode; stat.glfs_st_mask = GLFS_STAT_MODE; ret = glfs_setattr(fs, path, &stat, 1); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fchmod, 3.4.0) int pub_glfs_fchmod(struct glfs_fd *glfd, mode_t mode) { int ret = -1; struct glfs_stat stat = { 0, }; stat.glfs_st_mode = mode; stat.glfs_st_mask = GLFS_STAT_MODE; ret = glfs_fsetattr(glfd, &stat); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_chown, 3.4.0) int pub_glfs_chown(struct glfs *fs, const char *path, uid_t uid, gid_t gid) { int ret = 0; struct glfs_stat stat = { 0, }; if (uid != (uid_t)-1) { stat.glfs_st_uid = uid; stat.glfs_st_mask = GLFS_STAT_UID; } if (gid != (uid_t)-1) { stat.glfs_st_gid = gid; stat.glfs_st_mask = stat.glfs_st_mask | GLFS_STAT_GID; } if (stat.glfs_st_mask) ret = glfs_setattr(fs, path, &stat, 1); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lchown, 3.4.0) int pub_glfs_lchown(struct glfs *fs, const char *path, uid_t uid, gid_t gid) { int ret = 0; struct glfs_stat stat = { 0, }; if (uid != (uid_t)-1) { stat.glfs_st_uid = uid; stat.glfs_st_mask = GLFS_STAT_UID; } if (gid != (uid_t)-1) { stat.glfs_st_gid = gid; stat.glfs_st_mask = stat.glfs_st_mask | GLFS_STAT_GID; } if (stat.glfs_st_mask) ret = glfs_setattr(fs, path, &stat, 0); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fchown, 3.4.0) int pub_glfs_fchown(struct glfs_fd *glfd, uid_t uid, gid_t gid) { int ret = 0; struct glfs_stat stat = { 0, }; if (uid != (uid_t)-1) { stat.glfs_st_uid = uid; stat.glfs_st_mask = GLFS_STAT_UID; } if (gid != (uid_t)-1) { stat.glfs_st_gid = gid; stat.glfs_st_mask = stat.glfs_st_mask | GLFS_STAT_GID; } if (stat.glfs_st_mask) ret = glfs_fsetattr(glfd, &stat); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_utimens, 3.4.0) int pub_glfs_utimens(struct glfs *fs, const char *path, const struct timespec times[2]) { int ret = -1; struct glfs_stat stat = { 0, }; stat.glfs_st_atime = times[0]; stat.glfs_st_mtime = times[1]; stat.glfs_st_mask = GLFS_STAT_ATIME | GLFS_STAT_MTIME; ret = glfs_setattr(fs, path, &stat, 1); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lutimens, 3.4.0) int pub_glfs_lutimens(struct glfs *fs, const char *path, const struct timespec times[2]) { int ret = -1; struct glfs_stat stat = { 0, }; stat.glfs_st_atime = times[0]; stat.glfs_st_mtime = times[1]; stat.glfs_st_mask = GLFS_STAT_ATIME | GLFS_STAT_MTIME; ret = glfs_setattr(fs, path, &stat, 0); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_futimens, 3.4.0) int pub_glfs_futimens(struct glfs_fd *glfd, const struct timespec times[2]) { int ret = -1; struct glfs_stat stat = { 0, }; stat.glfs_st_atime = times[0]; stat.glfs_st_mtime = times[1]; stat.glfs_st_mask = GLFS_STAT_ATIME | GLFS_STAT_MTIME; ret = glfs_fsetattr(glfd, &stat); return ret; } int glfs_getxattr_process(void *value, size_t size, dict_t *xattr, const char *name) { data_t *data = NULL; int ret = -1; data = dict_get(xattr, (char *)name); if (!data) { errno = ENODATA; ret = -1; goto out; } ret = data->len; if (!value || !size) goto out; if (size < ret) { ret = -1; errno = ERANGE; goto out; } memcpy(value, data->data, ret); out: return ret; } ssize_t glfs_getxattr_common(struct glfs *fs, const char *path, const char *name, void *value, size_t size, int follow) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; dict_t *xattr = NULL; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); if (!name || *name == '\0') { ret = -1; errno = EINVAL; goto out; } if (strlen(name) > GF_XATTR_NAME_MAX) { ret = -1; errno = ENAMETOOLONG; goto out; } subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: if (follow) ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); else ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; ret = syncop_getxattr(subvol, &loc, &xattr, name, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; ret = glfs_getxattr_process(value, size, xattr, name); out: loc_wipe(&loc); if (xattr) dict_unref(xattr); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_getxattr, 3.4.0) ssize_t pub_glfs_getxattr(struct glfs *fs, const char *path, const char *name, void *value, size_t size) { return glfs_getxattr_common(fs, path, name, value, size, 1); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lgetxattr, 3.4.0) ssize_t pub_glfs_lgetxattr(struct glfs *fs, const char *path, const char *name, void *value, size_t size) { return glfs_getxattr_common(fs, path, name, value, size, 0); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fgetxattr, 3.4.0) ssize_t pub_glfs_fgetxattr(struct glfs_fd *glfd, const char *name, void *value, size_t size) { int ret = -1; xlator_t *subvol = NULL; dict_t *xattr = NULL; fd_t *fd = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); if (!name || *name == '\0') { ret = -1; errno = EINVAL; goto out; } if (strlen(name) > GF_XATTR_NAME_MAX) { ret = -1; errno = ENAMETOOLONG; goto out; } subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } ret = syncop_fgetxattr(subvol, fd, &xattr, name, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret) goto out; ret = glfs_getxattr_process(value, size, xattr, name); out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (xattr) dict_unref(xattr); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } /* filter out xattrs that need not be visible on the * client application. */ static int gfapi_filter_xattr(char *key) { int need_filter = 0; /* If there are by chance any internal virtual xattrs (those starting with * 'glusterfs.'), filter them */ if (strncmp("glusterfs.", key, SLEN("glusterfs.")) == 0) need_filter = 1; return need_filter; } int glfs_listxattr_process(void *value, size_t size, dict_t *xattr) { int ret = -1; if (!xattr) goto out; ret = dict_keys_join(NULL, 0, xattr, gfapi_filter_xattr); if (!value || !size) goto out; if (size < ret) { ret = -1; errno = ERANGE; } else { dict_keys_join(value, size, xattr, gfapi_filter_xattr); } out: return ret; } ssize_t glfs_listxattr_common(struct glfs *fs, const char *path, void *value, size_t size, int follow) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; dict_t *xattr = NULL; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: if (follow) ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); else ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; ret = syncop_getxattr(subvol, &loc, &xattr, NULL, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; ret = glfs_listxattr_process(value, size, xattr); out: loc_wipe(&loc); if (xattr) dict_unref(xattr); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_listxattr, 3.4.0) ssize_t pub_glfs_listxattr(struct glfs *fs, const char *path, void *value, size_t size) { return glfs_listxattr_common(fs, path, value, size, 1); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_llistxattr, 3.4.0) ssize_t pub_glfs_llistxattr(struct glfs *fs, const char *path, void *value, size_t size) { return glfs_listxattr_common(fs, path, value, size, 0); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_flistxattr, 3.4.0) ssize_t pub_glfs_flistxattr(struct glfs_fd *glfd, void *value, size_t size) { int ret = -1; xlator_t *subvol = NULL; dict_t *xattr = NULL; fd_t *fd = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } ret = syncop_fgetxattr(subvol, fd, &xattr, NULL, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret) goto out; ret = glfs_listxattr_process(value, size, xattr); out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (xattr) dict_unref(xattr); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } int glfs_setxattr_common(struct glfs *fs, const char *path, const char *name, const void *value, size_t size, int flags, int follow) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; dict_t *xattr = NULL; int reval = 0; void *value_cp = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); if (!name || *name == '\0') { ret = -1; errno = EINVAL; goto out; } if (strlen(name) > GF_XATTR_NAME_MAX) { ret = -1; errno = ENAMETOOLONG; goto out; } subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: if (follow) ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); else ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; value_cp = gf_memdup(value, size); GF_CHECK_ALLOC_AND_LOG(subvol->name, value_cp, ret, "Failed to" " duplicate setxattr value", out); xattr = dict_for_key_value(name, value_cp, size, _gf_false); if (!xattr) { GF_FREE(value_cp); ret = -1; errno = ENOMEM; goto out; } ret = syncop_setxattr(subvol, &loc, xattr, flags, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: loc_wipe(&loc); if (xattr) dict_unref(xattr); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setxattr, 3.4.0) int pub_glfs_setxattr(struct glfs *fs, const char *path, const char *name, const void *value, size_t size, int flags) { return glfs_setxattr_common(fs, path, name, value, size, flags, 1); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lsetxattr, 3.4.0) int pub_glfs_lsetxattr(struct glfs *fs, const char *path, const char *name, const void *value, size_t size, int flags) { return glfs_setxattr_common(fs, path, name, value, size, flags, 0); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fsetxattr, 3.4.0) int pub_glfs_fsetxattr(struct glfs_fd *glfd, const char *name, const void *value, size_t size, int flags) { int ret = -1; xlator_t *subvol = NULL; dict_t *xattr = NULL; fd_t *fd = NULL; void *value_cp = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); if (!name || *name == '\0') { ret = -1; errno = EINVAL; goto out; } if (strlen(name) > GF_XATTR_NAME_MAX) { ret = -1; errno = ENAMETOOLONG; goto out; } subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } value_cp = gf_memdup(value, size); GF_CHECK_ALLOC_AND_LOG(subvol->name, value_cp, ret, "Failed to" " duplicate setxattr value", out); xattr = dict_for_key_value(name, value_cp, size, _gf_false); if (!xattr) { GF_FREE(value_cp); ret = -1; errno = ENOMEM; goto out; } ret = syncop_fsetxattr(subvol, fd, xattr, flags, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: if (xattr) dict_unref(xattr); if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } int glfs_removexattr_common(struct glfs *fs, const char *path, const char *name, int follow) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: if (follow) ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); else ret = glfs_lresolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; ret = syncop_removexattr(subvol, &loc, name, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_removexattr, 3.4.0) int pub_glfs_removexattr(struct glfs *fs, const char *path, const char *name) { return glfs_removexattr_common(fs, path, name, 1); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lremovexattr, 3.4.0) int pub_glfs_lremovexattr(struct glfs *fs, const char *path, const char *name) { return glfs_removexattr_common(fs, path, name, 0); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fremovexattr, 3.4.0) int pub_glfs_fremovexattr(struct glfs_fd *glfd, const char *name) { int ret = -1; xlator_t *subvol = NULL; fd_t *fd = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } ret = syncop_fremovexattr(subvol, fd, name, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fallocate, 3.5.0) int pub_glfs_fallocate(struct glfs_fd *glfd, int keep_size, off_t offset, size_t len) { int ret = -1; xlator_t *subvol = NULL; fd_t *fd = NULL; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); ret = syncop_fallocate(subvol, fd, keep_size, offset, len, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (fop_attr) dict_unref(fop_attr); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_discard, 3.5.0) int pub_glfs_discard(struct glfs_fd *glfd, off_t offset, size_t len) { int ret = -1; xlator_t *subvol = NULL; fd_t *fd = NULL; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); ret = syncop_discard(subvol, fd, offset, len, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (fop_attr) dict_unref(fop_attr); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_zerofill, 3.5.0) int pub_glfs_zerofill(struct glfs_fd *glfd, off_t offset, off_t len) { int ret = -1; xlator_t *subvol = NULL; fd_t *fd = NULL; dict_t *fop_attr = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { errno = EBADFD; goto out; } ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); ret = syncop_zerofill(subvol, fd, offset, len, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); if (fop_attr) dict_unref(fop_attr); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_chdir, 3.4.0) int pub_glfs_chdir(struct glfs *fs, const char *path) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; if (!IA_ISDIR(iatt.ia_type)) { ret = -1; errno = ENOTDIR; goto out; } glfs_cwd_set(fs, loc.inode); out: loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fchdir, 3.4.0) int pub_glfs_fchdir(struct glfs_fd *glfd) { int ret = -1; inode_t *inode = NULL; xlator_t *subvol = NULL; fd_t *fd = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } inode = fd->inode; if (!IA_ISDIR(inode->ia_type)) { ret = -1; errno = ENOTDIR; goto out; } glfs_cwd_set(glfd->fs, inode); ret = 0; out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } static gf_boolean_t warn_realpath = _gf_true; /* log once */ static char * glfs_realpath_common(struct glfs *fs, const char *path, char *resolved_path, gf_boolean_t warn_deprecated) { int ret = -1; char *retpath = NULL; char *allocpath = NULL; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int reval = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); if (resolved_path) retpath = resolved_path; else if (warn_deprecated) { retpath = allocpath = malloc(PATH_MAX + 1); if (warn_realpath) { warn_realpath = _gf_false; gf_log(THIS->name, GF_LOG_WARNING, "this application " "is compiled against an old version of " "libgfapi, it should use glfs_free() to " "release the path returned by " "glfs_realpath()"); } } else { retpath = allocpath = GLFS_CALLOC(1, PATH_MAX + 1, NULL, glfs_mt_realpath_t); } if (!retpath) { ret = -1; errno = ENOMEM; goto out; } subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } retry: ret = glfs_resolve(fs, subvol, path, &loc, &iatt, reval); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret) goto out; if (loc.path) { snprintf(retpath, PATH_MAX + 1, "%s", loc.path); } out: loc_wipe(&loc); if (ret == -1) { if (warn_deprecated && allocpath) free(allocpath); else if (allocpath) GLFS_FREE(allocpath); retpath = NULL; } glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return retpath; } GFAPI_SYMVER_PUBLIC(glfs_realpath34, glfs_realpath, 3.4.0) char * pub_glfs_realpath34(struct glfs *fs, const char *path, char *resolved_path) { return glfs_realpath_common(fs, path, resolved_path, _gf_true); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_realpath, 3.7.17) char * pub_glfs_realpath(struct glfs *fs, const char *path, char *resolved_path) { return glfs_realpath_common(fs, path, resolved_path, _gf_false); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_getcwd, 3.4.0) char * pub_glfs_getcwd(struct glfs *fs, char *buf, size_t n) { int ret = -1; inode_t *inode = NULL; char *path = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); if (!buf || n < 2) { ret = -1; errno = EINVAL; goto out; } inode = glfs_cwd_get(fs); if (!inode) { strncpy(buf, "/", n); ret = 0; goto out; } ret = inode_path(inode, 0, &path); if (n <= ret) { ret = -1; errno = ERANGE; goto out; } strncpy(buf, path, n); ret = 0; out: GF_FREE(path); if (inode) inode_unref(inode); __GLFS_EXIT_FS; invalid_fs: if (ret < 0) return NULL; return buf; } static void gf_flock_to_flock(struct gf_flock *gf_flock, struct flock *flock) { flock->l_type = gf_flock->l_type; flock->l_whence = gf_flock->l_whence; flock->l_start = gf_flock->l_start; flock->l_len = gf_flock->l_len; flock->l_pid = gf_flock->l_pid; } static void gf_flock_from_flock(struct gf_flock *gf_flock, struct flock *flock) { gf_flock->l_type = flock->l_type; gf_flock->l_whence = flock->l_whence; gf_flock->l_start = flock->l_start; gf_flock->l_len = flock->l_len; gf_flock->l_pid = flock->l_pid; } static int glfs_lock_common(struct glfs_fd *glfd, int cmd, struct flock *flock, dict_t *xdata) { int ret = -1; xlator_t *subvol = NULL; struct gf_flock gf_flock = { 0, }; struct gf_flock saved_flock = { 0, }; fd_t *fd = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); if (!flock) { errno = EINVAL; goto out; } GF_REF_GET(glfd); subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } /* Generate glusterfs flock structure from client flock * structure to be processed by server */ gf_flock_from_flock(&gf_flock, flock); /* Keep another copy of flock for split/merge of locks * at client side */ gf_flock_from_flock(&saved_flock, flock); if (glfd->lk_owner.len != 0) { ret = syncopctx_setfslkowner(&glfd->lk_owner); if (ret) goto out; } ret = get_fop_attr_thrd_key(&xdata); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); ret = syncop_lk(subvol, fd, cmd, &gf_flock, xdata, NULL); DECODE_SYNCOP_ERR(ret); /* Convert back from gf_flock to flock as expected by application */ gf_flock_to_flock(&gf_flock, flock); if (ret == 0 && (cmd == F_SETLK || cmd == F_SETLKW)) { ret = fd_lk_insert_and_merge(fd, cmd, &saved_flock); if (ret) { gf_smsg(THIS->name, GF_LOG_ERROR, 0, API_MSG_LOCK_INSERT_MERGE_FAILED, "gfid=%s", uuid_utoa(fd->inode->gfid), NULL); ret = 0; } } out: if (fd) fd_unref(fd); if (glfd) GF_REF_PUT(glfd); glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_file_lock, 4.0.0) int pub_glfs_file_lock(struct glfs_fd *glfd, int cmd, struct flock *flock, glfs_lock_mode_t lk_mode) { int ret = -1; dict_t *xdata_in = NULL; if (lk_mode == GLFS_LK_MANDATORY) { /* Create a new dictionary */ xdata_in = dict_new(); if (xdata_in == NULL) { ret = -1; errno = ENOMEM; goto out; } /* Set GF_LK_MANDATORY internally within dictionary to map * GLFS_LK_MANDATORY */ ret = dict_set_uint32(xdata_in, GF_LOCK_MODE, GF_LK_MANDATORY); if (ret) { gf_smsg(THIS->name, GF_LOG_ERROR, 0, API_MSG_SETTING_LOCK_TYPE_FAILED, NULL); ret = -1; errno = ENOMEM; goto out; } } ret = glfs_lock_common(glfd, cmd, flock, xdata_in); out: if (xdata_in) dict_unref(xdata_in); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_posix_lock, 3.4.0) int pub_glfs_posix_lock(struct glfs_fd *glfd, int cmd, struct flock *flock) { return glfs_lock_common(glfd, cmd, flock, NULL); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fd_set_lkowner, 3.10.7) int pub_glfs_fd_set_lkowner(struct glfs_fd *glfd, void *data, int len) { int ret = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); if (!GF_REF_GET(glfd)) { goto invalid_fs; } GF_VALIDATE_OR_GOTO(THIS->name, data, out); if ((len <= 0) || (len > GFAPI_MAX_LOCK_OWNER_LEN)) { errno = EINVAL; gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ARG, "lk_owner len=%d", len, NULL); goto out; } glfd->lk_owner.len = len; memcpy(glfd->lk_owner.data, data, len); ret = 0; out: if (glfd) GF_REF_PUT(glfd); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_dup, 3.4.0) struct glfs_fd * pub_glfs_dup(struct glfs_fd *glfd) { xlator_t *subvol = NULL; fd_t *fd = NULL; struct glfs_fd *dupfd = NULL; struct glfs *fs = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); fs = glfd->fs; subvol = glfs_active_subvol(fs); if (!subvol) { errno = EIO; goto out; } fd = glfs_resolve_fd(fs, subvol, glfd); if (!fd) { errno = EBADFD; goto out; } dupfd = glfs_fd_new(fs); if (!dupfd) { errno = ENOMEM; goto out; } dupfd->fd = fd_ref(fd); dupfd->state = glfd->state; out: if (fd) fd_unref(fd); if (dupfd) glfs_fd_bind(dupfd); if (glfd) GF_REF_PUT(glfd); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return dupfd; } static void glfs_enqueue_upcall_data(struct glfs *fs, struct gf_upcall *upcall_data) { int ret = -1; upcall_entry *u_list = NULL; if (!fs || !upcall_data) goto out; u_list = GF_CALLOC(1, sizeof(*u_list), glfs_mt_upcall_entry_t); if (!u_list) { gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, "entry", NULL); goto out; } INIT_LIST_HEAD(&u_list->upcall_list); gf_uuid_copy(u_list->upcall_data.gfid, upcall_data->gfid); u_list->upcall_data.event_type = upcall_data->event_type; switch (upcall_data->event_type) { case GF_UPCALL_CACHE_INVALIDATION: ret = glfs_get_upcall_cache_invalidation(&u_list->upcall_data, upcall_data); break; case GF_UPCALL_RECALL_LEASE: ret = glfs_get_upcall_lease(&u_list->upcall_data, upcall_data); break; default: break; } if (ret) { gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ENTRY, NULL); goto out; } pthread_mutex_lock(&fs->upcall_list_mutex); { list_add_tail(&u_list->upcall_list, &fs->upcall_list); } pthread_mutex_unlock(&fs->upcall_list_mutex); ret = 0; out: if (ret && u_list) { GF_FREE(u_list->upcall_data.data); GF_FREE(u_list); } } static void glfs_free_upcall_lease(void *to_free) { struct glfs_upcall_lease *arg = to_free; if (!arg) return; if (arg->object) glfs_h_close(arg->object); GF_FREE(arg); } int glfs_recall_lease_fd(struct glfs *fs, struct gf_upcall *up_data) { struct gf_upcall_recall_lease *recall_lease = NULL; xlator_t *subvol = NULL; int ret = 0; inode_t *inode = NULL; struct glfs_fd *glfd = NULL; struct glfs_fd *tmp = NULL; struct list_head glfd_list; fd_t *fd = NULL; struct glfs_lease lease = { 0, }; GF_VALIDATE_OR_GOTO("gfapi", up_data, out); GF_VALIDATE_OR_GOTO("gfapi", fs, out); recall_lease = up_data->data; GF_VALIDATE_OR_GOTO("gfapi", recall_lease, out); INIT_LIST_HEAD(&glfd_list); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } gf_msg_debug(THIS->name, 0, "Recall lease received for gfid:%s", uuid_utoa(up_data->gfid)); inode = inode_find(subvol->itable, up_data->gfid); if (!inode) { ret = -1; gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INODE_FIND_FAILED, "gfid=%s", uuid_utoa(up_data->gfid), "graph_id=%d", subvol->graph->id, NULL); goto out; } LOCK(&inode->lock); { list_for_each_entry(fd, &inode->fd_list, inode_list) { glfd = fd_ctx_get_ptr(fd, subvol); if (glfd) { gf_msg_trace(THIS->name, 0, "glfd (%p) has held lease", glfd); GF_REF_GET(glfd); list_add_tail(&glfd->list, &glfd_list); } } } UNLOCK(&inode->lock); if (!list_empty(&glfd_list)) { list_for_each_entry_safe(glfd, tmp, &glfd_list, list) { LOCK(&glfd->lock); { if (glfd->state != GLFD_CLOSE) { gf_msg_trace(THIS->name, 0, "glfd (%p) has held lease, " "calling recall cbk", glfd); glfd->cbk(lease, glfd->cookie); } } UNLOCK(&glfd->lock); list_del_init(&glfd->list); GF_REF_PUT(glfd); } } out: return ret; } static int glfs_recall_lease_upcall(struct glfs *fs, struct glfs_upcall *up_arg, struct gf_upcall *up_data) { struct gf_upcall_recall_lease *recall_lease = NULL; struct glfs_object *object = NULL; xlator_t *subvol = NULL; int ret = -1; struct glfs_upcall_lease *up_lease_arg = NULL; GF_VALIDATE_OR_GOTO("gfapi", up_data, out); GF_VALIDATE_OR_GOTO("gfapi", fs, out); recall_lease = up_data->data; GF_VALIDATE_OR_GOTO("gfapi", recall_lease, out); subvol = glfs_active_subvol(fs); if (!subvol) { errno = EIO; goto out; } gf_msg_debug(THIS->name, 0, "Recall lease received for gfid:%s", uuid_utoa(up_data->gfid)); object = glfs_h_find_handle(fs, up_data->gfid, GFAPI_HANDLE_LENGTH); if (!object) { /* The reason handle creation will fail is because we * couldn't find the inode in the gfapi inode table. * * But since application would have taken inode_ref, the * only case when this can happen is when it has closed * the handle and hence will no more be interested in * the upcall for this particular gfid. */ gf_smsg(THIS->name, GF_LOG_DEBUG, errno, API_MSG_CREATE_HANDLE_FAILED, "gfid=%s", uuid_utoa(up_data->gfid), NULL); errno = ESTALE; goto out; } up_lease_arg = GF_MALLOC(sizeof(struct glfs_upcall_lease), glfs_mt_upcall_inode_t); if (!up_lease_arg) { errno = ENOMEM; goto out; } up_lease_arg->object = object; up_lease_arg->lease_type = recall_lease->lease_type; up_arg->reason = GLFS_UPCALL_RECALL_LEASE; up_arg->event = up_lease_arg; up_arg->free_event = glfs_free_upcall_lease; ret = 0; out: if (ret) { /* Close p_object and oldp_object as well if being referenced.*/ if (object) glfs_h_close(object); /* Set reason to prevent applications from using ->event */ up_arg->reason = GF_UPCALL_EVENT_NULL; } return ret; } static int upcall_syncop_args_free(struct upcall_syncop_args *args) { dict_t *dict = NULL; struct gf_upcall *upcall_data = NULL; if (args) { upcall_data = &args->upcall_data; switch (upcall_data->event_type) { case GF_UPCALL_CACHE_INVALIDATION: dict = ((struct gf_upcall_cache_invalidation *)(upcall_data ->data)) ->dict; break; case GF_UPCALL_RECALL_LEASE: dict = ((struct gf_upcall_recall_lease *)(upcall_data->data)) ->dict; break; } if (dict) dict_unref(dict); GF_FREE(upcall_data->client_uid); GF_FREE(upcall_data->data); } GF_FREE(args); return 0; } static int glfs_upcall_syncop_cbk(int ret, call_frame_t *frame, void *opaque) { struct upcall_syncop_args *args = opaque; (void)upcall_syncop_args_free(args); return 0; } static int glfs_cbk_upcall_syncop(void *opaque) { struct upcall_syncop_args *args = opaque; struct gf_upcall *upcall_data = NULL; struct glfs_upcall *up_arg = NULL; struct glfs *fs; int ret = -1; fs = args->fs; upcall_data = &args->upcall_data; if (!upcall_data) { goto out; } up_arg = GLFS_CALLOC(1, sizeof(struct gf_upcall), glfs_release_upcall, glfs_mt_upcall_entry_t); if (!up_arg) { gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, "entry", NULL); goto out; } switch (upcall_data->event_type) { case GF_UPCALL_CACHE_INVALIDATION: ret = glfs_h_poll_cache_invalidation(fs, up_arg, upcall_data); break; case GF_UPCALL_RECALL_LEASE: ret = glfs_recall_lease_upcall(fs, up_arg, upcall_data); break; default: errno = EINVAL; } /* It could so happen that the file which got * upcall notification may have got deleted by * the same client. In such cases up_arg->reason * is set to GLFS_UPCALL_EVENT_NULL. No need to * send upcall then */ if (up_arg->reason == GLFS_UPCALL_EVENT_NULL) { gf_smsg(THIS->name, GF_LOG_DEBUG, errno, API_MSG_UPCALL_EVENT_NULL_RECEIVED, NULL); ret = 0; GLFS_FREE(up_arg); goto out; } else if (ret) { gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ENTRY, NULL); GLFS_FREE(up_arg); goto out; } if (fs->up_cbk && up_arg) (fs->up_cbk)(up_arg, fs->up_data); /* application takes care of calling glfs_free on up_arg post * their processing */ out: return ret; } static struct gf_upcall_cache_invalidation * gf_copy_cache_invalidation(struct gf_upcall_cache_invalidation *src) { struct gf_upcall_cache_invalidation *dst = NULL; if (!src) goto out; dst = GF_MALLOC(sizeof(struct gf_upcall_cache_invalidation), glfs_mt_upcall_entry_t); if (!dst) { gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, "entry", NULL); goto out; } dst->flags = src->flags; dst->expire_time_attr = src->expire_time_attr; dst->stat = src->stat; dst->p_stat = src->p_stat; dst->oldp_stat = src->oldp_stat; dst->dict = NULL; if (src->dict) dst->dict = dict_copy_with_ref(src->dict, NULL); return dst; out: return NULL; } static struct gf_upcall_recall_lease * gf_copy_recall_lease(struct gf_upcall_recall_lease *src) { struct gf_upcall_recall_lease *dst = NULL; if (!src) goto out; dst = GF_MALLOC(sizeof(struct gf_upcall_recall_lease), glfs_mt_upcall_entry_t); if (!dst) { gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, "entry", NULL); goto out; } dst->lease_type = src->lease_type; memcpy(dst->tid, src->tid, 16); dst->dict = NULL; if (src->dict) dst->dict = dict_copy_with_ref(src->dict, NULL); return dst; out: return NULL; } static struct upcall_syncop_args * upcall_syncop_args_init(struct glfs *fs, struct gf_upcall *upcall_data) { struct upcall_syncop_args *args = NULL; int ret = -1; struct gf_upcall *t_data = NULL; if (!fs || !upcall_data) goto out; args = GF_CALLOC(1, sizeof(struct upcall_syncop_args), glfs_mt_upcall_entry_t); if (!args) { gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED, "syncop args", NULL); goto out; } /* Note: we are not taking any ref on fs here. * Ideally applications have to unregister for upcall events * or stop polling for upcall events before performing * glfs_fini. And as for outstanding synctasks created, we wait * for all syncenv threads to finish tasks before cleaning up the * fs->ctx. Hence it seems safe to process these callback * notification without taking any lock/ref. */ args->fs = fs; t_data = &(args->upcall_data); t_data->client_uid = gf_strdup(upcall_data->client_uid); gf_uuid_copy(t_data->gfid, upcall_data->gfid); t_data->event_type = upcall_data->event_type; switch (t_data->event_type) { case GF_UPCALL_CACHE_INVALIDATION: t_data->data = gf_copy_cache_invalidation( (struct gf_upcall_cache_invalidation *)upcall_data->data); break; case GF_UPCALL_RECALL_LEASE: t_data->data = gf_copy_recall_lease( (struct gf_upcall_recall_lease *)upcall_data->data); break; } if (!t_data->data) goto out; return args; out: if (ret) { if (args) { GF_FREE(args->upcall_data.client_uid); GF_FREE(args); } } return NULL; } static void glfs_cbk_upcall_data(struct glfs *fs, struct gf_upcall *upcall_data) { struct upcall_syncop_args *args = NULL; int ret = -1; if (!fs || !upcall_data) goto out; if (!(fs->upcall_events & upcall_data->event_type)) { /* ignore events which application hasn't registered*/ goto out; } args = upcall_syncop_args_init(fs, upcall_data); if (!args) goto out; ret = synctask_new(THIS->ctx->env, glfs_cbk_upcall_syncop, glfs_upcall_syncop_cbk, NULL, args); /* should we retry incase of failure? */ if (ret) { gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_UPCALL_SYNCOP_FAILED, "event_type=%d", upcall_data->event_type, "gfid=%s", (char *)(upcall_data->gfid), NULL); upcall_syncop_args_free(args); } out: return; } /* * This routine is called in case of any notification received * from the server. All the upcall events are queued up in a list * to be read by the applications. * * In case if the application registers a cbk function, that shall * be called by this routine in case of any event received. * The cbk fn is responsible for notifying the * applications the way it desires for each event queued (for eg., * can raise a signal or broadcast a cond variable etc.) * * Otherwise all the upcall events are queued up in a list * to be read/polled by the applications. */ GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_process_upcall_event, 3.7.0) void priv_glfs_process_upcall_event(struct glfs *fs, void *data) { glusterfs_ctx_t *ctx = NULL; struct gf_upcall *upcall_data = NULL; DECLARE_OLD_THIS; gf_msg_debug(THIS->name, 0, "Upcall gfapi callback is called"); __GLFS_ENTRY_VALIDATE_FS(fs, err); if (!data) goto out; /* Unlike in I/O path, "glfs_fini" would not have freed * 'fs' by the time we take lock as it waits for all epoll * threads to exit including this */ pthread_mutex_lock(&fs->mutex); { ctx = fs->ctx; /* if we're not interested in upcalls (anymore), skip them */ if (ctx->cleanup_started || !fs->cache_upcalls) { pthread_mutex_unlock(&fs->mutex); goto out; } fs->pin_refcnt++; } pthread_mutex_unlock(&fs->mutex); upcall_data = (struct gf_upcall *)data; gf_msg_trace(THIS->name, 0, "Upcall gfapi gfid = %s", (char *)(upcall_data->gfid)); /* * * TODO: RECALL LEASE for each glfd * * In case of RECALL_LEASE, we could associate separate * cbk function for each glfd either by * - extending pub_glfs_lease to accept new args (recall_cbk_fn, cookie) * - or by defining new API "glfs_register_recall_cbk_fn (glfd, * recall_cbk_fn, cookie) . In such cases, flag it and instead of calling * below upcall functions, define a new one to go through the glfd list and * invoke each of theirs recall_cbk_fn. * */ if (fs->up_cbk) { /* upcall cbk registered */ (void)glfs_cbk_upcall_data(fs, upcall_data); } else { (void)glfs_enqueue_upcall_data(fs, upcall_data); } pthread_mutex_lock(&fs->mutex); { fs->pin_refcnt--; } pthread_mutex_unlock(&fs->mutex); out: __GLFS_EXIT_FS; err: return; } ssize_t glfs_anonymous_pwritev(struct glfs *fs, struct glfs_object *object, const struct iovec *iovec, int iovcnt, off_t offset, int flags) { xlator_t *subvol = NULL; struct iobref *iobref = NULL; struct iobuf *iobuf = NULL; struct iovec iov = { 0, }; inode_t *inode = NULL; fd_t *fd = NULL; int ret = -1; size_t size = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { ret = -1; errno = ESTALE; goto out; } fd = fd_anonymous(inode); if (!fd) { ret = -1; gf_smsg("gfapi", GF_LOG_ERROR, ENOMEM, API_MSG_FDCREATE_FAILED, NULL); errno = ENOMEM; goto out; } size = iov_length(iovec, iovcnt); iobuf = iobuf_get2(subvol->ctx->iobuf_pool, size); if (!iobuf) { ret = -1; errno = ENOMEM; goto out; } iobref = iobref_new(); if (!iobref) { iobuf_unref(iobuf); errno = ENOMEM; ret = -1; goto out; } ret = iobref_add(iobref, iobuf); if (ret) { iobuf_unref(iobuf); iobref_unref(iobref); errno = ENOMEM; ret = -1; goto out; } iov_unload(iobuf_ptr(iobuf), iovec, iovcnt); iov.iov_base = iobuf_ptr(iobuf); iov.iov_len = size; /* TODO : set leaseid */ ret = syncop_writev(subvol, fd, &iov, 1, offset, iobref, flags, NULL, NULL, NULL, NULL); DECODE_SYNCOP_ERR(ret); iobuf_unref(iobuf); iobref_unref(iobref); if (ret <= 0) goto out; out: if (fd) fd_unref(fd); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } ssize_t glfs_anonymous_preadv(struct glfs *fs, struct glfs_object *object, const struct iovec *iovec, int iovcnt, off_t offset, int flags) { xlator_t *subvol = NULL; struct iovec *iov = NULL; struct iobref *iobref = NULL; inode_t *inode = NULL; fd_t *fd = NULL; int cnt = 0; ssize_t ret = -1; ssize_t size = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { ret = -1; errno = ESTALE; goto out; } fd = fd_anonymous(inode); if (!fd) { ret = -1; gf_smsg("gfapi", GF_LOG_ERROR, ENOMEM, API_MSG_FDCREATE_FAILED, NULL); errno = ENOMEM; goto out; } size = iov_length(iovec, iovcnt); /* TODO : set leaseid */ ret = syncop_readv(subvol, fd, size, offset, flags, &iov, &cnt, &iobref, NULL, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret <= 0) goto out; size = iov_copy(iovec, iovcnt, iov, cnt); ret = size; out: if (iov) GF_FREE(iov); if (iobref) iobref_unref(iobref); if (fd) fd_unref(fd); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } static void glfs_release_xreaddirp_stat(void *ptr) { struct glfs_xreaddirp_stat *to_free = ptr; if (to_free->object) glfs_h_close(to_free->object); } /* * Given glfd of a directory, this function does readdirp and returns * xstat along with dirents. */ GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_xreaddirplus_r, 3.11.0) int pub_glfs_xreaddirplus_r(struct glfs_fd *glfd, uint32_t flags, struct glfs_xreaddirp_stat **xstat_p, struct dirent *ext, struct dirent **res) { int ret = -1; gf_dirent_t *entry = NULL; struct dirent *buf = NULL; struct glfs_xreaddirp_stat *xstat = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); GF_VALIDATE_OR_GOTO(THIS->name, xstat_p, out); GF_VALIDATE_OR_GOTO(THIS->name, res, out); errno = 0; if (ext) buf = ext; else buf = glfs_readdirbuf_get(glfd); if (!buf) goto out; xstat = GLFS_CALLOC(1, sizeof(struct glfs_xreaddirp_stat), glfs_release_xreaddirp_stat, glfs_mt_xreaddirp_stat_t); if (!xstat) goto out; /* this is readdirplus operation */ entry = glfd_entry_next(glfd, 1); /* XXX: Ideally when we reach EOD, errno should have been * set to ENOENT. But that doesn't seem to be the case. * * The only way to confirm if its EOD at this point is that * errno == 0 and entry == NULL */ if (errno) goto out; if (!entry) { /* reached EOD, ret = 0 */ ret = 0; *res = NULL; *xstat_p = NULL; /* free xstat as applications shall not be using it */ GLFS_FREE(xstat); goto out; } *res = buf; gf_dirent_to_dirent(entry, buf); if (flags & GFAPI_XREADDIRP_STAT) { glfs_iatt_to_stat(glfd->fs, &entry->d_stat, &xstat->st); xstat->flags_handled |= GFAPI_XREADDIRP_STAT; } if ((flags & GFAPI_XREADDIRP_HANDLE) && /* skip . and .. */ strcmp(buf->d_name, ".") && strcmp(buf->d_name, "..")) { /* Now create object. * We can use "glfs_h_find_handle" as well as inodes would have * already got linked as part of 'gf_link_inodes_from_dirent' */ xstat->object = glfs_h_create_from_handle( glfd->fs, entry->d_stat.ia_gfid, GFAPI_HANDLE_LENGTH, NULL); if (xstat->object) { /* success */ /* note: xstat->object->inode->ref is taken * This shall be unref'ed when application does * glfs_free(xstat) */ xstat->flags_handled |= GFAPI_XREADDIRP_HANDLE; } } ret = xstat->flags_handled; *xstat_p = xstat; gf_msg_debug(THIS->name, 0, "xreaddirp- requested_flags (%x) , processed_flags (%x)", flags, xstat->flags_handled); out: GF_REF_PUT(glfd); if (ret < 0) { gf_smsg(THIS->name, GF_LOG_WARNING, errno, API_MSG_XREADDIRP_R_FAILED, "reason=%s", strerror(errno), NULL); if (xstat) GLFS_FREE(xstat); } __GLFS_EXIT_FS; return ret; invalid_fs: return -1; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_xreaddirplus_get_stat, 3.11.0) struct stat * pub_glfs_xreaddirplus_get_stat(struct glfs_xreaddirp_stat *xstat) { GF_VALIDATE_OR_GOTO("glfs_xreaddirplus_get_stat", xstat, out); if (!(xstat->flags_handled & GFAPI_XREADDIRP_STAT)) gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_FLAGS_HANDLE, "GFAPI_XREADDIRP_STAT" "xstat=%p", xstat, "handles=%x", xstat->flags_handled, NULL); return &xstat->st; out: return NULL; } void gf_lease_to_glfs_lease(struct gf_lease *gf_lease, struct glfs_lease *lease) { u_int lease_type = gf_lease->lease_type; lease->cmd = gf_lease->cmd; lease->lease_type = lease_type; memcpy(lease->lease_id, gf_lease->lease_id, LEASE_ID_SIZE); } void glfs_lease_to_gf_lease(struct glfs_lease *lease, struct gf_lease *gf_lease) { u_int lease_type = lease->lease_type; gf_lease->cmd = lease->cmd; gf_lease->lease_type = lease_type; memcpy(gf_lease->lease_id, lease->lease_id, LEASE_ID_SIZE); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_lease, 4.0.0) int pub_glfs_lease(struct glfs_fd *glfd, struct glfs_lease *lease, glfs_recall_cbk fn, void *data) { int ret = -1; loc_t loc = { 0, }; xlator_t *subvol = NULL; fd_t *fd = NULL; struct gf_lease gf_lease = { 0, }; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(glfd, invalid_fs); GF_REF_GET(glfd); if (!is_valid_lease_id(lease->lease_id)) { ret = -1; errno = EINVAL; goto out; } subvol = glfs_active_subvol(glfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd = glfs_resolve_fd(glfd->fs, subvol, glfd); if (!fd) { ret = -1; errno = EBADFD; goto out; } switch (lease->lease_type) { case GLFS_RD_LEASE: if ((fd->flags != O_RDONLY) && !(fd->flags & O_RDWR)) { ret = -1; errno = EINVAL; goto out; } break; case GLFS_RW_LEASE: if (!((fd->flags & O_WRONLY) || (fd->flags & O_RDWR))) { ret = -1; errno = EINVAL; goto out; } break; default: if (lease->cmd != GLFS_GET_LEASE) { ret = -1; errno = EINVAL; goto out; } break; } /* populate loc */ GLFS_LOC_FILL_INODE(fd->inode, loc, out); glfs_lease_to_gf_lease(lease, &gf_lease); ret = syncop_lease(subvol, &loc, &gf_lease, NULL, NULL); DECODE_SYNCOP_ERR(ret); gf_lease_to_glfs_lease(&gf_lease, lease); /* TODO: Add leases for client replay if (ret == 0 && (cmd == F_SETLK || cmd == F_SETLKW)) fd_lk_insert_and_merge (fd, cmd, &saved_flock); */ if (ret == 0) { ret = fd_ctx_set(glfd->fd, subvol, (uint64_t)(long)glfd); if (ret) { gf_smsg(subvol->name, GF_LOG_ERROR, ENOMEM, API_MSG_FDCTX_SET_FAILED, "fd=%p", glfd->fd, NULL); goto out; } glfd->cbk = fn; glfd->cookie = data; } out: if (glfd) GF_REF_PUT(glfd); if (subvol) glfs_subvol_done(glfd->fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_mkdirat, 11.0) int pub_glfs_mkdirat(struct glfs_fd *pglfd, const char *path, mode_t mode) { int ret = -1; int reval = 0; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(pglfd, invalid_fs); retry: /* Retry case */ if (subvol) { cleanup_fopat_args(pglfd, subvol, ret, &loc); } subvol = setup_fopat_args(pglfd, path, 0, &loc, &iatt, reval); if (!subvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &loc, retry); if (!subvol) { ret = -1; goto out; } ret = setup_entry_fopat_args(gfid, &xattr_req, &loc); if (ret) { goto out; } ret = syncop_mkdir(subvol, &loc, mode, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0) ret = glfs_loc_link(&loc, &iatt); out: if (xattr_req) dict_unref(xattr_req); cleanup_fopat_args(pglfd, subvol, ret, &loc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_faccessat, 11.0) int pub_glfs_faccessat(struct glfs_fd *pglfd, const char *path, int mode, int flags) { int ret = -1; int reval = 0; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int no_follow = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(pglfd, invalid_fs); no_follow = (flags & AT_SYMLINK_NOFOLLOW) == AT_SYMLINK_NOFOLLOW; retry: /* Retry case */ if (subvol) { cleanup_fopat_args(pglfd, subvol, ret, &loc); } subvol = setup_fopat_args(pglfd, path, !no_follow, &loc, &iatt, reval); if (!subvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &loc, retry); if (!subvol) { ret = -1; goto out; } if (!loc.inode) { ret = -1; errno = ENOENT; goto out; } ret = syncop_access(subvol, &loc, mode, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); out: cleanup_fopat_args(pglfd, subvol, ret, &loc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fchmodat, 11.0) int pub_glfs_fchmodat(struct glfs_fd *pglfd, const char *path, mode_t mode, int flags) { int ret = -1; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int glvalid; int no_follow = 0; struct glfs_stat stat = { 0, }; stat.glfs_st_mode = mode; stat.glfs_st_mask = GLFS_STAT_MODE; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(pglfd, invalid_fs); no_follow = (flags & AT_SYMLINK_NOFOLLOW) == AT_SYMLINK_NOFOLLOW; subvol = setup_fopat_args(pglfd, path, !no_follow, &loc, &iatt, 0); if (!subvol) { ret = -1; errno = EIO; goto out; } if (!loc.inode) { ret = -1; errno = ENOENT; goto out; } glfs_iatt_from_statx(&iatt, &stat); glfsflags_from_gfapiflags(&stat, &glvalid); ret = syncop_setattr(subvol, &loc, &iatt, glvalid, 0, 0, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: cleanup_fopat_args(pglfd, subvol, ret, &loc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fchownat, 11.0) int pub_glfs_fchownat(struct glfs_fd *pglfd, const char *path, uid_t uid, gid_t gid, int flags) { int ret = 0; struct glfs_stat stat = { 0, }; if (uid != (uid_t)-1) { stat.glfs_st_uid = uid; stat.glfs_st_mask = GLFS_STAT_UID; } if (gid != (uid_t)-1) { stat.glfs_st_gid = gid; stat.glfs_st_mask = stat.glfs_st_mask | GLFS_STAT_GID; } xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int glvalid; int no_follow = 0; int is_path_empty = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(pglfd, invalid_fs); no_follow = (flags & AT_SYMLINK_NOFOLLOW) == AT_SYMLINK_NOFOLLOW; is_path_empty = (flags & AT_EMPTY_PATH) == AT_EMPTY_PATH; if (is_path_empty && path[0] == '\0') { GF_REF_GET(pglfd); subvol = glfs_active_subvol(pglfd->fs); if (!subvol) { ret = -1; errno = EIO; goto out; } fd_to_loc(pglfd, &loc); } else { subvol = setup_fopat_args(pglfd, path, !no_follow, &loc, &iatt, 0); if (!subvol) { ret = -1; errno = EIO; goto out; } } if (!loc.inode) { ret = -1; errno = ENOENT; goto out; } glfs_iatt_from_statx(&iatt, &stat); glfsflags_from_gfapiflags(&stat, &glvalid); if (stat.glfs_st_mask) { ret = syncop_setattr(subvol, &loc, &iatt, glvalid, 0, 0, NULL, NULL); DECODE_SYNCOP_ERR(ret); } out: cleanup_fopat_args(pglfd, subvol, ret, &loc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_linkat, 11.0) int pub_glfs_linkat(struct glfs_fd *oldpglfd, const char *oldpath, struct glfs_fd *newpglfd, const char *newpath, int flags) { int ret = -1; int reval = 0; xlator_t *oldsubvol = NULL; xlator_t *newsubvol = NULL; loc_t oldloc = { 0, }; loc_t newloc = { 0, }; struct iatt oldiatt = { 0, }; struct iatt newiatt = { 0, }; int follow = 0; int is_path_empty = 0; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(oldpglfd, invalid_fs); __GLFS_ENTRY_VALIDATE_FD(newpglfd, invalid_fs); /* Old path will not be de-referenced by default if it is a sym-link. If 'AT_SYMLINK_FOLLOW' flag is set, then oldpath is deferenced to its original path. If oldpath is a symbolic link and 'AT_SYMLINK_FOLLOW' is set then a new link created will be a symbolic link to defreferenced oldpath. */ follow = (flags & AT_SYMLINK_FOLLOW) == AT_SYMLINK_FOLLOW; is_path_empty = (flags & AT_EMPTY_PATH) == AT_EMPTY_PATH; retry: /* Retry case */ if (oldsubvol) { cleanup_fopat_args(oldpglfd, oldsubvol, ret, &oldloc); } if (is_path_empty && oldpath[0] == '\0') { GF_REF_GET(oldpglfd); oldsubvol = glfs_active_subvol(oldpglfd->fs); if (!oldsubvol) { ret = -1; errno = EIO; goto out; } fd_to_loc(oldpglfd, &oldloc); if (*&oldloc.inode->ia_type == IA_IFDIR) { ret = -1; errno = EISDIR; goto out; } } else { oldsubvol = setup_fopat_args(oldpglfd, oldpath, follow, &oldloc, &oldiatt, reval); } if (!oldsubvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &oldloc, retry); if (!oldsubvol) { goto out; } if (oldsubvol && !oldloc.inode) { ret = -1; errno = ENOENT; goto out; } retrynew: /* Retry case */ if (newsubvol) { cleanup_fopat_args(newpglfd, newsubvol, ret, &newloc); } /* The 'AT_SYMLINK_FOLLOW' flag applies only to oldpath. */ newsubvol = setup_fopat_args(newpglfd, newpath, 0, &newloc, &newiatt, reval); if (!newsubvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &newloc, retrynew); if (newsubvol && newloc.inode) { ret = -1; errno = EEXIST; goto out; } if (oldiatt.ia_type == IA_IFDIR) { ret = -1; errno = EISDIR; goto out; } /* Filling the inode of the hard link to be same as that of the original file */ if (newloc.inode) { inode_unref(newloc.inode); newloc.inode = NULL; } newloc.inode = inode_ref(oldloc.inode); ret = syncop_link(newsubvol, &oldloc, &newloc, &newiatt, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret == 0) ret = glfs_loc_link(&newloc, &newiatt); out: cleanup_fopat_args(oldpglfd, oldsubvol, ret, &oldloc); cleanup_fopat_args(newpglfd, newsubvol, ret, &newloc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_mknodat, 11.0) int pub_glfs_mknodat(struct glfs_fd *pglfd, const char *path, mode_t mode, dev_t dev) { int ret = -1; int reval = 0; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(pglfd, invalid_fs); retry: /* Retry case */ if (subvol) { cleanup_fopat_args(pglfd, subvol, ret, &loc); } subvol = setup_fopat_args(pglfd, path, 0, &loc, &iatt, reval); if (!subvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &loc, retry); if (!subvol) { ret = -1; goto out; } ret = setup_entry_fopat_args(gfid, &xattr_req, &loc); if (ret) { goto out; } ret = syncop_mknod(subvol, &loc, mode, dev, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0) ret = glfs_loc_link(&loc, &iatt); out: if (xattr_req) dict_unref(xattr_req); cleanup_fopat_args(pglfd, subvol, ret, &loc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_readlinkat, 11.0) int pub_glfs_readlinkat(struct glfs_fd *pglfd, const char *path, char *buf, size_t bufsiz) { int ret = -1; int reval = 0; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; char *linkval = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(pglfd, invalid_fs); retry: /* retry case */ if (subvol) { cleanup_fopat_args(pglfd, subvol, ret, &loc); } subvol = setup_fopat_args(pglfd, path, 0, &loc, &iatt, reval); if (!subvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &loc, retry); if (!subvol) goto out; if (!loc.inode) { ret = -1; errno = ENOENT; goto out; } if (iatt.ia_type != IA_IFLNK) { ret = -1; errno = EINVAL; goto out; } ret = syncop_readlink(subvol, &loc, &linkval, bufsiz, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret > 0) { memcpy(buf, linkval, ret); GF_FREE(linkval); } ESTALE_RETRY(ret, errno, reval, &loc, retry); out: cleanup_fopat_args(pglfd, subvol, ret, &loc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_renameat, 11.0) int pub_glfs_renameat(struct glfs_fd *oldpglfd, const char *oldpath, struct glfs_fd *newpglfd, const char *newpath) { int ret = -1; int reval = 0; xlator_t *oldsubvol = NULL; xlator_t *newsubvol = NULL; loc_t oldloc = { 0, }; loc_t newloc = { 0, }; struct iatt oldiatt = { 0, }; struct iatt newiatt = { 0, }; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(oldpglfd, invalid_fs); __GLFS_ENTRY_VALIDATE_FD(newpglfd, invalid_fs); retry: /* Retry case */ if (oldsubvol) { cleanup_fopat_args(oldpglfd, oldsubvol, ret, &oldloc); } oldsubvol = setup_fopat_args(oldpglfd, oldpath, 0, &oldloc, &oldiatt, reval); if (!oldsubvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &oldloc, retry); if (!oldsubvol) { goto out; } /* subvol is not NULL */ if (!oldloc.inode) { ret = -1; errno = ENOENT; goto out; } retrynew: /* Retry case */ if (newsubvol) { cleanup_fopat_args(newpglfd, newsubvol, ret, &newloc); } newsubvol = setup_fopat_args(newpglfd, newpath, 0, &newloc, &newiatt, reval); if (!newsubvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &newloc, retrynew); if (!newsubvol) { goto out; } if (newsubvol && newloc.inode) { ret = -1; errno = EEXIST; goto out; } if (errno != ENOENT && newloc.parent) goto out; if (newiatt.ia_type != IA_INVAL) { if ((oldiatt.ia_type == IA_IFDIR) != (newiatt.ia_type == IA_IFDIR)) { /* Either both old and new must be dirs, * or both must be non-dirs. Else, fail. */ ret = -1; errno = EISDIR; goto out; } } /* TODO: - check if new or old is a prefix of the other, and fail EINVAL * - Add leaseid */ ret = syncop_rename(newsubvol, &oldloc, &newloc, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret == 0) { inode_rename(oldloc.parent->table, oldloc.parent, oldloc.name, newloc.parent, newloc.name, oldloc.inode, &oldiatt); if (newloc.inode && !inode_has_dentry(newloc.inode)) inode_forget(newloc.inode, 0); } out: cleanup_fopat_args(oldpglfd, oldsubvol, ret, &oldloc); cleanup_fopat_args(newpglfd, newsubvol, ret, &newloc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_renameat2, 11.0) int pub_glfs_renameat2(struct glfs_fd *oldpglfd, const char *oldpath, struct glfs_fd *newpglfd, const char *newpath, int flags) { int ret = -1; int reval = 0; xlator_t *oldsubvol = NULL; xlator_t *newsubvol = NULL; loc_t oldloc = { 0, }; loc_t newloc = { 0, }; struct iatt oldiatt = { 0, }; struct iatt newiatt = { 0, }; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(oldpglfd, invalid_fs); __GLFS_ENTRY_VALIDATE_FD(newpglfd, invalid_fs); retry: /* Retry case */ if (oldsubvol) { cleanup_fopat_args(oldpglfd, oldsubvol, ret, &oldloc); } oldsubvol = setup_fopat_args(oldpglfd, oldpath, 0, &oldloc, &oldiatt, reval); if (!oldsubvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &oldloc, retry); if (!oldsubvol) { goto out; } /* subvol is not NULL */ if (!oldloc.inode) { ret = -1; errno = ENOENT; goto out; } retrynew: /* Retry case */ if (newsubvol) { cleanup_fopat_args(newpglfd, newsubvol, ret, &newloc); } newsubvol = setup_fopat_args(newpglfd, newpath, 0, &newloc, &newiatt, reval); if (!newsubvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &newloc, retrynew); if (!newsubvol) { goto out; } if (newloc.inode) { ret = -1; errno = EEXIST; goto out; } if (errno != ENOENT && newloc.parent) goto out; if (newiatt.ia_type != IA_INVAL) { if ((oldiatt.ia_type == IA_IFDIR) != (newiatt.ia_type == IA_IFDIR)) { /* Either both old and new must be dirs, * or both must be non-dirs. Else, fail. */ ret = -1; errno = EISDIR; goto out; } } /* TODO: - check if new or old is a prefix of the other, and fail EINVAL * - Add leaseid */ ret = syncop_rename(newsubvol, &oldloc, &newloc, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret == -1 && errno == ESTALE) { if (reval < DEFAULT_REVAL_COUNT) { reval++; loc_wipe(&oldloc); loc_wipe(&newloc); goto retry; } } if (ret == 0) { inode_rename(oldloc.parent->table, oldloc.parent, oldloc.name, newloc.parent, newloc.name, oldloc.inode, &oldiatt); if (newloc.inode && !inode_has_dentry(newloc.inode)) inode_forget(newloc.inode, 0); } out: cleanup_fopat_args(oldpglfd, oldsubvol, ret, &oldloc); cleanup_fopat_args(newpglfd, newsubvol, ret, &newloc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_symlinkat, 11.0) int pub_glfs_symlinkat(const char *data, struct glfs_fd *pglfd, const char *path) { int ret = -1; int reval = 0; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(pglfd, invalid_fs); retry: /* Retry case */ if (subvol) { cleanup_fopat_args(pglfd, subvol, ret, &loc); } subvol = setup_fopat_args(pglfd, path, 0, &loc, &iatt, reval); if (!subvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &loc, retry); if (!subvol) { ret = -1; goto out; } /* loc == newloc */ if (subvol && loc.inode) { ret = -1; errno = EEXIST; goto out; } ret = setup_entry_fopat_args(gfid, &xattr_req, &loc); if (ret) { goto out; } ret = syncop_symlink(subvol, &loc, data, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0) ret = glfs_loc_link(&loc, &iatt); out: if (xattr_req) dict_unref(xattr_req); cleanup_fopat_args(pglfd, subvol, ret, &loc); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_unlinkat, 11.0) int pub_glfs_unlinkat(struct glfs_fd *pglfd, const char *path, int flags) { int ret = -1; int reval = 0; int is_rmdir = 0; xlator_t *subvol = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FD(pglfd, invalid_fs); is_rmdir = (flags & AT_REMOVEDIR) == AT_REMOVEDIR; retry: /* Retry case */ if (subvol) { cleanup_fopat_args(pglfd, subvol, ret, &loc); } subvol = setup_fopat_args(pglfd, path, 0, &loc, &iatt, reval); if (!subvol) { ret = -1; } ESTALE_RETRY(ret, errno, reval, &loc, retry); if (!subvol) { ret = -1; goto out; } /* If a directory is to be unlinked then 'AT_REMOVEDIR' is to be used mandatorily. */ if (iatt.ia_type == IA_IFDIR && !is_rmdir) { ret = -1; errno = EISDIR; goto out; } else if (iatt.ia_type != IA_IFDIR && is_rmdir) { ret = -1; errno = ENOTDIR; goto out; } /* TODO: Add leaseid */ /* Unlink or rmdir based on 'AT_REMOVEDIR' flag */ if (!is_rmdir) ret = syncop_unlink(subvol, &loc, NULL, NULL); else ret = syncop_rmdir(subvol, &loc, 0, NULL, NULL); DECODE_SYNCOP_ERR(ret); ESTALE_RETRY(ret, errno, reval, &loc, retry); if (ret == 0) ret = glfs_loc_unlink(&loc); out: cleanup_fopat_args(pglfd, subvol, ret, &loc); __GLFS_EXIT_FS; invalid_fs: return ret; } glusterfs-11.2/api/src/glfs-handleops.c000066400000000000000000001705511503123251100200670ustar00rootroot00000000000000/* * Copyright (c) 2013-2018 Red Hat, Inc. * This file is part of GlusterFS. * * This file is licensed to you under your choice of the GNU Lesser * General Public License, version 3 or any later version (LGPLv3 or * later), or the GNU General Public License, version 2 (GPLv2), in all * cases as published by the Free Software Foundation. */ #include "glfs-internal.h" #include "glfs-mem-types.h" #include "glfs.h" #include "gfapi-messages.h" int glfs_listxattr_process(void *value, size_t size, dict_t *xattr); void glfs_iatt_from_stat(struct stat *stat, int valid, struct iatt *iatt, int *glvalid) { /* validate in args */ if ((stat == NULL) || (iatt == NULL) || (glvalid == NULL)) { errno = EINVAL; return; } *glvalid = 0; if (valid & GFAPI_SET_ATTR_MODE) { iatt->ia_prot = ia_prot_from_st_mode(stat->st_mode); *glvalid |= GF_SET_ATTR_MODE; } if (valid & GFAPI_SET_ATTR_UID) { iatt->ia_uid = stat->st_uid; *glvalid |= GF_SET_ATTR_UID; } if (valid & GFAPI_SET_ATTR_GID) { iatt->ia_gid = stat->st_gid; *glvalid |= GF_SET_ATTR_GID; } if (valid & GFAPI_SET_ATTR_ATIME) { iatt->ia_atime = stat->st_atime; iatt->ia_atime_nsec = ST_ATIM_NSEC(stat); *glvalid |= GF_SET_ATTR_ATIME; } if (valid & GFAPI_SET_ATTR_MTIME) { iatt->ia_mtime = stat->st_mtime; iatt->ia_mtime_nsec = ST_MTIM_NSEC(stat); *glvalid |= GF_SET_ATTR_MTIME; } return; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_lookupat, 3.7.4) struct glfs_object * pub_glfs_h_lookupat(struct glfs *fs, struct glfs_object *parent, const char *path, struct stat *stat, int follow) { int ret = 0; xlator_t *subvol = NULL; inode_t *inode = NULL; struct iatt iatt = { 0, }; struct glfs_object *object = NULL; loc_t loc = { 0, }; DECLARE_OLD_THIS; /* validate in args */ if (path == NULL) { errno = EINVAL; return NULL; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ if (parent) { inode = glfs_resolve_inode(fs, subvol, parent); if (!inode) { errno = ESTALE; goto out; } } /* fop/op */ ret = glfs_resolve_at(fs, subvol, inode, path, &loc, &iatt, follow, 0); /* populate out args */ if (!ret) { if (stat) glfs_iatt_to_stat(fs, &iatt, stat); ret = glfs_create_object(&loc, &object); } out: loc_wipe(&loc); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return object; } GFAPI_SYMVER_PUBLIC(glfs_h_lookupat34, glfs_h_lookupat, 3.4.2) struct glfs_object * pub_glfs_h_lookupat34(struct glfs *fs, struct glfs_object *parent, const char *path, struct stat *stat) { return pub_glfs_h_lookupat(fs, parent, path, stat, 0); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_statfs, 3.7.0) int pub_glfs_h_statfs(struct glfs *fs, struct glfs_object *object, struct statvfs *statvfs) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; DECLARE_OLD_THIS; /* validate in args */ if ((fs == NULL) || (object == NULL || statvfs == NULL)) { errno = EINVAL; return -1; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } /* populate loc */ GLFS_LOC_FILL_INODE(inode, loc, out); /* fop/op */ ret = syncop_statfs(subvol, &loc, statvfs, NULL, NULL); DECODE_SYNCOP_ERR(ret); loc_wipe(&loc); out: if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_stat, 3.4.2) int pub_glfs_h_stat(struct glfs *fs, struct glfs_object *object, struct stat *stat) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; DECLARE_OLD_THIS; /* validate in args */ if ((fs == NULL) || (object == NULL)) { errno = EINVAL; return -1; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } /* populate loc */ GLFS_LOC_FILL_INODE(inode, loc, out); /* fop/op */ if (stat) { ret = syncop_stat(subvol, &loc, &iatt, NULL, NULL); if (!ret) /* populate out args */ glfs_iatt_to_stat(fs, &iatt, stat); } else ret = syncop_stat(subvol, &loc, NULL, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: loc_wipe(&loc); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_getattrs, 3.4.2) int pub_glfs_h_getattrs(struct glfs *fs, struct glfs_object *object, struct stat *stat) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; struct iatt iatt = { 0, }; /* validate in args */ if ((fs == NULL) || (object == NULL)) { errno = EINVAL; return -1; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { ret = 0; errno = ESTALE; goto out; } /* fop/op */ if (stat) { ret = glfs_resolve_base(fs, subvol, inode, &iatt); /* populate out args */ if (!ret) { glfs_iatt_to_stat(fs, &iatt, stat); } } else ret = glfs_resolve_base(fs, subvol, inode, NULL); out: if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } int glfs_h_getxattrs_common(struct glfs *fs, struct glfs_object *object, dict_t **xattr, const char *name, gf_boolean_t is_listxattr) { int ret = 0; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; /* validate in args */ if ((fs == NULL) || (object == NULL)) { errno = EINVAL; return -1; } if (!is_listxattr) { if (!name || *name == '\0') { errno = EINVAL; return -1; } if (strlen(name) > GF_XATTR_NAME_MAX) { errno = ENAMETOOLONG; return -1; } } /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } /* populate loc */ GLFS_LOC_FILL_INODE(inode, loc, out); ret = syncop_getxattr(subvol, &loc, xattr, name, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: loc_wipe(&loc); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_getxattrs, 3.5.1) int pub_glfs_h_getxattrs(struct glfs *fs, struct glfs_object *object, const char *name, void *value, size_t size) { int ret = -1; dict_t *xattr = NULL; /* validate in args */ if ((fs == NULL) || (object == NULL)) { errno = EINVAL; return -1; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); ret = glfs_h_getxattrs_common(fs, object, &xattr, name, (name == NULL)); if (ret) goto out; /* If @name is NULL, means get all the xattrs (i.e listxattr). */ if (name) ret = glfs_getxattr_process(value, size, xattr, name); else ret = glfs_listxattr_process(value, size, xattr); out: if (xattr) dict_unref(xattr); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_setattrs, 3.4.2) int pub_glfs_h_setattrs(struct glfs *fs, struct glfs_object *object, struct stat *stat, int valid) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; int glvalid = 0; /* validate in args */ if ((fs == NULL) || (object == NULL) || (stat == NULL)) { errno = EINVAL; return -1; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } /* map valid masks from in args */ glfs_iatt_from_stat(stat, valid, &iatt, &glvalid); /* populate loc */ GLFS_LOC_FILL_INODE(inode, loc, out); /* fop/op */ ret = syncop_setattr(subvol, &loc, &iatt, glvalid, 0, 0, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: loc_wipe(&loc); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_setxattrs, 3.5.0) int pub_glfs_h_setxattrs(struct glfs *fs, struct glfs_object *object, const char *name, const void *value, size_t size, int flags) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; dict_t *xattr = NULL; void *value_cp = NULL; /* validate in args */ if ((fs == NULL) || (object == NULL) || (name == NULL) || (value == NULL)) { errno = EINVAL; return -1; } if (!name || *name == '\0') { errno = EINVAL; return -1; } if (strlen(name) > GF_XATTR_NAME_MAX) { errno = ENAMETOOLONG; return -1; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } value_cp = gf_memdup(value, size); GF_CHECK_ALLOC_AND_LOG(subvol->name, value_cp, ret, "Failed to" " duplicate setxattr value", out); xattr = dict_for_key_value(name, value_cp, size, _gf_false); if (!xattr) { GF_FREE(value_cp); ret = -1; errno = ENOMEM; goto out; } /* populate loc */ GLFS_LOC_FILL_INODE(inode, loc, out); /* fop/op */ ret = syncop_setxattr(subvol, &loc, xattr, flags, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: loc_wipe(&loc); if (inode) inode_unref(inode); if (xattr) dict_unref(xattr); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_removexattrs, 3.5.1) int pub_glfs_h_removexattrs(struct glfs *fs, struct glfs_object *object, const char *name) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; /* validate in args */ if ((fs == NULL) || (object == NULL) || (name == NULL)) { errno = EINVAL; return -1; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } /* populate loc */ GLFS_LOC_FILL_INODE(inode, loc, out); /* fop/op */ ret = syncop_removexattr(subvol, &loc, name, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: loc_wipe(&loc); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_open, 3.4.2) struct glfs_fd * pub_glfs_h_open(struct glfs *fs, struct glfs_object *object, int flags) { int ret = -1; struct glfs_fd *glfd = NULL; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; dict_t *fop_attr = NULL; /* validate in args */ if ((fs == NULL) || (object == NULL)) { errno = EINVAL; return NULL; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } ret = validate_open_flags(flags, inode->ia_type); if (ret) goto out; glfd = glfs_fd_new(fs); if (!glfd) { ret = -1; errno = ENOMEM; goto out; } glfd->fd = fd_create(inode, getpid()); if (!glfd->fd) { ret = -1; errno = ENOMEM; goto out; } glfd->fd->flags = flags; /* populate loc */ GLFS_LOC_FILL_INODE(inode, loc, out); /* fop/op */ ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); if (IA_ISDIR(inode->ia_type)) ret = syncop_opendir(subvol, &loc, glfd->fd, NULL, NULL); else ret = syncop_open(subvol, &loc, flags, glfd->fd, fop_attr, NULL); DECODE_SYNCOP_ERR(ret); glfd->fd->flags = flags; out: loc_wipe(&loc); if (inode) inode_unref(inode); if (fop_attr) dict_unref(fop_attr); if (ret && glfd) { GF_REF_PUT(glfd); glfd = NULL; } else if (glfd) { glfd_set_state_bind(glfd); } glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return glfd; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_creat, 3.4.2) struct glfs_object * pub_glfs_h_creat(struct glfs *fs, struct glfs_object *parent, const char *path, int flags, mode_t mode, struct stat *stat) { int ret = -1; fd_t *fd = NULL; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; struct glfs_object *object = NULL; /* validate in args */ if ((fs == NULL) || (parent == NULL) || (path == NULL)) { errno = EINVAL; return NULL; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, parent); if (!inode) { ret = -1; errno = ESTALE; goto out; } xattr_req = dict_new(); if (!xattr_req) { ret = -1; errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { ret = -1; errno = ENOMEM; goto out; } GLFS_LOC_FILL_PINODE(inode, loc, ret, errno, out, path); fd = fd_create(loc.inode, getpid()); if (!fd) { ret = -1; errno = ENOMEM; goto out; } fd->flags = flags; /* fop/op */ ret = syncop_create(subvol, &loc, flags, mode, fd, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); /* populate out args */ if (ret == 0) { ret = glfs_loc_link(&loc, &iatt); if (ret != 0) { goto out; } if (stat) glfs_iatt_to_stat(fs, &iatt, stat); ret = glfs_create_object(&loc, &object); } out: if (ret && object != NULL) { /* Release the held reference */ glfs_h_close(object); object = NULL; } loc_wipe(&loc); if (inode) inode_unref(inode); if (xattr_req) dict_unref(xattr_req); if (fd) fd_unref(fd); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_creat_open, 6.6) struct glfs_object * pub_glfs_h_creat_open(struct glfs *fs, struct glfs_object *parent, const char *path, int flags, mode_t mode, struct stat *stat, struct glfs_fd **out_fd) { int ret = -1; struct glfs_fd *glfd = NULL; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; struct glfs_object *object = NULL; dict_t *fop_attr = NULL; /* validate in args */ if ((fs == NULL) || (parent == NULL) || (path == NULL) || (out_fd == NULL)) { errno = EINVAL; return NULL; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, parent); if (!inode) { ret = -1; goto out; } xattr_req = dict_new(); if (!xattr_req) { ret = -1; errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { ret = -1; errno = ENOMEM; goto out; } GLFS_LOC_FILL_PINODE(inode, loc, ret, errno, out, path); glfd = glfs_fd_new(fs); if (!glfd) { ret = -1; errno = ENOMEM; goto out; } glfd->fd = fd_create(loc.inode, getpid()); if (!glfd->fd) { ret = -1; errno = ENOMEM; goto out; } glfd->fd->flags = flags; ret = get_fop_attr_thrd_key(&fop_attr); if (ret) gf_msg_debug("gfapi", 0, "Getting leaseid from thread failed"); /* fop/op */ ret = syncop_create(subvol, &loc, flags, mode, glfd->fd, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); /* populate out args */ if (ret == 0) { glfd->fd->flags = flags; ret = glfs_loc_link(&loc, &iatt); if (ret != 0) { goto out; } if (stat) glfs_iatt_to_stat(fs, &iatt, stat); ret = glfs_create_object(&loc, &object); } out: if (ret && object != NULL) { /* Release the held reference */ glfs_h_close(object); object = NULL; } loc_wipe(&loc); if (inode) inode_unref(inode); if (fop_attr) dict_unref(fop_attr); if (xattr_req) dict_unref(xattr_req); if (ret && glfd) { GF_REF_PUT(glfd); } else if (glfd) { glfd_set_state_bind(glfd); *out_fd = glfd; } glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_mkdir, 3.4.2) struct glfs_object * pub_glfs_h_mkdir(struct glfs *fs, struct glfs_object *parent, const char *path, mode_t mode, struct stat *stat) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; struct glfs_object *object = NULL; /* validate in args */ if ((fs == NULL) || (parent == NULL) || (path == NULL)) { errno = EINVAL; return NULL; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, parent); if (!inode) { errno = ESTALE; goto out; } xattr_req = dict_new(); if (!xattr_req) { ret = -1; errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { ret = -1; errno = ENOMEM; goto out; } GLFS_LOC_FILL_PINODE(inode, loc, ret, errno, out, path); /* fop/op */ ret = syncop_mkdir(subvol, &loc, mode, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); /* populate out args */ if (ret == 0) { ret = glfs_loc_link(&loc, &iatt); if (ret != 0) { goto out; } if (stat) glfs_iatt_to_stat(fs, &iatt, stat); ret = glfs_create_object(&loc, &object); } out: if (ret && object != NULL) { glfs_h_close(object); object = NULL; } loc_wipe(&loc); if (inode) inode_unref(inode); if (xattr_req) dict_unref(xattr_req); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_mknod, 3.4.2) struct glfs_object * pub_glfs_h_mknod(struct glfs *fs, struct glfs_object *parent, const char *path, mode_t mode, dev_t dev, struct stat *stat) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; struct glfs_object *object = NULL; /* validate in args */ if ((fs == NULL) || (parent == NULL) || (path == NULL)) { errno = EINVAL; return NULL; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, parent); if (!inode) { errno = ESTALE; goto out; } xattr_req = dict_new(); if (!xattr_req) { ret = -1; errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { ret = -1; errno = ENOMEM; goto out; } GLFS_LOC_FILL_PINODE(inode, loc, ret, errno, out, path); /* fop/op */ ret = syncop_mknod(subvol, &loc, mode, dev, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); /* populate out args */ if (ret == 0) { ret = glfs_loc_link(&loc, &iatt); if (ret != 0) { goto out; } if (stat) glfs_iatt_to_stat(fs, &iatt, stat); ret = glfs_create_object(&loc, &object); } out: if (ret && object != NULL) { glfs_h_close(object); object = NULL; } loc_wipe(&loc); if (inode) inode_unref(inode); if (xattr_req) dict_unref(xattr_req); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_unlink, 3.4.2) int pub_glfs_h_unlink(struct glfs *fs, struct glfs_object *parent, const char *path) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; /* validate in args */ if ((fs == NULL) || (parent == NULL) || (path == NULL)) { errno = EINVAL; return -1; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, parent); if (!inode) { errno = ESTALE; goto out; } ret = glfs_resolve_at(fs, subvol, inode, path, &loc, NULL, 0, 0); if (ret != 0) { goto out; } if (!IA_ISDIR(loc.inode->ia_type)) { ret = syncop_unlink(subvol, &loc, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret != 0) { goto out; } } else { ret = syncop_rmdir(subvol, &loc, 0, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret != 0) { goto out; } } if (ret == 0) ret = glfs_loc_unlink(&loc); out: loc_wipe(&loc); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_opendir, 3.4.2) struct glfs_fd * pub_glfs_h_opendir(struct glfs *fs, struct glfs_object *object) { int ret = -1; struct glfs_fd *glfd = NULL; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; /* validate in args */ if ((fs == NULL) || (object == NULL)) { errno = EINVAL; return NULL; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } if (!IA_ISDIR(inode->ia_type)) { ret = -1; errno = ENOTDIR; goto out; } glfd = glfs_fd_new(fs); if (!glfd) goto out; INIT_LIST_HEAD(&glfd->entries); glfd->fd = fd_create(inode, getpid()); if (!glfd->fd) { ret = -1; errno = ENOMEM; goto out; } GLFS_LOC_FILL_INODE(inode, loc, out); /* fop/op */ ret = syncop_opendir(subvol, &loc, glfd->fd, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: loc_wipe(&loc); if (inode) inode_unref(inode); if (ret && glfd) { GF_REF_PUT(glfd); glfd = NULL; } else if (glfd) { glfd_set_state_bind(glfd); } glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return glfd; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_access, 3.6.0) int pub_glfs_h_access(struct glfs *fs, struct glfs_object *object, int mask) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; DECLARE_OLD_THIS; /* validate in args */ if ((fs == NULL) || (object == NULL)) { errno = EINVAL; return ret; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } GLFS_LOC_FILL_INODE(inode, loc, out); /* fop/op */ ret = syncop_access(subvol, &loc, mask, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: loc_wipe(&loc); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_extract_handle, 3.4.2) ssize_t pub_glfs_h_extract_handle(struct glfs_object *object, unsigned char *handle, int len) { ssize_t ret = -1; /* validate in args */ if (object == NULL) { errno = EINVAL; goto out; } if (!handle || !len) { ret = GFAPI_HANDLE_LENGTH; goto out; } if (len < GFAPI_HANDLE_LENGTH) { errno = ERANGE; goto out; } memcpy(handle, object->gfid, GFAPI_HANDLE_LENGTH); ret = GFAPI_HANDLE_LENGTH; out: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_create_from_handle, 3.4.2) struct glfs_object * pub_glfs_h_create_from_handle(struct glfs *fs, unsigned char *handle, int len, struct stat *stat) { loc_t loc = { 0, }; int ret = -1; struct iatt iatt = { 0, }; inode_t *newinode = NULL; xlator_t *subvol = NULL; struct glfs_object *object = NULL; uint64_t ctx_value = LOOKUP_NOT_NEEDED; gf_boolean_t lookup_needed = _gf_false; /* validate in args */ if ((fs == NULL) || (handle == NULL) || (len != GFAPI_HANDLE_LENGTH)) { errno = EINVAL; return NULL; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { errno = EIO; goto out; } memcpy(loc.gfid, handle, GFAPI_HANDLE_LENGTH); /* make sure the gfid received is valid */ GF_VALIDATE_OR_GOTO("glfs_h_create_from_handle", !(gf_uuid_is_null(loc.gfid)), out); newinode = inode_find(subvol->itable, loc.gfid); if (newinode) { if (!stat) /* No need of lookup */ goto found; lookup_needed = inode_needs_lookup(newinode, THIS); if (lookup_needed) { loc.inode = newinode; } else { /* populate loc */ GLFS_LOC_FILL_INODE(newinode, loc, fill_out); /* fop/op */ ret = syncop_stat(subvol, &loc, &iatt, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret) { fill_out: /* Drop the reference hold in inode_find */ inode_unref(newinode); goto out; } glfs_iatt_to_stat(fs, &iatt, stat); goto found; } } else { loc.inode = inode_new(subvol->itable); if (!loc.inode) { errno = ENOMEM; goto out; } } ret = syncop_lookup(subvol, &loc, &iatt, 0, 0, 0); DECODE_SYNCOP_ERR(ret); if (ret) { gf_smsg(subvol->name, GF_LOG_WARNING, errno, API_MSG_INODE_REFRESH_FAILED, "gfid=%s", uuid_utoa(loc.gfid), "error=%s", strerror(errno), NULL); goto out; } newinode = inode_link(loc.inode, 0, 0, &iatt); if (newinode) { if (newinode == loc.inode) { inode_ctx_set(newinode, THIS, &ctx_value); } inode_lookup(newinode); } else { gf_smsg(subvol->name, GF_LOG_WARNING, errno, API_MSG_INODE_LINK_FAILED, "gfid=%s", uuid_utoa(loc.gfid), NULL); goto out; } /* populate stat */ if (stat) glfs_iatt_to_stat(fs, &iatt, stat); found: object = GF_CALLOC(1, sizeof(struct glfs_object), glfs_mt_glfs_object_t); if (object == NULL) { errno = ENOMEM; ret = -1; goto out; } /* populate the return object */ object->inode = newinode; gf_uuid_copy(object->gfid, object->inode->gfid); out: /* TODO: Check where the inode ref is being held? */ loc_wipe(&loc); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_close, 3.4.2) int pub_glfs_h_close(struct glfs_object *object) { /* since glfs_h_* objects hold a reference to inode * it is safe to keep lookup count to '0' */ inode_forget(object->inode, 0); inode_unref(object->inode); GF_FREE(object); return 0; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_truncate, 3.4.2) int pub_glfs_h_truncate(struct glfs *fs, struct glfs_object *object, off_t offset) { loc_t loc = { 0, }; int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; DECLARE_OLD_THIS; /* validate in args */ if (object == NULL) { errno = EINVAL; return -1; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } GLFS_LOC_FILL_INODE(inode, loc, out); /* fop/op */ ret = syncop_truncate(subvol, &loc, (off_t)offset, NULL, NULL); DECODE_SYNCOP_ERR(ret); /* populate out args */ if (ret == 0) ret = glfs_loc_unlink(&loc); out: loc_wipe(&loc); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_symlink, 3.4.2) struct glfs_object * pub_glfs_h_symlink(struct glfs *fs, struct glfs_object *parent, const char *name, const char *data, struct stat *stat) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; struct iatt iatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; struct glfs_object *object = NULL; DECLARE_OLD_THIS; /* validate in args */ if ((parent == NULL) || (name == NULL) || (data == NULL)) { errno = EINVAL; return NULL; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, parent); if (!inode) { errno = ESTALE; goto out; } xattr_req = dict_new(); if (!xattr_req) { ret = -1; errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { ret = -1; errno = ENOMEM; goto out; } GLFS_LOC_FILL_PINODE(inode, loc, ret, errno, out, name); /* fop/op */ ret = syncop_symlink(subvol, &loc, data, &iatt, xattr_req, NULL); DECODE_SYNCOP_ERR(ret); /* populate out args */ if (ret == 0) { ret = glfs_loc_link(&loc, &iatt); if (ret != 0) { goto out; } if (stat) glfs_iatt_to_stat(fs, &iatt, stat); ret = glfs_create_object(&loc, &object); } out: if (ret && object != NULL) { pub_glfs_h_close(object); object = NULL; } loc_wipe(&loc); if (inode) inode_unref(inode); if (xattr_req) dict_unref(xattr_req); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_readlink, 3.4.2) int pub_glfs_h_readlink(struct glfs *fs, struct glfs_object *object, char *buf, size_t bufsiz) { loc_t loc = { 0, }; int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; char *linkval = NULL; DECLARE_OLD_THIS; /* validate in args */ if ((object == NULL) || (buf == NULL)) { errno = EINVAL; return -1; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } GLFS_LOC_FILL_INODE(inode, loc, out); /* fop/op */ ret = syncop_readlink(subvol, &loc, &linkval, bufsiz, NULL, NULL); DECODE_SYNCOP_ERR(ret); /* populate out args */ if (ret > 0) memcpy(buf, linkval, ret); out: loc_wipe(&loc); if (inode) inode_unref(inode); if (linkval) GF_FREE(linkval); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_link, 3.4.2) int pub_glfs_h_link(struct glfs *fs, struct glfs_object *linksrc, struct glfs_object *parent, const char *name) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; inode_t *pinode = NULL; loc_t oldloc = { 0, }; loc_t newloc = { 0, }; struct iatt iatt = { 0, }; DECLARE_OLD_THIS; /* validate in args */ if ((linksrc == NULL) || (parent == NULL) || (name == NULL)) { errno = EINVAL; return -1; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, linksrc); if (!inode) { errno = ESTALE; goto out; } if (inode->ia_type == IA_IFDIR) { ret = -1; errno = EISDIR; goto out; } GLFS_LOC_FILL_INODE(inode, oldloc, out); /* get/refresh the in arg objects inode in correlation to the xlator */ pinode = glfs_resolve_inode(fs, subvol, parent); if (!pinode) { errno = ESTALE; goto out; } /* setup newloc based on parent */ newloc.parent = inode_ref(pinode); newloc.name = name; ret = glfs_loc_touchup(&newloc); if (ret != 0) { errno = EINVAL; goto out; } /* Filling the inode of the hard link to be same as that of the * original file */ newloc.inode = inode_ref(inode); /* fop/op */ ret = syncop_link(subvol, &oldloc, &newloc, &iatt, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret == 0) ret = glfs_loc_link(&newloc, &iatt); out: loc_wipe(&oldloc); loc_wipe(&newloc); if (inode) inode_unref(inode); if (pinode) inode_unref(pinode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_rename, 3.4.2) int pub_glfs_h_rename(struct glfs *fs, struct glfs_object *olddir, const char *oldname, struct glfs_object *newdir, const char *newname) { int ret = -1; xlator_t *subvol = NULL; inode_t *oldpinode = NULL; inode_t *newpinode = NULL; loc_t oldloc = { 0, }; loc_t newloc = { 0, }; struct iatt oldiatt = { 0, }; struct iatt newiatt = { 0, }; DECLARE_OLD_THIS; /* validate in args */ if ((olddir == NULL) || (oldname == NULL) || (newdir == NULL) || (newname == NULL)) { errno = EINVAL; return -1; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ oldpinode = glfs_resolve_inode(fs, subvol, olddir); if (!oldpinode) { errno = ESTALE; goto out; } ret = glfs_resolve_at(fs, subvol, oldpinode, oldname, &oldloc, &oldiatt, 0, 0); if (ret != 0) { goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ newpinode = glfs_resolve_inode(fs, subvol, newdir); if (!newpinode) { errno = ESTALE; goto out; } ret = glfs_resolve_at(fs, subvol, newpinode, newname, &newloc, &newiatt, 0, 0); if (ret && errno != ENOENT && newloc.parent) goto out; if (newiatt.ia_type != IA_INVAL) { if ((oldiatt.ia_type == IA_IFDIR) != (newiatt.ia_type == IA_IFDIR)) { /* Either both old and new must be dirs, * or both must be non-dirs. Else, fail. */ ret = -1; errno = EEXIST; goto out; } } /* TODO: check if new or old is a prefix of the other, and fail EINVAL */ ret = syncop_rename(subvol, &oldloc, &newloc, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret == 0) { inode_rename(oldloc.parent->table, oldloc.parent, oldloc.name, newloc.parent, newloc.name, oldloc.inode, &oldiatt); if (newloc.inode && !inode_has_dentry(newloc.inode)) inode_forget(newloc.inode, 0); } out: loc_wipe(&oldloc); loc_wipe(&newloc); if (oldpinode) inode_unref(oldpinode); if (newpinode) inode_unref(newpinode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } /* * Given a handle/gfid, find if the corresponding inode is present in * the inode table. If yes create and return the corresponding glfs_object. */ struct glfs_object * glfs_h_find_handle(struct glfs *fs, unsigned char *handle, int len) { inode_t *newinode = NULL; xlator_t *subvol = NULL; struct glfs_object *object = NULL; uuid_t gfid; /* validate in args */ if ((fs == NULL) || (handle == NULL) || (len != GFAPI_HANDLE_LENGTH)) { errno = EINVAL; return NULL; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { errno = EIO; goto out; } memcpy(gfid, handle, GFAPI_HANDLE_LENGTH); /* make sure the gfid received is valid */ GF_VALIDATE_OR_GOTO("glfs_h_find_handle", !(gf_uuid_is_null(gfid)), out); newinode = inode_find(subvol->itable, gfid); if (!newinode) { goto out; } object = GF_CALLOC(1, sizeof(struct glfs_object), glfs_mt_glfs_object_t); if (object == NULL) { errno = ENOMEM; goto out; } /* populate the return object. The ref taken here * is un'refed when the application does glfs_h_close() */ object->inode = inode_ref(newinode); gf_uuid_copy(object->gfid, object->inode->gfid); out: /* inode_find takes a reference. Unref it. */ if (newinode) inode_unref(newinode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return object; } static void glfs_free_upcall_inode(void *to_free) { struct glfs_upcall_inode *arg = to_free; if (!arg) return; if (arg->object) glfs_h_close(arg->object); if (arg->p_object) glfs_h_close(arg->p_object); if (arg->oldp_object) glfs_h_close(arg->oldp_object); GF_FREE(arg); } int glfs_h_poll_cache_invalidation(struct glfs *fs, struct glfs_upcall *up_arg, struct gf_upcall *upcall_data) { int ret = -1; struct glfs_object *p_object = NULL; struct glfs_object *oldp_object = NULL; struct glfs_object *object = NULL; struct gf_upcall_cache_invalidation *ca_data = NULL; struct glfs_upcall_inode *up_inode_arg = NULL; ca_data = upcall_data->data; GF_VALIDATE_OR_GOTO("glfs_h_poll_cache_invalidation", ca_data, out); object = glfs_h_find_handle(fs, upcall_data->gfid, GFAPI_HANDLE_LENGTH); if (!object) { /* The reason handle creation will fail is because we * couldn't find the inode in the gfapi inode table. * * But since application would have taken inode_ref, the * only case when this can happen is when it has closed * the handle and hence will no more be interested in * the upcall for this particular gfid. */ gf_smsg(THIS->name, GF_LOG_DEBUG, errno, API_MSG_CREATE_HANDLE_FAILED, "gfid=%s", uuid_utoa(upcall_data->gfid), NULL); errno = ESTALE; goto out; } up_inode_arg = GF_CALLOC(1, sizeof(struct glfs_upcall_inode), glfs_mt_upcall_inode_t); GF_VALIDATE_OR_GOTO("glfs_h_poll_cache_invalidation", up_inode_arg, out); up_inode_arg->object = object; up_inode_arg->flags = ca_data->flags; up_inode_arg->expire_time_attr = ca_data->expire_time_attr; /* XXX: Update stat as well in case of UP_*_TIMES. * This will be addressed as part of INODE_UPDATE */ if (ca_data->flags & GFAPI_INODE_UPDATE_FLAGS) { glfs_iatt_to_stat(fs, &ca_data->stat, &up_inode_arg->buf); } if (ca_data->flags & GFAPI_UP_PARENT_TIMES) { p_object = glfs_h_find_handle(fs, ca_data->p_stat.ia_gfid, GFAPI_HANDLE_LENGTH); if (!p_object) { gf_smsg(THIS->name, GF_LOG_DEBUG, errno, API_MSG_CREATE_HANDLE_FAILED, "gfid=%s", uuid_utoa(ca_data->p_stat.ia_gfid), NULL); errno = ESTALE; goto out; } glfs_iatt_to_stat(fs, &ca_data->p_stat, &up_inode_arg->p_buf); } up_inode_arg->p_object = p_object; /* In case of RENAME, update old parent as well */ if (ca_data->flags & GFAPI_UP_RENAME) { oldp_object = glfs_h_find_handle(fs, ca_data->oldp_stat.ia_gfid, GFAPI_HANDLE_LENGTH); if (!oldp_object) { gf_smsg(THIS->name, GF_LOG_DEBUG, errno, API_MSG_CREATE_HANDLE_FAILED, "gfid=%s", uuid_utoa(ca_data->oldp_stat.ia_gfid), NULL); errno = ESTALE; /* By the time we receive upcall old parent_dir may * have got removed. We still need to send upcall * for the file/dir and current parent handles. */ up_inode_arg->oldp_object = NULL; ret = 0; } glfs_iatt_to_stat(fs, &ca_data->oldp_stat, &up_inode_arg->oldp_buf); } up_inode_arg->oldp_object = oldp_object; up_arg->reason = GLFS_UPCALL_INODE_INVALIDATE; up_arg->event = up_inode_arg; up_arg->free_event = glfs_free_upcall_inode; ret = 0; out: if (ret) { /* Close p_object and oldp_object as well if being referenced.*/ if (object) glfs_h_close(object); /* Set reason to prevent applications from using ->event */ up_arg->reason = GLFS_UPCALL_EVENT_NULL; GF_FREE(up_inode_arg); } return ret; } void glfs_release_upcall(void *ptr) { struct glfs_upcall *to_free = ptr; if (to_free->event) to_free->free_event(to_free->event); } /* * This API is used to poll for upcall events stored in the upcall list. * Current users of this API is NFS-Ganesha. In case of any event received, it * will be mapped appropriately into 'glfs_upcall' along with the handle object * to be passed to NFS-Ganesha. * * On success, applications need to check if up_arg is not-NULL or errno is not * ENOENT. glfs_upcall_get_reason() can be used to decide what kind of event * has been received. * * Current supported upcall_events: * GLFS_UPCALL_INODE_INVALIDATE * * After processing the event, applications need to free 'up_arg' by calling * glfs_free(). * * Also similar to I/Os, the application should ideally stop polling before * calling glfs_fini(..). Hence making an assumption that 'fs' & ctx structures * cannot be freed while in this routine. */ GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_poll_upcall, 3.7.16) int pub_glfs_h_poll_upcall(struct glfs *fs, struct glfs_upcall **up_arg) { upcall_entry *u_list = NULL; upcall_entry *tmp = NULL; xlator_t *subvol = NULL; glusterfs_ctx_t *ctx = NULL; int ret = -1; struct gf_upcall *upcall_data = NULL; DECLARE_OLD_THIS; if (!up_arg) { errno = EINVAL; goto err; } __GLFS_ENTRY_VALIDATE_FS(fs, err); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { errno = EIO; goto restore; } /* Ideally applications should stop polling before calling * 'glfs_fini'. Yet cross check if cleanup has started. */ pthread_mutex_lock(&fs->mutex); { ctx = fs->ctx; if (ctx->cleanup_started) { pthread_mutex_unlock(&fs->mutex); goto out; } fs->pin_refcnt++; /* once we call this function, the applications seems to be * interested in events, enable caching them */ fs->cache_upcalls = _gf_true; } pthread_mutex_unlock(&fs->mutex); pthread_mutex_lock(&fs->upcall_list_mutex); { list_for_each_entry_safe(u_list, tmp, &fs->upcall_list, upcall_list) { list_del_init(&u_list->upcall_list); upcall_data = &u_list->upcall_data; break; } } /* No other thread can delete this entry. So unlock it */ pthread_mutex_unlock(&fs->upcall_list_mutex); if (upcall_data) { switch (upcall_data->event_type) { case GF_UPCALL_CACHE_INVALIDATION: *up_arg = GLFS_CALLOC(1, sizeof(struct gf_upcall), glfs_release_upcall, glfs_mt_upcall_entry_t); if (!*up_arg) { errno = ENOMEM; break; /* goto free u_list */ } /* XXX: Need to revisit this to support * GLFS_UPCALL_INODE_UPDATE if required. */ ret = glfs_h_poll_cache_invalidation(fs, *up_arg, upcall_data); if (ret || (*up_arg)->reason == GLFS_UPCALL_EVENT_NULL) { /* It could so happen that the file which got * upcall notification may have got deleted by * the same client. Irrespective of the error, * return with an error or success+ENOENT. */ if ((*up_arg)->reason == GLFS_UPCALL_EVENT_NULL) errno = ENOENT; GLFS_FREE(*up_arg); *up_arg = NULL; } break; case GF_UPCALL_RECALL_LEASE: gf_log("glfs_h_poll_upcall", GF_LOG_DEBUG, "UPCALL_RECALL_LEASE is not implemented yet"); /* fallthrough till we support leases */ case GF_UPCALL_EVENT_NULL: /* no 'default:' label, to force handling all upcall events */ errno = ENOENT; break; } GF_FREE(u_list->upcall_data.data); GF_FREE(u_list); } else { /* fs->upcall_list was empty, no upcall events cached */ errno = ENOENT; } ret = 0; out: pthread_mutex_lock(&fs->mutex); { fs->pin_refcnt--; } pthread_mutex_unlock(&fs->mutex); glfs_subvol_done(fs, subvol); restore: __GLFS_EXIT_FS; err: return ret; } static gf_boolean_t log_upcall370 = _gf_true; /* log once */ /* The old glfs_h_poll_upcall interface requires intimate knowledge of the * structures that are returned to the calling application. This is not * recommended, as the returned structures need to returned correctly (handles * closed, memory free'd with the unavailable GF_FREE(), and possibly more.) * * To the best of our knowledge, only NFS-Ganesha uses the upcall events * through gfapi. We keep this backwards compatibility function around so that * applications using the existing implementation do not break. * * WARNING: this function will be removed in the future. */ GFAPI_SYMVER_PUBLIC(glfs_h_poll_upcall370, glfs_h_poll_upcall, 3.7.0) int pub_glfs_h_poll_upcall370(struct glfs *fs, struct glfs_callback_arg *up_arg) { struct glfs_upcall *upcall = NULL; int ret = -1; if (log_upcall370) { log_upcall370 = _gf_false; gf_log(THIS->name, GF_LOG_WARNING, "this application is " "compiled against an old version of libgfapi, it " "should use glfs_free() to release the structure " "returned by glfs_h_poll_upcall() - for more details, " "see http://review.gluster.org/14701"); } ret = pub_glfs_h_poll_upcall(fs, &upcall); if (ret == 0) { up_arg->fs = fs; if ((errno == ENOENT) || !upcall || !upcall->event) { up_arg->reason = GLFS_UPCALL_EVENT_NULL; goto out; } up_arg->reason = upcall->reason; if (upcall->reason == GLFS_UPCALL_INODE_INVALIDATE) { struct glfs_callback_inode_arg *cb_inode = NULL; struct glfs_upcall_inode *up_inode = NULL; cb_inode = GF_CALLOC(1, sizeof(struct glfs_callback_inode_arg), glfs_mt_upcall_inode_t); if (!cb_inode) { errno = ENOMEM; ret = -1; goto out; } up_inode = upcall->event; /* copy attributes one by one, the memory layout might * be different between the old glfs_callback_inode_arg * and new glfs_upcall_inode */ cb_inode->object = up_inode->object; cb_inode->flags = up_inode->flags; memcpy(&cb_inode->buf, &up_inode->buf, sizeof(struct stat)); cb_inode->expire_time_attr = up_inode->expire_time_attr; cb_inode->p_object = up_inode->p_object; memcpy(&cb_inode->p_buf, &up_inode->p_buf, sizeof(struct stat)); cb_inode->oldp_object = up_inode->oldp_object; memcpy(&cb_inode->oldp_buf, &up_inode->oldp_buf, sizeof(struct stat)); up_arg->event_arg = cb_inode; } } out: if (upcall) { /* we can not use glfs_free() here, objects need to stay */ GF_FREE(upcall->event); GF_FREE(upcall); } return ret; } #ifdef HAVE_ACL_LIBACL_H #include #include GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_acl_set, 3.7.0) int pub_glfs_h_acl_set(struct glfs *fs, struct glfs_object *object, const acl_type_t type, const acl_t acl) { int ret = -1; char *acl_s = NULL; const char *acl_key = NULL; struct glfs_object *new_object = NULL; DECLARE_OLD_THIS; if (!object || !acl) { errno = EINVAL; return ret; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); acl_key = gf_posix_acl_get_key(type); if (!acl_key) goto out; acl_s = acl_to_any_text(acl, NULL, ',', TEXT_ABBREVIATE | TEXT_NUMERIC_IDS); if (!acl_s) goto out; if (IA_ISLNK(object->inode->ia_type)) { new_object = glfs_h_resolve_symlink(fs, object); if (new_object == NULL) goto out; } else new_object = object; ret = pub_glfs_h_setxattrs(fs, new_object, acl_key, acl_s, strlen(acl_s) + 1, 0); acl_free(acl_s); out: if (IA_ISLNK(object->inode->ia_type) && new_object) glfs_h_close(new_object); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_acl_get, 3.7.0) acl_t pub_glfs_h_acl_get(struct glfs *fs, struct glfs_object *object, const acl_type_t type) { int ret = 0; acl_t acl = NULL; char *acl_s = NULL; dict_t *xattr = NULL; const char *acl_key = NULL; struct glfs_object *new_object = NULL; DECLARE_OLD_THIS; if (!object) { errno = EINVAL; return NULL; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); acl_key = gf_posix_acl_get_key(type); if (!acl_key) goto out; if (IA_ISLNK(object->inode->ia_type)) { new_object = glfs_h_resolve_symlink(fs, object); if (new_object == NULL) goto out; } else new_object = object; ret = glfs_h_getxattrs_common(fs, new_object, &xattr, acl_key, _gf_false); if (ret) goto out; ret = dict_get_str(xattr, (char *)acl_key, &acl_s); if (ret) goto out; acl = acl_from_text(acl_s); out: if (xattr) dict_unref(xattr); if (IA_ISLNK(object->inode->ia_type) && new_object) glfs_h_close(new_object); __GLFS_EXIT_FS; invalid_fs: return acl; } #else /* !HAVE_ACL_LIBACL_H */ GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_acl_get, 3.7.0) acl_t pub_glfs_h_acl_get(struct glfs *fs, struct glfs_object *object, const acl_type_t type) { errno = ENOTSUP; return NULL; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_acl_set, 3.7.0) int pub_glfs_h_acl_set(struct glfs *fs, struct glfs_object *object, const acl_type_t type, const acl_t acl) { errno = ENOTSUP; return -1; } #endif /* The API to perform read using anonymous fd */ GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_anonymous_read, 3.7.0) ssize_t pub_glfs_h_anonymous_read(struct glfs *fs, struct glfs_object *object, const void *buf, size_t count, off_t offset) { struct iovec iov = { 0, }; ssize_t ret = 0; /* validate in args */ if ((fs == NULL) || (object == NULL)) { errno = EINVAL; return -1; } iov.iov_base = (void *)buf; iov.iov_len = count; ret = glfs_anonymous_preadv(fs, object, &iov, 1, offset, 0); return ret; } /* The API to perform write using anonymous fd */ GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_anonymous_write, 3.7.0) ssize_t pub_glfs_h_anonymous_write(struct glfs *fs, struct glfs_object *object, const void *buf, size_t count, off_t offset) { struct iovec iov = { 0, }; ssize_t ret = 0; /* validate in args */ if ((fs == NULL) || (object == NULL)) { errno = EINVAL; return -1; } iov.iov_base = (void *)buf; iov.iov_len = count; ret = glfs_anonymous_pwritev(fs, object, &iov, 1, offset, 0); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_object_copy, 3.11.0) struct glfs_object * pub_glfs_object_copy(struct glfs_object *src) { struct glfs_object *object = NULL; GF_VALIDATE_OR_GOTO("glfs_dup_object", src, out); object = GF_CALLOC(1, sizeof(struct glfs_object), glfs_mt_glfs_object_t); if (object == NULL) { errno = ENOMEM; gf_smsg(THIS->name, GF_LOG_WARNING, errno, API_MSG_CREATE_HANDLE_FAILED, "glfs_dup_object gfid=%s", uuid_utoa(src->inode->gfid), NULL); return NULL; } object->inode = inode_ref(src->inode); gf_uuid_copy(object->gfid, src->inode->gfid); out: return object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_xreaddirplus_get_object, 3.11.0) struct glfs_object * pub_glfs_xreaddirplus_get_object(struct glfs_xreaddirp_stat *xstat) { GF_VALIDATE_OR_GOTO("glfs_xreaddirplus_get_object", xstat, out); if (!(xstat->flags_handled & GFAPI_XREADDIRP_HANDLE)) gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_HANDLE_NOT_SET, "GFAPI_XREADDIRP_HANDLE xstat=%p", xstat, "handle=%x", xstat->flags_handled, NULL); return xstat->object; out: return NULL; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_h_lease, 4.0.0) int pub_glfs_h_lease(struct glfs *fs, struct glfs_object *object, struct glfs_lease *lease) { int ret = -1; xlator_t *subvol = NULL; inode_t *inode = NULL; loc_t loc = { 0, }; struct gf_lease gf_lease = { 0, }; /* validate in args */ if ((fs == NULL) || (object == NULL)) { errno = EINVAL; return -1; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* get the active volume */ subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } /* get/refresh the in arg objects inode in correlation to the xlator */ inode = glfs_resolve_inode(fs, subvol, object); if (!inode) { errno = ESTALE; goto out; } /* populate loc */ GLFS_LOC_FILL_INODE(inode, loc, out); glfs_lease_to_gf_lease(lease, &gf_lease); ret = syncop_lease(subvol, &loc, &gf_lease, NULL, NULL); DECODE_SYNCOP_ERR(ret); gf_lease_to_glfs_lease(&gf_lease, lease); out: loc_wipe(&loc); if (inode) inode_unref(inode); glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } glusterfs-11.2/api/src/glfs-handles.h000066400000000000000000000307171503123251100175340ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #ifndef _GLFS_HANDLES_H #define _GLFS_HANDLES_H #include "glfs.h" /* GLFS OBJECT BASED OPERATIONS * * The following APIs are introduced to provide an API framework that can work * with gluster objects (files and directories), instead of absolute paths. * * The following API set can be related to the POSIX *at interfaces (like * openat (2)). The intention of these APIs is to be able to operate based * on parent object and looking up or creating child objects within, OR to be * used on the actual object thus looked up or created, and retrieve information * regarding the same. * * The APIs also provide for generating an opaque invariant handle to the * object, that can later be used to lookup the object, instead of the regular * glfs_h_* variants. The APIs that provide this behaviour are, * glfs_h_extract_handle and glfs_h_create_from_handle. * * The object handles can be transitioned to fd based operations as supported * by glfs.h calls, using the glfs_h_open call. This provides a way to move * from objects to fd's akin to moving from path to fd for required operations. * * NOTE: The opaque invariant handle is the GFID of the object in reality, but * maintained as an opaque data value, for potential internal changes to the * same without impacting the caller. * * NOTE: Currently looking up an object can create multiple object handles to * the same, i.e distinct glfs_object *. Hence each such looked up or received * handle from other calls, would need to be closed. In the future, for a given * object these pointers would be the same, and an ease of use API to forget all * instances of this bject would be provided (instead of a per lookup close). * This should not change the APIs in their current form. * */ /* Handle length for object handles returned from glfs_h_extract_handle or * glfs_h_create_from_handle */ #define GFAPI_HANDLE_LENGTH 16 /* These flags should be in sync to the ones defined in upcall.h */ #define GFAPI_UP_NLINK 0x00000001 /* update nlink */ #define GFAPI_UP_MODE 0x00000002 /* update mode and ctime */ #define GFAPI_UP_OWN 0x00000004 /* update mode,uid,gid and ctime */ #define GFAPI_UP_SIZE 0x00000008 /* update fsize */ #define GFAPI_UP_TIMES 0x00000010 /* update all times */ #define GFAPI_UP_ATIME 0x00000020 /* update atime only */ #define GFAPI_UP_PERM \ 0x00000040 /* update fields needed for \ permission checking */ #define GFAPI_UP_RENAME \ 0x00000080 /* this is a rename op - \ delete the cache entry */ #define GFAPI_UP_FORGET \ 0x00000100 /* inode_forget on server side - \ invalidate the cache entry */ #define GFAPI_UP_PARENT_TIMES 0x00000200 /* update parent dir times */ #define GFAPI_INODE_UPDATE_FLAGS \ (GFAPI_UP_NLINK | GFAPI_UP_MODE | GFAPI_UP_OWN | GFAPI_UP_SIZE | \ GFAPI_UP_TIMES | GFAPI_UP_ATIME) /* Portability non glibc c++ build systems */ #ifndef __THROW #if defined __cplusplus #define __THROW throw() #else #define __THROW #endif #endif __BEGIN_DECLS /* * Notes: * * The file object handle. One per looked up, created file/directory * * This had been introduced to facilitate gfid/inode based gfapi * - a requirement introduced by nfs-ganesha */ struct glfs_object; typedef struct glfs_object glfs_object_t; /* Functions for getting details about the glfs_upcall_inode * * None of the pointers returned by the below functions should be free()'d, * glfs_free()'d or glfs_h_close()'d by the application. * * Releasing of the structures is done by passing the glfs_upcall pointer * to glfs_free(). */ struct glfs_upcall_inode; typedef struct glfs_upcall_inode glfs_upcall_inode_t; glfs_object_t * glfs_upcall_inode_get_object(glfs_upcall_inode_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_inode_get_object, 3.7.16); uint64_t glfs_upcall_inode_get_flags(glfs_upcall_inode_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_inode_get_flags, 3.7.16); struct stat * glfs_upcall_inode_get_stat(glfs_upcall_inode_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_inode_get_stat, 3.7.16); uint64_t glfs_upcall_inode_get_expire(glfs_upcall_inode_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_inode_get_expire, 3.7.16); glfs_object_t * glfs_upcall_inode_get_pobject(glfs_upcall_inode_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_inode_get_pobject, 3.7.16); struct stat * glfs_upcall_inode_get_pstat(glfs_upcall_inode_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_inode_get_pstat, 3.7.16); glfs_object_t * glfs_upcall_inode_get_oldpobject(glfs_upcall_inode_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_inode_get_oldpobject, 3.7.16); struct stat * glfs_upcall_inode_get_oldpstat(glfs_upcall_inode_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_inode_get_oldpstat, 3.7.16); /* Handle based operations */ /* Operations that generate handles */ glfs_object_t * glfs_h_lookupat(glfs_t *fs, glfs_object_t *parent, const char *path, struct stat *stat, int follow) __THROW GFAPI_PUBLIC(glfs_h_lookupat, 3.7.4); glfs_object_t * glfs_h_creat(glfs_t *fs, glfs_object_t *parent, const char *path, int flags, mode_t mode, struct stat *sb) __THROW GFAPI_PUBLIC(glfs_h_creat, 3.4.2); glfs_object_t * glfs_h_mkdir(glfs_t *fs, glfs_object_t *parent, const char *path, mode_t flags, struct stat *sb) __THROW GFAPI_PUBLIC(glfs_h_mkdir, 3.4.2); glfs_object_t * glfs_h_mknod(glfs_t *fs, glfs_object_t *parent, const char *path, mode_t mode, dev_t dev, struct stat *sb) __THROW GFAPI_PUBLIC(glfs_h_mknod, 3.4.2); glfs_object_t * glfs_h_symlink(glfs_t *fs, glfs_object_t *parent, const char *name, const char *data, struct stat *stat) __THROW GFAPI_PUBLIC(glfs_h_symlink, 3.4.2); /* Operations on the actual objects */ int glfs_h_unlink(glfs_t *fs, glfs_object_t *parent, const char *path) __THROW GFAPI_PUBLIC(glfs_h_unlink, 3.4.2); int glfs_h_close(glfs_object_t *object) __THROW GFAPI_PUBLIC(glfs_h_close, 3.4.2); int glfs_caller_specific_init(void *uid_caller_key, void *gid_caller_key, void *future) __THROW GFAPI_PUBLIC(glfs_caller_specific_init, 3.5.0); int glfs_h_truncate(glfs_t *fs, glfs_object_t *object, off_t offset) __THROW GFAPI_PUBLIC(glfs_h_truncate, 3.4.2); int glfs_h_stat(glfs_t *fs, glfs_object_t *object, struct stat *stat) __THROW GFAPI_PUBLIC(glfs_h_stat, 3.4.2); int glfs_h_statfs(glfs_t *fs, glfs_object_t *object, struct statvfs *stat) __THROW GFAPI_PUBLIC(glfs_h_statfs, 3.7.0); int glfs_h_getattrs(glfs_t *fs, glfs_object_t *object, struct stat *stat) __THROW GFAPI_PUBLIC(glfs_h_getattrs, 3.4.2); int glfs_h_getxattrs(glfs_t *fs, glfs_object_t *object, const char *name, void *value, size_t size) __THROW GFAPI_PUBLIC(glfs_h_getxattrs, 3.5.1); int glfs_h_setattrs(glfs_t *fs, glfs_object_t *object, struct stat *sb, int valid) __THROW GFAPI_PUBLIC(glfs_h_setattrs, 3.4.2); int glfs_h_setxattrs(glfs_t *fs, glfs_object_t *object, const char *name, const void *value, size_t size, int flags) __THROW GFAPI_PUBLIC(glfs_h_setxattrs, 3.5.0); int glfs_h_readlink(glfs_t *fs, glfs_object_t *object, char *buf, size_t bufsiz) __THROW GFAPI_PUBLIC(glfs_h_readlink, 3.4.2); int glfs_h_link(glfs_t *fs, glfs_object_t *linktgt, glfs_object_t *parent, const char *name) __THROW GFAPI_PUBLIC(glfs_h_link, 3.4.2); int glfs_h_rename(glfs_t *fs, glfs_object_t *olddir, const char *oldname, glfs_object_t *newdir, const char *newname) __THROW GFAPI_PUBLIC(glfs_h_rename, 3.4.2); int glfs_h_removexattrs(glfs_t *fs, glfs_object_t *object, const char *name) __THROW GFAPI_PUBLIC(glfs_h_removexattrs, 3.5.1); /* Operations enabling opaque invariant handle to object transitions */ ssize_t glfs_h_extract_handle(glfs_object_t *object, unsigned char *handle, int len) __THROW GFAPI_PUBLIC(glfs_h_extract_handle, 3.4.2); /* Given a handle, looks up the inode and creates glfs_object. * In addition, if provided 'stat', copies the inode attributes */ glfs_object_t * glfs_h_create_from_handle(glfs_t *fs, unsigned char *handle, int len, struct stat *stat) __THROW GFAPI_PUBLIC(glfs_h_create_from_handle, 3.4.2); /* Operations enabling object handles to fd transitions */ glfs_fd_t * glfs_h_opendir(glfs_t *fs, glfs_object_t *object) __THROW GFAPI_PUBLIC(glfs_h_opendir, 3.4.2); glfs_fd_t * glfs_h_open(glfs_t *fs, glfs_object_t *object, int flags) __THROW GFAPI_PUBLIC(glfs_h_open, 3.4.2); int glfs_h_access(glfs_t *fs, glfs_object_t *object, int mask) __THROW GFAPI_PUBLIC(glfs_h_access, 3.6.0); struct glfs_object * glfs_h_creat_open(struct glfs *fs, struct glfs_object *parent, const char *path, int flags, mode_t mode, struct stat *stat, struct glfs_fd **out_fd) __THROW GFAPI_PUBLIC(glfs_h_creat_open, 6.6); /* SYNOPSIS glfs_h_poll_upcall: Poll for upcall events given a 'glfs' object. DESCRIPTION This API is used to poll for upcall events stored in the upcall list. Current users of this API is NFS-Ganesha. In case of any event received, it will be mapped appropriately into 'glfs_upcall' along with the handle('glfs_object') to be passed to NFS-Ganesha. In case of success, applications need to check the value of cbk->handle to be NON NULL before processing the upcall events. PARAMETERS @fs: glfs object to poll the upcall events for @cbk: Pointer that will contain an upcall event for use by the application. Application is responsible for free'ing the structure with glfs_free(). RETURN VALUES 0 : Success. -1 : Error condition, mostly due to out of memory. */ int glfs_h_poll_upcall(glfs_t *fs, glfs_upcall_t **cbk) __THROW GFAPI_PUBLIC(glfs_h_poll_upcall, 3.7.16); int glfs_h_acl_set(glfs_t *fs, glfs_object_t *object, const acl_type_t type, const acl_t acl) __THROW GFAPI_PUBLIC(glfs_h_acl_set, 3.7.0); acl_t glfs_h_acl_get(glfs_t *fs, glfs_object_t *object, const acl_type_t type) __THROW GFAPI_PUBLIC(glfs_h_acl_get, 3.7.0); size_t glfs_h_anonymous_write(glfs_t *fs, glfs_object_t *object, const void *buf, size_t count, off_t offset) __THROW GFAPI_PUBLIC(glfs_h_anonymous_write, 3.7.0); ssize_t glfs_h_anonymous_read(glfs_t *fs, glfs_object_t *object, const void *buf, size_t count, off_t offset) __THROW GFAPI_PUBLIC(glfs_h_anonymous_read, 3.7.0); /* * Caution: The object returned by this object gets freed as part * of 'glfs_free(xstat)'. Make sure to have a copy using 'glfs_object_copy()' * to use post that. */ glfs_object_t * glfs_xreaddirplus_get_object(struct glfs_xreaddirp_stat *xstat) __THROW GFAPI_PUBLIC(glfs_xreaddirplus_get_object, 3.11.0); /* Applications should close the object returned by this routine * explicitly using 'glfs_h_close()' */ glfs_object_t * glfs_object_copy(glfs_object_t *src) __THROW GFAPI_PUBLIC(glfs_object_copy, 3.11.0); int glfs_h_lease(glfs_t *fs, glfs_object_t *object, glfs_lease_t *lease) __THROW GFAPI_PUBLIC(glfs_h_lease, 4.0.0); glfs_object_t * glfs_h_find_handle(glfs_t *fs, unsigned char *handle, int len) __THROW GFAPI_PUBLIC(glfs_h_lease, 4.0.0); /* Functions for getting details about the glfs_upcall_lease * * None of the pointers returned by the below functions should be free()'d, * glfs_free()'d or glfs_h_close()'d by the application. * * Releasing of the structures is done by passing the glfs_upcall pointer * to glfs_free(). */ struct glfs_upcall_lease; typedef struct glfs_upcall_lease glfs_upcall_lease_t; glfs_object_t * glfs_upcall_lease_get_object(glfs_upcall_lease_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_lease_get_object, 4.1.6); uint32_t glfs_upcall_lease_get_lease_type(glfs_upcall_lease_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_lease_get_lease_type, 4.1.6); __END_DECLS #endif /* !_GLFS_HANDLES_H */ glusterfs-11.2/api/src/glfs-internal.h000066400000000000000000000675261503123251100177420ustar00rootroot00000000000000/* Copyright (c) 2012-2018 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #ifndef _GLFS_INTERNAL_H #define _GLFS_INTERNAL_H #include #include "glfs-handles.h" #include #include #define GLFS_SYMLINK_MAX_FOLLOW 2048 #define DEFAULT_REVAL_COUNT 1 /* * According to pthread mutex and conditional variable ( cond, * child_down_count, upcall mutex and mutex) initialization of struct glfs * members, below GLFS_INIT_* flags are set in 'pthread_flags' member of struct * glfs. The flags are set from glfs_init() and glfs_new_from_ctx() functions * as part of fs inititialization. * * These flag bits are validated in glfs_fini() to destroy all or partially * initialized mutex and conditional variables of glfs object. * If you introduce new pthread mutex or conditional variable in glfs object, * please make sure you have a flag bit intorduced here for proper cleanup * in glfs_fini(). * */ #define PTHREAD_MUTEX_INIT(mutex, attr, flags, mask, label) \ do { \ int __ret = -1; \ __ret = pthread_mutex_init(mutex, attr); \ if (__ret == 0) \ flags |= mask; \ else \ goto label; \ } while (0) #define PTHREAD_MUTEX_DESTROY(mutex, flags, mask) \ do { \ if (flags & mask) \ (void)pthread_mutex_destroy(mutex); \ } while (0) #define PTHREAD_COND_INIT(cond, attr, flags, mask, label) \ do { \ int __ret = -1; \ __ret = pthread_cond_init(cond, attr); \ if (__ret == 0) \ flags |= mask; \ else \ goto label; \ } while (0) #define PTHREAD_COND_DESTROY(cond, flags, mask) \ do { \ if (flags & mask) \ (void)pthread_cond_destroy(cond); \ } while (0) #define GLFS_INIT_MUTEX 0x00000001 /* pthread_mutex_flag */ #define GLFS_INIT_COND 0x00000002 /* pthread_cond_flag */ #define GLFS_INIT_COND_CHILD 0x00000004 /* pthread_cond_child_down_flag */ #define GLFS_INIT_MUTEX_UPCALL 0x00000008 /* pthread_mutex_upcall_flag */ #ifndef GF_DARWIN_HOST_OS #ifndef GFAPI_PUBLIC #define GFAPI_PUBLIC(sym, ver) /**/ #endif #ifndef GFAPI_PRIVATE #define GFAPI_PRIVATE(sym, ver) /**/ #endif #if __GNUC__ >= 10 #define GFAPI_SYMVER_PUBLIC_DEFAULT(fn, ver) \ __attribute__((__symver__(STR(fn) "@@GFAPI_" STR(ver)))) #define GFAPI_SYMVER_PRIVATE_DEFAULT(fn, ver) \ __attribute__((__symver__(STR(fn) "@@GFAPI_PRIVATE_" STR(ver)))) #define GFAPI_SYMVER_PUBLIC(fn1, fn2, ver) \ __attribute__((__symver__(STR(fn2) "@GFAPI_" STR(ver)))) #define GFAPI_SYMVER_PRIVATE(fn1, fn2, ver) \ __attribute__((__symver__(STR(fn2) "@GFAPI_PRIVATE_" STR(ver)))) #else #define GFAPI_SYMVER_PUBLIC_DEFAULT(fn, ver) \ asm(".symver pub_" STR(fn) ", " STR(fn) "@@GFAPI_" STR(ver)); #define GFAPI_SYMVER_PRIVATE_DEFAULT(fn, ver) \ asm(".symver priv_" STR(fn) ", " STR(fn) "@@GFAPI_PRIVATE_" STR(ver)); #define GFAPI_SYMVER_PUBLIC(fn1, fn2, ver) \ asm(".symver pub_" STR(fn1) ", " STR(fn2) "@GFAPI_" STR(ver)); #define GFAPI_SYMVER_PRIVATE(fn1, fn2, ver) \ asm(".symver priv_" STR(fn1) ", " STR(fn2) "@GFAPI_PRIVATE_" STR(ver)); #endif #define STR(str) #str #else #ifndef GFAPI_PUBLIC #define GFAPI_PUBLIC(sym, ver) __asm("_" __STRING(sym) "$GFAPI_" __STRING(ver)); #endif #ifndef GFAPI_PRIVATE #define GFAPI_PRIVATE(sym, ver) \ __asm("_" __STRING(sym) "$GFAPI_PRIVATE_" __STRING(ver)); #endif #define GFAPI_SYMVER_PUBLIC_DEFAULT(fn, dotver) /**/ #define GFAPI_SYMVER_PRIVATE_DEFAULT(fn, dotver) /**/ #define GFAPI_SYMVER_PUBLIC(fn1, fn2, dotver) /**/ #define GFAPI_SYMVER_PRIVATE(fn1, fn2, dotver) /**/ #endif #define ESTALE_RETRY(ret, errno, reval, loc, label) \ do { \ if (ret == -1 && errno == ESTALE) { \ if (reval < DEFAULT_REVAL_COUNT) { \ reval++; \ loc_wipe(loc); \ goto label; \ } \ } \ } while (0) #define GLFS_LOC_FILL_INODE(oinode, loc, label) \ do { \ loc.inode = inode_ref(oinode); \ gf_uuid_copy(loc.gfid, oinode->gfid); \ ret = glfs_loc_touchup(&loc); \ if (ret != 0) { \ errno = EINVAL; \ goto label; \ } \ } while (0) #define GLFS_LOC_FILL_PINODE(pinode, loc, ret, errno, label, path) \ do { \ loc.inode = inode_new(pinode->table); \ if (!loc.inode) { \ ret = -1; \ errno = ENOMEM; \ goto label; \ } \ loc.parent = inode_ref(pinode); \ loc.name = path; \ ret = glfs_loc_touchup(&loc); \ if (ret != 0) { \ errno = EINVAL; \ goto label; \ } \ } while (0) struct glfs; struct _upcall_entry { struct list_head upcall_list; struct gf_upcall upcall_data; }; typedef struct _upcall_entry upcall_entry; typedef int (*glfs_init_cbk)(struct glfs *fs, int ret); struct glfs { char *volname; uuid_t vol_uuid; glusterfs_ctx_t *ctx; pthread_t poller; glfs_init_cbk init_cbk; pthread_mutex_t mutex; pthread_cond_t cond; pthread_cond_t child_down_cond; /* for broadcasting CHILD_DOWN */ int init; int ret; int err; xlator_t *active_subvol; /* active graph */ xlator_t *mip_subvol; /* graph for which migration is in * progress */ xlator_t *next_subvol; /* Any new graph is put to * next_subvol, the graph in * next_subvol can either be moved * to mip_subvol (if any IO picks it * up for migration), or be * destroyed (if there is a new * graph, and this was never picked * for migration) */ xlator_t *old_subvol; char *oldvolfile; ssize_t oldvollen; inode_t *cwd; uint32_t dev_id; /* Used to fill st_dev in struct stat */ struct list_head openfds; gf_boolean_t migration_in_progress; gf_boolean_t cache_upcalls; /* add upcalls to the upcall_list? */ struct list_head upcall_list; pthread_mutex_t upcall_list_mutex; /* mutex for upcall entry list */ uint32_t pin_refcnt; uint32_t pthread_flags; /* GLFS_INIT_* # defines set this flag */ uint32_t upcall_events; /* Mask of upcall events application * is interested in */ glfs_upcall_cbk up_cbk; /* upcall cbk function to be registered */ void *up_data; /* Opaque data provided by application * during upcall registration */ struct list_head waitq; /* waiting synctasks */ }; /* This enum is used to maintain the state of glfd. In case of async fops * fd might be closed before the actual fop is complete. Therefore we need * to track whether the fd is closed or not, instead actually closing it.*/ enum glfs_fd_state { GLFD_INIT, GLFD_OPEN, GLFD_CLOSE }; struct glfs_fd { struct list_head openfds; struct list_head list; GF_REF_DECL; struct glfs *fs; enum glfs_fd_state state; off_t offset; fd_t *fd; /* Currently guared by @fs->mutex. TODO: per-glfd lock */ struct list_head entries; gf_dirent_t *next; struct dirent *readdirbuf; gf_lkowner_t lk_owner; glfs_leaseid_t lease_id; /* Stores lease_id of client in glfd */ gf_lock_t lock; /* lock taken before updating fd state */ glfs_recall_cbk cbk; void *cookie; }; /* glfs object handle introduced for the alternate gfapi implementation based on glfs handles/gfid/inode */ struct glfs_object { inode_t *inode; uuid_t gfid; }; struct glfs_upcall { struct glfs *fs; /* glfs object */ enum glfs_upcall_reason reason; /* Upcall event type */ void *event; /* changes based in the event type */ void (*free_event)(void *); /* free event after the usage */ }; struct glfs_upcall_inode { struct glfs_object *object; /* Object which need to be acted upon */ int flags; /* Cache UPDATE/INVALIDATE flags */ struct stat buf; /* Latest stat of this entry */ unsigned int expire_time_attr; /* the amount of time for which * the application need to cache * this entry */ struct glfs_object *p_object; /* parent Object to be updated */ struct stat p_buf; /* Latest stat of parent dir handle */ struct glfs_object *oldp_object; /* Old parent Object to be updated */ struct stat oldp_buf; /* Latest stat of old parent dir handle */ }; struct glfs_upcall_lease { struct glfs_object *object; /* Object which need to be acted upon */ uint32_t lease_type; /* Lease type to which client can downgrade to*/ }; struct glfs_upcall_lease_fd { uint32_t lease_type; /* Lease type to which client can downgrade to*/ void *fd_cookie; /* Object which need to be acted upon */ }; struct glfs_xreaddirp_stat { struct stat st; /* Stat for that dirent - corresponds to GFAPI_XREADDIRP_STAT */ struct glfs_object *object; /* handled for GFAPI_XREADDIRP_HANDLE */ uint32_t flags_handled; /* final set of flags successfulyy handled */ }; #define DEFAULT_EVENT_POOL_SIZE 16384 #define GF_MEMPOOL_COUNT_OF_DICT_T 4096 #define GF_MEMPOOL_COUNT_OF_DATA_T (GF_MEMPOOL_COUNT_OF_DICT_T * 4) #define GF_MEMPOOL_COUNT_OF_DATA_PAIR_T (GF_MEMPOOL_COUNT_OF_DICT_T * 4) #define GF_MEMPOOL_COUNT_OF_LRU_BUF_T 256 typedef void(glfs_mem_release_t)(void *ptr); struct glfs_mem_header { uint32_t magic; size_t nmemb; size_t size; glfs_mem_release_t *release; }; #define GLFS_MEM_HEADER_SIZE (sizeof(struct glfs_mem_header)) #define GLFS_MEM_HEADER_MAGIC 0x20170830 static inline void * __glfs_calloc(size_t nmemb, size_t size, glfs_mem_release_t release, uint32_t type, const char *typestr) { struct glfs_mem_header *header = NULL; header = __gf_calloc(nmemb, (size + GLFS_MEM_HEADER_SIZE), type, typestr); if (!header) return NULL; header->magic = GLFS_MEM_HEADER_MAGIC; header->nmemb = nmemb; header->size = size; header->release = release; return header + 1; } static inline void * __glfs_malloc(size_t size, glfs_mem_release_t release, uint32_t type, const char *typestr) { struct glfs_mem_header *header = NULL; header = __gf_malloc((size + GLFS_MEM_HEADER_SIZE), type, typestr); if (!header) return NULL; header->magic = GLFS_MEM_HEADER_MAGIC; header->nmemb = 1; header->size = size; header->release = release; return header + 1; } static inline void * __glfs_realloc(void *ptr, size_t size) { struct glfs_mem_header *old_header = NULL; struct glfs_mem_header *new_header = NULL; struct glfs_mem_header tmp_header; void *new_ptr = NULL; GF_ASSERT(NULL != ptr); old_header = (struct glfs_mem_header *)(ptr - GLFS_MEM_HEADER_SIZE); GF_ASSERT(old_header->magic == GLFS_MEM_HEADER_MAGIC); tmp_header = *old_header; new_ptr = __gf_realloc(old_header, (size + GLFS_MEM_HEADER_SIZE)); if (!new_ptr) return NULL; new_header = (struct glfs_mem_header *)new_ptr; *new_header = tmp_header; new_header->size = size; return new_header + 1; } static inline void __glfs_free(void *free_ptr) { struct glfs_mem_header *header = NULL; void *release_ptr = NULL; int i = 0; if (!free_ptr) return; header = (struct glfs_mem_header *)(free_ptr - GLFS_MEM_HEADER_SIZE); GF_ASSERT(header->magic == GLFS_MEM_HEADER_MAGIC); if (header->release) { release_ptr = free_ptr; for (i = 0; i < header->nmemb; i++) { header->release(release_ptr); release_ptr += header->size; } } __gf_free(header); } #define GLFS_CALLOC(nmemb, size, release, type) \ __glfs_calloc(nmemb, size, release, type, #type) #define GLFS_MALLOC(size, release, type) \ __glfs_malloc(size, release, type, #type) #define GLFS_REALLOC(ptr, size) __glfs_realloc(ptr, size) #define GLFS_FREE(free_ptr) __glfs_free(free_ptr) int glfs_mgmt_init(struct glfs *fs); void glfs_init_done(struct glfs *fs, int ret) GFAPI_PRIVATE(glfs_init_done, 3.4.0); int glfs_process_volfp(struct glfs *fs, FILE *fp); int glfs_resolve(struct glfs *fs, xlator_t *subvol, const char *path, loc_t *loc, struct iatt *iatt, int reval) GFAPI_PRIVATE(glfs_resolve, 3.7.0); int glfs_lresolve(struct glfs *fs, xlator_t *subvol, const char *path, loc_t *loc, struct iatt *iatt, int reval); fd_t * glfs_resolve_fd(struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd); fd_t * __glfs_migrate_fd(struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd); int glfs_first_lookup(xlator_t *subvol); void glfs_process_upcall_event(struct glfs *fs, void *data) GFAPI_PRIVATE(glfs_process_upcall_event, 3.7.0); #define __GLFS_ENTRY_VALIDATE_FS(fs, label) \ do { \ if (!fs) { \ errno = EINVAL; \ goto label; \ } \ old_THIS = THIS; \ THIS = fs->ctx->root; \ } while (0) #define __GLFS_EXIT_FS \ do { \ THIS = old_THIS; \ } while (0) #define __GLFS_ENTRY_VALIDATE_FD(glfd, label) \ do { \ if (!glfd || !glfd->fd || !glfd->fd->inode || \ glfd->state != GLFD_OPEN) { \ errno = EBADF; \ goto label; \ } \ old_THIS = THIS; \ THIS = glfd->fd->inode->table->xl->ctx->root; \ } while (0) #define __GLFS_LOCK_WAIT(fs) \ do { \ struct synctask *task = NULL; \ \ task = synctask_get(); \ \ if (task) { \ list_add_tail(&task->waitq, &fs->waitq); \ pthread_mutex_unlock(&fs->mutex); \ synctask_yield(task, NULL); \ pthread_mutex_lock(&fs->mutex); \ } else { \ /* non-synctask */ \ pthread_cond_wait(&fs->cond, &fs->mutex); \ } \ } while (0) #define __GLFS_SYNCTASK_WAKE(fs) \ do { \ struct synctask *waittask = NULL; \ \ while (!list_empty(&fs->waitq)) { \ waittask = list_entry(fs->waitq.next, struct synctask, waitq); \ list_del_init(&waittask->waitq); \ synctask_wake(waittask); \ } \ } while (0) /* By default all lock attempts from user context must use glfs_lock() and glfs_unlock(). This allows for a safe implementation of graph migration where we can give up the mutex during syncop calls so that bottom up calls (particularly CHILD_UP notify) can do a mutex_lock() on @glfs without deadlocking the filesystem. All the fops should wait for graph migration to finish before starting the fops. Therefore these functions should call glfs_lock with wait_for_migration as true. But waiting for migration to finish in call-back path can result thread dead-locks. The reason for this is we only have finite number of epoll threads. so if we wait on epoll threads there will not be any thread left to handle outstanding rpc replies. */ static inline int glfs_lock(struct glfs *fs, gf_boolean_t wait_for_migration) { pthread_mutex_lock(&fs->mutex); while (!fs->init) __GLFS_LOCK_WAIT(fs); while (wait_for_migration && fs->migration_in_progress) __GLFS_LOCK_WAIT(fs); return 0; } static inline void glfs_unlock(struct glfs *fs) { pthread_mutex_unlock(&fs->mutex); } struct glfs_fd * glfs_fd_new(struct glfs *fs); void glfs_fd_bind(struct glfs_fd *glfd); void glfd_set_state_bind(struct glfs_fd *glfd); xlator_t * glfs_active_subvol(struct glfs *fs) GFAPI_PRIVATE(glfs_active_subvol, 3.4.0); xlator_t * __glfs_active_subvol(struct glfs *fs); void glfs_subvol_done(struct glfs *fs, xlator_t *subvol) GFAPI_PRIVATE(glfs_subvol_done, 3.4.0); inode_t * glfs_refresh_inode(xlator_t *subvol, inode_t *inode); inode_t * glfs_cwd_get(struct glfs *fs); int glfs_cwd_set(struct glfs *fs, inode_t *inode); inode_t * glfs_resolve_inode(struct glfs *fs, xlator_t *subvol, struct glfs_object *object); int glfs_create_object(loc_t *loc, struct glfs_object **retobject); int __glfs_cwd_set(struct glfs *fs, inode_t *inode); int glfs_resolve_base(struct glfs *fs, xlator_t *subvol, inode_t *inode, struct iatt *iatt); int glfs_resolve_at(struct glfs *fs, xlator_t *subvol, inode_t *at, const char *origpath, loc_t *loc, struct iatt *iatt, int follow, int reval) GFAPI_PRIVATE(glfs_resolve_at, 3.4.0); int glfs_loc_touchup(loc_t *loc) GFAPI_PRIVATE(glfs_loc_touchup, 3.4.0); void glfs_iatt_to_stat(struct glfs *fs, struct iatt *iatt, struct stat *stat); void glfs_iatt_from_stat(struct stat *stat, int valid, struct iatt *iatt, int *gvalid); int glfs_loc_link(loc_t *loc, struct iatt *iatt); int glfs_loc_unlink(loc_t *loc); int glfs_getxattr_process(void *value, size_t size, dict_t *xattr, const char *name); /* Sends RPC call to glusterd to fetch required volume info */ int glfs_get_volume_info(struct glfs *fs); /* SYNOPSIS glfs_new_from_ctx: Creates a virtual mount object by taking a glusterfs_ctx_t object. DESCRIPTION glfs_new_from_ctx() is not same as glfs_new(). It takes the glusterfs_ctx_t object instead of creating one by glusterfs_ctx_new(). Again the usage is restricted to NFS MOUNT over UDP i.e. in glfs_resolve_at() which would take fs object as input but never use (purpose is not to change the ABI of glfs_resolve_at()). PARAMETERS @ctx: glusterfs_ctx_t object RETURN VALUES fs : Pointer to the newly created glfs_t object. NULL : Otherwise. */ struct glfs * glfs_new_from_ctx(glusterfs_ctx_t *ctx) GFAPI_PRIVATE(glfs_new_from_ctx, 3.7.0); /* SYNOPSIS glfs_free_from_ctx: Free up the memory occupied by glfs_t object created by glfs_new_from_ctx(). DESCRIPTION The glfs_t object allocated by glfs_new_from_ctx() must be released by the caller using this routine. The usage can be found at glfs_fini() or NFS, MOUNT over UDP i.e. __mnt3udp_get_export_subdir_inode () => glfs_resolve_at(). PARAMETERS @fs: The glfs_t object to be deallocated. RETURN VALUES void */ void glfs_free_from_ctx(struct glfs *fs) GFAPI_PRIVATE(glfs_free_from_ctx, 3.7.0); int glfs_recall_lease_fd(struct glfs *fs, struct gf_upcall *up_data); int glfs_get_upcall_cache_invalidation(struct gf_upcall *to_up_data, struct gf_upcall *from_up_data); int glfs_h_poll_cache_invalidation(struct glfs *fs, struct glfs_upcall *up_arg, struct gf_upcall *upcall_data); ssize_t glfs_anonymous_preadv(struct glfs *fs, struct glfs_object *object, const struct iovec *iovec, int iovcnt, off_t offset, int flags); ssize_t glfs_anonymous_pwritev(struct glfs *fs, struct glfs_object *object, const struct iovec *iovec, int iovcnt, off_t offset, int flags); struct glfs_object * glfs_h_resolve_symlink(struct glfs *fs, struct glfs_object *object); /* Deprecated structures that were passed to client applications, replaced by * accessor functions. Do not use these in new applications, and update older * usage. * * See http://review.gluster.org/14701 for more details. * * WARNING: These structures will be removed in the future. */ struct glfs_callback_arg { struct glfs *fs; enum glfs_upcall_reason reason; void *event_arg; }; struct glfs_callback_inode_arg { struct glfs_object *object; /* Object which need to be acted upon */ int flags; /* Cache UPDATE/INVALIDATE flags */ struct stat buf; /* Latest stat of this entry */ unsigned int expire_time_attr; /* the amount of time for which * the application need to cache * this entry */ struct glfs_object *p_object; /* parent Object to be updated */ struct stat p_buf; /* Latest stat of parent dir handle */ struct glfs_object *oldp_object; /* Old parent Object * to be updated */ struct stat oldp_buf; /* Latest stat of old parent * dir handle */ }; struct dirent * glfs_readdirbuf_get(struct glfs_fd *glfd); gf_dirent_t * glfd_entry_next(struct glfs_fd *glfd, int plus); void gf_dirent_to_dirent(gf_dirent_t *gf_dirent, struct dirent *dirent); void gf_lease_to_glfs_lease(struct gf_lease *gf_lease, struct glfs_lease *lease); void glfs_lease_to_gf_lease(struct glfs_lease *lease, struct gf_lease *gf_lease); void glfs_release_upcall(void *ptr); int get_fop_attr_glfd(dict_t **fop_attr, struct glfs_fd *glfd); int set_fop_attr_glfd(struct glfs_fd *glfd); int get_fop_attr_thrd_key(dict_t **fop_attr); void unset_fop_attr(dict_t **fop_attr); int validate_open_flags(int flags, ia_type_t ia_type); /* SYNOPSIS glfs_statx: Fetch extended file attributes for the given path. DESCRIPTION This function fetches extended file attributes for the given path. PARAMETERS @fs: The 'virtual mount' object referencing a volume, under which file exists. @path: Path of the file within the virtual mount. @mask: Requested extended file attributes mask, (See mask defines above) RETURN VALUES -1 : Failure. @errno will be set with the type of failure. 0 : Filled in statxbuf with appropriate masks for valid items in the structure. ERRNO VALUES EINVAL: fs is invalid EINVAL: mask has unsupported bits set Other errors as returned by stat(2) */ int glfs_statx(struct glfs *fs, const char *path, unsigned int mask, struct glfs_stat *statxbuf) GFAPI_PRIVATE(glfs_statx, 6.0); void glfs_iatt_from_statx(struct iatt *, const struct glfs_stat *) GFAPI_PRIVATE(glfs_iatt_from_statx, 6.0); /* * This API is a per thread setting, similar to glfs_setfs{u/g}id, because of * the call to syncopctx_setfspid. */ int glfs_setfspid(struct glfs *, pid_t) GFAPI_PRIVATE(glfs_setfspid, 6.1); #endif /* !_GLFS_INTERNAL_H */ /* This function is of use when filepath is not present to * resolve and to fill loc. */ void fd_to_loc(struct glfs_fd *glfd, loc_t *loc); glusterfs-11.2/api/src/glfs-mem-types.h000066400000000000000000000016511503123251100200310ustar00rootroot00000000000000/* Copyright (c) 2012-2017 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #ifndef _GLFS_MEM_TYPES_H #define _GLFS_MEM_TYPES_H #include #define GF_MEM_TYPE_START (gf_common_mt_end + 1) enum glfs_mem_types_ { glfs_mt_call_pool_t = GF_MEM_TYPE_START, glfs_mt_xlator_t, glfs_mt_glfs_fd_t, glfs_mt_glfs_io_t, glfs_mt_volfile_t, glfs_mt_xlator_cmdline_option_t, glfs_mt_server_cmdline_t, glfs_mt_glfs_object_t, glfs_mt_readdirbuf_t, glfs_mt_upcall_entry_t, glfs_mt_acl_t, glfs_mt_upcall_inode_t, glfs_mt_realpath_t, glfs_mt_xreaddirp_stat_t, glfs_mt_end }; #endif glusterfs-11.2/api/src/glfs-mgmt.c000066400000000000000000000676621503123251100170660ustar00rootroot00000000000000/* Copyright (c) 2012-2018 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include "rpc-clnt.h" #include "rpc-common-xdr.h" #include "glfs-internal.h" #include "gfapi-messages.h" #include int glfs_volfile_fetch(struct glfs *fs); int32_t glfs_get_volume_info_rpc(call_frame_t *frame, xlator_t *this, struct glfs *fs); int glfs_process_volfp(struct glfs *fs, FILE *fp) { glusterfs_graph_t *graph = NULL; int ret = -1; xlator_t *trav = NULL; glusterfs_ctx_t *ctx = NULL; ctx = fs->ctx; graph = glusterfs_graph_construct(fp); if (!graph) { gf_smsg("glfs", GF_LOG_ERROR, errno, API_MSG_GRAPH_CONSTRUCT_FAILED, NULL); goto out; } for (trav = graph->first; trav; trav = trav->next) { if (strcmp(trav->type, "mount/api") == 0) { gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_API_XLATOR_ERROR, NULL); goto out; } } ret = glusterfs_graph_prepare(graph, ctx, fs->volname); if (ret) { glusterfs_graph_destroy(graph); goto out; } ret = glusterfs_graph_activate(graph, ctx); if (ret) { glusterfs_graph_destroy(graph); goto out; } gf_log_dump_graph(fp, graph); ret = 0; out: if (fp) fclose(fp); if (!ctx->active) { ret = -1; } return ret; } int mgmt_cbk_spec(struct rpc_clnt *rpc, void *mydata, void *data) { struct glfs *fs = NULL; xlator_t *this = NULL; this = mydata; fs = this->private; glfs_volfile_fetch(fs); return 0; } int mgmt_cbk_event(struct rpc_clnt *rpc, void *mydata, void *data) { return 0; } static int mgmt_cbk_statedump(struct rpc_clnt *rpc, void *mydata, void *data) { struct glfs *fs = NULL; xlator_t *this = NULL; gf_statedump target_pid = { 0, }; struct iovec *iov = NULL; int ret = -1; this = mydata; if (!this) { gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_NULL, "mydata", NULL); errno = EINVAL; goto out; } fs = this->private; if (!fs) { gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_NULL, "glfs", NULL); errno = EINVAL; goto out; } iov = (struct iovec *)data; if (!iov) { gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_NULL, "iovec data", NULL); errno = EINVAL; goto out; } ret = xdr_to_generic(*iov, &target_pid, (xdrproc_t)xdr_gf_statedump); if (ret < 0) { gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_DECODE_XDR_FAILED, NULL); goto out; } gf_msg_trace("glfs", 0, "statedump requested for pid: %d", target_pid.pid); if ((uint64_t)getpid() == target_pid.pid) { gf_msg_debug("glfs", 0, "Taking statedump for pid: %d", target_pid.pid); ret = glfs_sysrq(fs, GLFS_SYSRQ_STATEDUMP); if (ret < 0) { gf_smsg("glfs", GF_LOG_INFO, 0, API_MSG_STATEDUMP_FAILED, NULL); } } out: return ret; } static rpcclnt_cb_actor_t mgmt_cbk_actors[GF_CBK_MAXVALUE] = { [GF_CBK_FETCHSPEC] = {"FETCHSPEC", mgmt_cbk_spec, GF_CBK_FETCHSPEC}, [GF_CBK_EVENT_NOTIFY] = {"EVENTNOTIFY", mgmt_cbk_event, GF_CBK_EVENT_NOTIFY}, [GF_CBK_STATEDUMP] = {"STATEDUMP", mgmt_cbk_statedump, GF_CBK_STATEDUMP}, }; static struct rpcclnt_cb_program mgmt_cbk_prog = { .progname = "GlusterFS Callback", .prognum = GLUSTER_CBK_PROGRAM, .progver = GLUSTER_CBK_VERSION, .actors = mgmt_cbk_actors, .numactors = GF_CBK_MAXVALUE, }; static char *clnt_handshake_procs[GF_HNDSK_MAXVALUE] = { [GF_HNDSK_NULL] = "NULL", [GF_HNDSK_SETVOLUME] = "SETVOLUME", [GF_HNDSK_GETSPEC] = "GETSPEC", [GF_HNDSK_PING] = "PING", [GF_HNDSK_EVENT_NOTIFY] = "EVENTNOTIFY", [GF_HNDSK_GET_VOLUME_INFO] = "GETVOLUMEINFO", }; static rpc_clnt_prog_t clnt_handshake_prog = { .progname = "GlusterFS Handshake", .prognum = GLUSTER_HNDSK_PROGRAM, .progver = GLUSTER_HNDSK_VERSION, .procnames = clnt_handshake_procs, }; int mgmt_submit_request(void *req, call_frame_t *frame, glusterfs_ctx_t *ctx, rpc_clnt_prog_t *prog, int procnum, fop_cbk_fn_t cbkfn, xdrproc_t xdrproc) { int ret = -1; int count = 0; struct iovec iov = { 0, }; struct iobuf *iobuf = NULL; struct iobref *iobref = NULL; ssize_t xdr_size = 0; gf_boolean_t frame_cleanup = _gf_true; iobref = iobref_new(); if (!iobref) { goto out; } if (req) { xdr_size = xdr_sizeof(xdrproc, req); iobuf = iobuf_get2(ctx->iobuf_pool, xdr_size); if (!iobuf) { goto out; }; iobref_add(iobref, iobuf); iov.iov_base = iobuf->ptr; iov.iov_len = iobuf_pagesize(iobuf); /* Create the xdr payload */ ret = xdr_serialize_generic(iov, req, xdrproc); if (ret == -1) { gf_smsg(THIS->name, GF_LOG_WARNING, 0, API_MSG_XDR_PAYLOAD_FAILED, NULL); goto out; } iov.iov_len = ret; count = 1; } /* Send the msg */ ret = rpc_clnt_submit(ctx->mgmt, prog, procnum, cbkfn, &iov, count, NULL, 0, iobref, frame, NULL, 0, NULL, 0, NULL); frame_cleanup = _gf_false; out: if (iobref) iobref_unref(iobref); if (iobuf) iobuf_unref(iobuf); if (frame_cleanup) STACK_DESTROY(frame->root); return ret; } /* * Callback routine for 'GF_HNDSK_GET_VOLUME_INFO' rpc request */ int mgmt_get_volinfo_cbk(struct rpc_req *req, struct iovec *iov, int count, void *myframe) { int ret = 0; char *volume_id_str = NULL; dict_t *dict = NULL; gf_get_volume_info_rsp rsp = { 0, }; call_frame_t *frame = NULL; glusterfs_ctx_t *ctx = NULL; struct glfs *fs = NULL; struct syncargs *args; frame = myframe; ctx = frame->this->ctx; args = frame->local; if (!ctx) { gf_smsg(frame->this->name, GF_LOG_ERROR, EINVAL, API_MSG_NULL, "context", NULL); errno = EINVAL; ret = -1; goto out; } fs = ((xlator_t *)ctx->root)->private; if (-1 == req->rpc_status) { gf_smsg(frame->this->name, GF_LOG_ERROR, EINVAL, API_MSG_CALL_NOT_SUCCESSFUL, NULL); errno = EINVAL; ret = -1; goto out; } ret = xdr_to_generic(*iov, &rsp, (xdrproc_t)xdr_gf_get_volume_info_rsp); if (ret < 0) { gf_smsg(frame->this->name, GF_LOG_ERROR, 0, API_MSG_XDR_RESPONSE_DECODE_FAILED, NULL); goto out; } gf_msg_debug(frame->this->name, 0, "Received resp to GET_VOLUME_INFO " "RPC: %d", rsp.op_ret); if (rsp.op_ret == -1) { errno = rsp.op_errno; ret = -1; goto out; } if (!rsp.dict.dict_len) { gf_smsg(frame->this->name, GF_LOG_ERROR, EINVAL, API_MSG_CALL_NOT_VALID, NULL); ret = -1; errno = EINVAL; goto out; } dict = dict_new(); if (!dict) { ret = -1; errno = ENOMEM; goto out; } ret = dict_unserialize(rsp.dict.dict_val, rsp.dict.dict_len, &dict); if (ret) { errno = ENOMEM; goto out; } ret = dict_get_str_sizen(dict, "volume_id", &volume_id_str); if (ret) { errno = EINVAL; goto out; } ret = 0; out: if (volume_id_str) { gf_msg_debug(frame->this->name, 0, "Volume Id: %s", volume_id_str); pthread_mutex_lock(&fs->mutex); gf_uuid_parse(volume_id_str, fs->vol_uuid); pthread_mutex_unlock(&fs->mutex); } if (ret) { gf_smsg(frame->this->name, GF_LOG_ERROR, errno, API_MSG_GET_VOLINFO_CBK_FAILED, "error=%s", strerror(errno), NULL); } if (dict) dict_unref(dict); if (rsp.dict.dict_val) free(rsp.dict.dict_val); if (rsp.op_errstr) free(rsp.op_errstr); gf_msg_debug(frame->this->name, 0, "Returning: %d", ret); __wake(args); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_get_volumeid, 3.5.0) int pub_glfs_get_volumeid(struct glfs *fs, char *volid, size_t size) { /* TODO: Define a global macro to store UUID size */ size_t uuid_size = 16; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); pthread_mutex_lock(&fs->mutex); { /* check if the volume uuid is initialized */ if (!gf_uuid_is_null(fs->vol_uuid)) { pthread_mutex_unlock(&fs->mutex); goto done; } } pthread_mutex_unlock(&fs->mutex); /* Need to fetch volume_uuid */ glfs_get_volume_info(fs); if (gf_uuid_is_null(fs->vol_uuid)) { gf_smsg(THIS->name, GF_LOG_ERROR, EINVAL, API_MSG_FETCH_VOLUUID_FAILED, NULL); goto out; } done: if (!volid || !size) { gf_msg_debug(THIS->name, 0, "volumeid/size is null"); __GLFS_EXIT_FS; return uuid_size; } if (size < uuid_size) { gf_smsg(THIS->name, GF_LOG_ERROR, ERANGE, API_MSG_INSUFF_SIZE, NULL); errno = ERANGE; goto out; } memcpy(volid, fs->vol_uuid, uuid_size); __GLFS_EXIT_FS; return uuid_size; out: __GLFS_EXIT_FS; invalid_fs: return -1; } int glfs_get_volume_info(struct glfs *fs) { call_frame_t *frame = NULL; glusterfs_ctx_t *ctx = NULL; struct syncargs args = { 0, }; int ret = 0; ctx = fs->ctx; frame = create_frame(THIS, ctx->pool); if (!frame) { gf_smsg("glfs", GF_LOG_ERROR, ENOMEM, API_MSG_FRAME_CREAT_FAILED, NULL); ret = -1; goto out; } frame->local = &args; __yawn((&args)); ret = glfs_get_volume_info_rpc(frame, THIS, fs); if (ret) goto out; __yield((&args)); frame->local = NULL; STACK_DESTROY(frame->root); out: return ret; } int32_t glfs_get_volume_info_rpc(call_frame_t *frame, xlator_t *this, struct glfs *fs) { gf_get_volume_info_req req = {{ 0, }}; int ret = 0; glusterfs_ctx_t *ctx = NULL; dict_t *dict = NULL; int32_t flags = 0; if (!frame || !this || !fs) { ret = -1; goto out; } ctx = fs->ctx; dict = dict_new(); if (!dict) { ret = -1; goto out; } if (fs->volname) { ret = dict_set_str(dict, "volname", fs->volname); if (ret) goto out; } // Set the flags for the fields which we are interested in flags = (int32_t)GF_GET_VOLUME_UUID; // ctx->flags; ret = dict_set_int32(dict, "flags", flags); if (ret) { gf_smsg(frame->this->name, GF_LOG_ERROR, EINVAL, API_MSG_DICT_SET_FAILED, "flags", NULL); goto out; } ret = dict_allocate_and_serialize(dict, &req.dict.dict_val, &req.dict.dict_len); ret = mgmt_submit_request(&req, frame, ctx, &clnt_handshake_prog, GF_HNDSK_GET_VOLUME_INFO, mgmt_get_volinfo_cbk, (xdrproc_t)xdr_gf_get_volume_info_req); out: if (dict) { dict_unref(dict); } GF_FREE(req.dict.dict_val); return ret; } static int glusterfs_oldvolfile_update(struct glfs *fs, char *volfile, ssize_t size) { int ret = -1; pthread_mutex_lock(&fs->mutex); fs->oldvollen = size; if (!fs->oldvolfile) { fs->oldvolfile = CALLOC(1, size + 1); } else { fs->oldvolfile = REALLOC(fs->oldvolfile, size + 1); } if (!fs->oldvolfile) { fs->oldvollen = 0; } else { memcpy(fs->oldvolfile, volfile, size); fs->oldvollen = size; ret = 0; } pthread_mutex_unlock(&fs->mutex); return ret; } int glfs_mgmt_getspec_cbk(struct rpc_req *req, struct iovec *iov, int count, void *myframe) { gf_getspec_rsp rsp = { 0, }; call_frame_t *frame = NULL; glusterfs_ctx_t *ctx = NULL; int ret = 0; ssize_t size = 0; FILE *tmpfp = NULL; int need_retry = 0; struct glfs *fs = NULL; dict_t *dict = NULL; char *servers_list = NULL; int tmp_fd = -1; char template[] = "/tmp/gfapi.volfile.XXXXXX"; frame = myframe; ctx = frame->this->ctx; if (!ctx) { gf_smsg(frame->this->name, GF_LOG_ERROR, EINVAL, API_MSG_NULL, "context", NULL); errno = EINVAL; ret = -1; goto out; } fs = ((xlator_t *)ctx->root)->private; if (-1 == req->rpc_status) { ret = -1; need_retry = 1; goto out; } ret = xdr_to_generic(*iov, &rsp, (xdrproc_t)xdr_gf_getspec_rsp); if (ret < 0) { gf_smsg(frame->this->name, GF_LOG_ERROR, 0, API_MSG_XDR_DECODE_FAILED, NULL); ret = -1; goto out; } if (-1 == rsp.op_ret) { gf_smsg(frame->this->name, GF_LOG_ERROR, rsp.op_errno, API_MSG_GET_VOLFILE_FAILED, "from server", NULL); ret = -1; errno = rsp.op_errno; goto out; } if (!rsp.xdata.xdata_len) { goto volfile; } dict = dict_new(); if (!dict) { ret = -1; errno = ENOMEM; goto out; } ret = dict_unserialize(rsp.xdata.xdata_val, rsp.xdata.xdata_len, &dict); if (ret) { gf_log(frame->this->name, GF_LOG_ERROR, "failed to unserialize xdata to dictionary"); goto out; } dict->extra_stdfree = rsp.xdata.xdata_val; /* glusterd2 only */ ret = dict_get_str(dict, "servers-list", &servers_list); if (ret) { goto volfile; } gf_log(frame->this->name, GF_LOG_INFO, "Received list of available volfile servers: %s", servers_list); ret = gf_process_getspec_servers_list(&ctx->cmd_args, servers_list); if (ret) { gf_log(frame->this->name, GF_LOG_ERROR, "Failed (%s) to process servers list: %s", strerror(errno), servers_list); } volfile: ret = 0; size = rsp.op_ret; pthread_mutex_lock(&fs->mutex); if ((size == fs->oldvollen) && (memcmp(fs->oldvolfile, rsp.spec, size) == 0)) { pthread_mutex_unlock(&fs->mutex); gf_smsg(frame->this->name, GF_LOG_INFO, 0, API_MSG_VOLFILE_INFO, NULL); goto out; } pthread_mutex_unlock(&fs->mutex); /* coverity[secure_temp] mkstemp uses 0600 as the mode and is safe */ tmp_fd = mkstemp(template); if (-1 == tmp_fd) { ret = -1; goto out; } /* Calling unlink so that when the file is closed or program * terminates the temporary file is deleted. */ ret = sys_unlink(template); if (ret < 0) { gf_smsg(frame->this->name, GF_LOG_INFO, 0, API_MSG_UNABLE_TO_DEL, "template=%s", template, NULL); ret = 0; } tmpfp = fdopen(tmp_fd, "w+b"); if (!tmpfp) { ret = -1; goto out; } fwrite(rsp.spec, size, 1, tmpfp); fflush(tmpfp); if (ferror(tmpfp)) { ret = -1; goto out; } /* Check if only options have changed. No need to reload the * volfile if topology hasn't changed. * glusterfs_volfile_reconfigure returns 3 possible return states * return 0 =======> reconfiguration of options has succeeded * return 1 =======> the graph has to be reconstructed and all * the xlators should be inited return -1(or -ve) =======> Some Internal * Error occurred during the operation */ pthread_mutex_lock(&fs->mutex); ret = gf_volfile_reconfigure(fs->oldvollen, tmpfp, fs->ctx, fs->oldvolfile); pthread_mutex_unlock(&fs->mutex); if (ret == 0) { gf_msg_debug("glusterfsd-mgmt", 0, "No need to re-load " "volfile, reconfigure done"); ret = glusterfs_oldvolfile_update(fs, rsp.spec, size); goto out; } if (ret < 0) { gf_msg_debug("glusterfsd-mgmt", 0, "Reconfigure failed !!"); goto out; } ret = glfs_process_volfp(fs, tmpfp); /* tmpfp closed */ tmpfp = NULL; tmp_fd = -1; if (ret) goto out; ret = glusterfs_oldvolfile_update(fs, rsp.spec, size); out: STACK_DESTROY(frame->root); if (rsp.spec) free(rsp.spec); if (dict) dict_unref(dict); // Stop if server is running at an unsupported op-version if (ENOTSUP == ret) { gf_smsg("mgmt", GF_LOG_ERROR, ENOTSUP, API_MSG_WRONG_OPVERSION, NULL); errno = ENOTSUP; glfs_init_done(fs, -1); } if (ret && ctx && !ctx->active) { /* Do it only for the first time */ /* Failed to get the volume file, something wrong, restart the process */ gf_smsg("glfs-mgmt", GF_LOG_ERROR, EINVAL, API_MSG_GET_VOLFILE_FAILED, "key=%s", ctx->cmd_args.volfile_id, NULL); if (!need_retry) { if (!errno) errno = EINVAL; glfs_init_done(fs, -1); } } if (tmpfp) fclose(tmpfp); else if (tmp_fd != -1) sys_close(tmp_fd); return 0; } int glfs_volfile_fetch(struct glfs *fs) { cmd_args_t *cmd_args = NULL; gf_getspec_req req = { 0, }; int ret = -1; call_frame_t *frame = NULL; glusterfs_ctx_t *ctx = NULL; dict_t *dict = NULL; ctx = fs->ctx; cmd_args = &ctx->cmd_args; req.key = cmd_args->volfile_id; req.flags = 0; dict = dict_new(); if (!dict) { goto out; } // Set the supported min and max op-versions, so glusterd can make a // decision ret = dict_set_int32(dict, "min-op-version", GD_OP_VERSION_MIN); if (ret) { gf_smsg(THIS->name, GF_LOG_ERROR, EINVAL, API_MSG_DICT_SET_FAILED, "min-op-version", NULL); goto out; } ret = dict_set_int32(dict, "max-op-version", GD_OP_VERSION_MAX); if (ret) { gf_smsg(THIS->name, GF_LOG_ERROR, EINVAL, API_MSG_DICT_SET_FAILED, "max-op-version", NULL); goto out; } /* Ask for a list of volfile (glusterd2 only) servers */ if (GF_CLIENT_PROCESS == ctx->process_mode) { req.flags = req.flags | GF_GETSPEC_FLAG_SERVERS_LIST; } ret = dict_allocate_and_serialize(dict, &req.xdata.xdata_val, &req.xdata.xdata_len); if (ret < 0) { gf_smsg(THIS->name, GF_LOG_ERROR, 0, API_MSG_DICT_SERIALIZE_FAILED, NULL); goto out; } frame = create_frame(THIS, ctx->pool); if (!frame) { ret = -1; goto out; } ret = mgmt_submit_request(&req, frame, ctx, &clnt_handshake_prog, GF_HNDSK_GETSPEC, glfs_mgmt_getspec_cbk, (xdrproc_t)xdr_gf_getspec_req); out: if (req.xdata.xdata_val) GF_FREE(req.xdata.xdata_val); if (dict) dict_unref(dict); return ret; } static int mgmt_rpc_notify(struct rpc_clnt *rpc, void *mydata, rpc_clnt_event_t event, void *data) { xlator_t *this = NULL; glusterfs_ctx_t *ctx = NULL; server_cmdline_t *server = NULL; rpc_transport_t *rpc_trans = NULL; struct glfs *fs = NULL; int ret = 0; static int log_ctr2; struct dnscache6 *dnscache = NULL; this = mydata; rpc_trans = rpc->conn.trans; ctx = this->ctx; if (!ctx) goto out; fs = ((xlator_t *)ctx->root)->private; switch (event) { case RPC_CLNT_DISCONNECT: if (!ctx->active) { if (rpc_trans->connect_failed) gf_smsg("glfs-mgmt", GF_LOG_ERROR, 0, API_MSG_REMOTE_HOST_CONN_FAILED, "server=%s", ctx->cmd_args.volfile_server, NULL); else gf_smsg("glfs-mgmt", GF_LOG_INFO, 0, API_MSG_REMOTE_HOST_CONN_FAILED, "server=%s", ctx->cmd_args.volfile_server, NULL); if (!rpc->disabled) { /* * Check if dnscache is exhausted for current server * and continue until cache is exhausted */ dnscache = rpc_trans->dnscache; if (dnscache && dnscache->next) { break; } } server = ctx->cmd_args.curr_server; if (server->list.next == &ctx->cmd_args.volfile_servers) { if (!ctx->active) { errno = ENOTCONN; gf_smsg("glfs-mgmt", GF_LOG_INFO, ENOTCONN, API_MSG_VOLFILE_SERVER_EXHAUST, NULL); glfs_init_done(fs, -1); break; } else { server = list_first_entry( &ctx->cmd_args.volfile_servers, typeof(*server), list); GF_LOG_OCCASIONALLY(log_ctr2, "glusterfsd-mgmt", GF_LOG_INFO, "Exhausted all volfile servers, " "Retrying from again!"); } } else { server = list_entry(server->list.next, typeof(*server), list); } ctx->cmd_args.curr_server = server; ctx->cmd_args.volfile_server_port = server->port; ctx->cmd_args.volfile_server = server->volfile_server; ctx->cmd_args.volfile_server_transport = server->transport; ret = dict_set_str(rpc_trans->options, "transport-type", server->transport); if (ret != 0) { gf_smsg("glfs-mgmt", GF_LOG_ERROR, ENOTCONN, API_MSG_DICT_SET_FAILED, "transport-type=%s", server->transport, NULL); errno = ENOTCONN; glfs_init_done(fs, -1); break; } if (strcmp(server->transport, "unix") == 0) { ret = dict_set_str(rpc_trans->options, "transport.socket.connect-path", server->volfile_server); if (ret != 0) { gf_smsg("glfs-mgmt", GF_LOG_ERROR, ENOTCONN, API_MSG_DICT_SET_FAILED, "socket.connect-path=%s", server->volfile_server, NULL); errno = ENOTCONN; glfs_init_done(fs, -1); break; } /* delete the remote-host and remote-port keys * in case they were set while looping through * list of volfile servers previously */ dict_del(rpc_trans->options, "remote-host"); dict_del(rpc_trans->options, "remote-port"); } else { ret = dict_set_int32(rpc_trans->options, "remote-port", server->port); if (ret != 0) { gf_smsg("glfs-mgmt", GF_LOG_ERROR, ENOTCONN, API_MSG_DICT_SET_FAILED, "remote-port=%d", server->port, NULL); errno = ENOTCONN; glfs_init_done(fs, -1); break; } ret = dict_set_str(rpc_trans->options, "remote-host", server->volfile_server); if (ret != 0) { gf_smsg("glfs-mgmt", GF_LOG_ERROR, ENOTCONN, API_MSG_DICT_SET_FAILED, "remote-host=%s", server->volfile_server, NULL); errno = ENOTCONN; glfs_init_done(fs, -1); break; } /* delete the "transport.socket.connect-path" * key in case if it was set while looping * through list of volfile servers previously */ dict_del(rpc_trans->options, "transport.socket.connect-path"); } gf_smsg("glfs-mgmt", GF_LOG_INFO, 0, API_MSG_VOLFILE_CONNECTING, "server=%s", server->volfile_server, "port=%d", server->port, "transport=%s", server->transport, NULL); } break; case RPC_CLNT_CONNECT: ret = glfs_volfile_fetch(fs); if (ret && (ctx->active == NULL)) { /* Do it only for the first time */ /* Exit the process.. there are some wrong options */ gf_smsg("glfs-mgmt", GF_LOG_ERROR, EINVAL, API_MSG_GET_VOLFILE_FAILED, "key=%s", ctx->cmd_args.volfile_id, NULL); errno = EINVAL; glfs_init_done(fs, -1); } break; default: break; } out: return 0; } int glusterfs_mgmt_notify(int32_t op, void *data, ...) { int ret = 0; switch (op) { case GF_EN_DEFRAG_STATUS: break; default: break; } return ret; } int glfs_mgmt_init(struct glfs *fs) { cmd_args_t *cmd_args = NULL; struct rpc_clnt *rpc = NULL; dict_t *options = NULL; int ret = -1; int port = GF_DEFAULT_BASE_PORT; char *host = NULL; glusterfs_ctx_t *ctx = NULL; ctx = fs->ctx; cmd_args = &ctx->cmd_args; if (ctx->mgmt) return 0; options = dict_new(); if (!options) goto out; if (cmd_args->volfile_server_port) port = cmd_args->volfile_server_port; if (cmd_args->volfile_server) { host = cmd_args->volfile_server; } else if (cmd_args->volfile_server_transport && !strcmp(cmd_args->volfile_server_transport, "unix")) { host = DEFAULT_GLUSTERD_SOCKFILE; } else { host = "localhost"; } if (cmd_args->volfile_server_transport && !strcmp(cmd_args->volfile_server_transport, "unix")) { ret = rpc_transport_unix_options_build(options, host, 0); } else { xlator_cmdline_option_t *opt = find_xlator_option_in_cmd_args_t( "address-family", cmd_args); ret = rpc_transport_inet_options_build(options, host, port, (opt ? opt->value : NULL)); } if (ret) goto out; rpc = rpc_clnt_new(options, THIS, THIS->name, 8); if (!rpc) { ret = -1; gf_smsg(THIS->name, GF_LOG_WARNING, 0, API_MSG_CREATE_RPC_CLIENT_FAILED, NULL); goto out; } ret = rpc_clnt_register_notify(rpc, mgmt_rpc_notify, THIS); if (ret) { gf_smsg(THIS->name, GF_LOG_WARNING, 0, API_MSG_REG_NOTIFY_FUNC_FAILED, NULL); goto out; } ret = rpcclnt_cbk_program_register(rpc, &mgmt_cbk_prog, THIS); if (ret) { gf_smsg(THIS->name, GF_LOG_WARNING, 0, API_MSG_REG_CBK_FUNC_FAILED, NULL); goto out; } ctx->notify = glusterfs_mgmt_notify; /* This value should be set before doing the 'rpc_clnt_start()' as the notify function uses this variable */ ctx->mgmt = rpc; ret = rpc_clnt_start(rpc); out: if (options) dict_unref(options); return ret; } glusterfs-11.2/api/src/glfs-primary.c000066400000000000000000000077331503123251100175760ustar00rootroot00000000000000/* Copyright (c) 2012-2016 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include "glfs-internal.h" #include "glfs-mem-types.h" #include "gfapi-messages.h" int graph_setup(struct glfs *fs, glusterfs_graph_t *graph) { xlator_t *new_subvol = NULL; xlator_t *old_subvol = NULL; inode_table_t *itable = NULL; int ret = -1; new_subvol = graph->top; /* This is called in a bottom-up context, it should specifically NOT be glfs_lock() */ pthread_mutex_lock(&fs->mutex); { if (new_subvol->switched || new_subvol == fs->active_subvol || new_subvol == fs->next_subvol || new_subvol == fs->mip_subvol) { /* Spurious CHILD_UP event on old graph */ ret = 0; goto unlock; } if (!new_subvol->itable) { itable = inode_table_new(131072, new_subvol, 0, 0); if (!itable) { errno = ENOMEM; ret = -1; goto unlock; } new_subvol->itable = itable; } old_subvol = fs->next_subvol; fs->next_subvol = new_subvol; fs->next_subvol->winds++; /* first ref */ ret = 0; } unlock: pthread_mutex_unlock(&fs->mutex); if (old_subvol) /* wasn't picked up so far, skip */ glfs_subvol_done(fs, old_subvol); return ret; } int notify(xlator_t *this, int event, void *data, ...) { glusterfs_graph_t *graph = NULL; struct glfs *fs = NULL; graph = data; fs = this->private; switch (event) { case GF_EVENT_GRAPH_NEW: gf_smsg(this->name, GF_LOG_INFO, 0, API_MSG_NEW_GRAPH, "graph-uuid=%s", uuid_utoa((unsigned char *)graph->graph_uuid), "id=%d", graph->id, NULL); break; case GF_EVENT_CHILD_UP: pthread_mutex_lock(&fs->mutex); { graph->used = 1; } pthread_mutex_unlock(&fs->mutex); graph_setup(fs, graph); glfs_init_done(fs, 0); break; case GF_EVENT_CHILD_DOWN: pthread_mutex_lock(&fs->mutex); { graph->used = 0; pthread_cond_broadcast(&fs->child_down_cond); } pthread_mutex_unlock(&fs->mutex); glfs_init_done(fs, 1); break; case GF_EVENT_CHILD_CONNECTING: break; case GF_EVENT_UPCALL: glfs_process_upcall_event(fs, data); break; default: gf_msg_debug(this->name, 0, "got notify event %d", event); break; } return 0; } int mem_acct_init(xlator_t *this) { int ret = -1; if (!this) return ret; ret = xlator_mem_acct_init(this, glfs_mt_end + 1); if (ret) { gf_smsg(this->name, GF_LOG_ERROR, ENOMEM, API_MSG_MEM_ACCT_INIT_FAILED, NULL); return ret; } return 0; } int init(xlator_t *this) { return 0; } void fini(xlator_t *this) { } /* place-holder fops */ int glfs_forget(xlator_t *this, inode_t *inode) { return 0; } int glfs_release(xlator_t *this, fd_t *fd) { return 0; } int glfs_releasedir(xlator_t *this, fd_t *fd) { return 0; } struct xlator_dumpops dumpops; struct xlator_fops fops; struct xlator_cbks cbks = { .forget = glfs_forget, .release = glfs_release, .releasedir = glfs_releasedir, }; xlator_api_t xlator_api = { .init = init, .fini = fini, .notify = notify, .mem_acct_init = mem_acct_init, .op_version = {1}, .dumpops = &dumpops, .fops = &fops, .cbks = &cbks, .identifier = "glfs-api", .category = GF_MAINTAINED, }; glusterfs-11.2/api/src/glfs-resolve.c000066400000000000000000000776661503123251100176060ustar00rootroot00000000000000/* Copyright (c) 2012-2018 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include "glfs-mem-types.h" #include #include "gfapi-messages.h" #include "glfs-internal.h" #define graphid_str(subvol) \ (uuid_utoa((unsigned char *)subvol->graph->graph_uuid)) int glfs_first_lookup_safe(xlator_t *subvol) { loc_t loc = { 0, }; int ret = -1; loc.inode = subvol->itable->root; memset(loc.gfid, 0, 16); loc.gfid[15] = 1; loc.path = "/"; loc.name = ""; ret = syncop_lookup(subvol, &loc, 0, 0, 0, 0); DECODE_SYNCOP_ERR(ret); gf_msg_debug(subvol->name, 0, "first lookup complete %d", ret); return ret; } int __glfs_first_lookup(struct glfs *fs, xlator_t *subvol) { int ret = -1; fs->migration_in_progress = 1; pthread_mutex_unlock(&fs->mutex); { ret = glfs_first_lookup_safe(subvol); } pthread_mutex_lock(&fs->mutex); fs->migration_in_progress = 0; pthread_cond_broadcast(&fs->cond); /* wake up other waiting tasks */ __GLFS_SYNCTASK_WAKE(fs); return ret; } /** * We have to check if need_lookup flag is set in both old and the new inodes. * If its set in oldinode, then directly go ahead and do an explicit lookup. * But if its not set in the oldinode, then check if the newinode is linked * via readdirp. If so an explicit lookup is needed on the new inode, so that * below xlators can set their respective contexts. */ inode_t * glfs_refresh_inode_safe(xlator_t *subvol, inode_t *oldinode, gf_boolean_t need_lookup) { loc_t loc = { 0, }; int ret = -1; struct iatt iatt = { 0, }; inode_t *newinode = NULL; gf_boolean_t lookup_needed = _gf_false; uint64_t ctx_value = LOOKUP_NOT_NEEDED; if (!oldinode) return NULL; if (!need_lookup && oldinode->table->xl == subvol) return inode_ref(oldinode); newinode = inode_find(subvol->itable, oldinode->gfid); if (!need_lookup && newinode) { lookup_needed = inode_needs_lookup(newinode, THIS); if (!lookup_needed) return newinode; } gf_uuid_copy(loc.gfid, oldinode->gfid); if (!newinode) loc.inode = inode_new(subvol->itable); else loc.inode = newinode; if (!loc.inode) return NULL; ret = syncop_lookup(subvol, &loc, &iatt, 0, 0, 0); DECODE_SYNCOP_ERR(ret); if (ret) { gf_smsg(subvol->name, GF_LOG_WARNING, errno, API_MSG_INODE_REFRESH_FAILED, "gfid=%s", uuid_utoa(oldinode->gfid), "err=%s", strerror(errno), NULL); loc_wipe(&loc); return NULL; } newinode = inode_link(loc.inode, 0, 0, &iatt); if (newinode) { if (newinode == loc.inode) inode_ctx_set(newinode, THIS, &ctx_value); inode_lookup(newinode); } else { gf_smsg(subvol->name, GF_LOG_WARNING, errno, API_MSG_INODE_LINK_FAILED, "gfid=%s", uuid_utoa((unsigned char *)&iatt.ia_gfid), NULL); } loc_wipe(&loc); return newinode; } inode_t * __glfs_refresh_inode(struct glfs *fs, xlator_t *subvol, inode_t *inode, gf_boolean_t need_lookup) { inode_t *newinode = NULL; fs->migration_in_progress = 1; pthread_mutex_unlock(&fs->mutex); { newinode = glfs_refresh_inode_safe(subvol, inode, need_lookup); } pthread_mutex_lock(&fs->mutex); fs->migration_in_progress = 0; pthread_cond_broadcast(&fs->cond); /* wake up other waiting tasks */ __GLFS_SYNCTASK_WAKE(fs); return newinode; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_loc_touchup, 3.4.0) int priv_glfs_loc_touchup(loc_t *loc) { int ret = 0; ret = loc_touchup(loc, loc->name); if (ret < 0) { errno = -ret; ret = -1; } return ret; } int glfs_resolve_symlink(struct glfs *fs, xlator_t *subvol, inode_t *inode, char **lpath) { loc_t loc = { 0, }; char *path = NULL; char *rpath = NULL; int ret = -1; loc.inode = inode_ref(inode); gf_uuid_copy(loc.gfid, inode->gfid); ret = inode_path(inode, NULL, &rpath); if (ret < 0) goto out; loc.path = rpath; ret = syncop_readlink(subvol, &loc, &path, 4096, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret < 0) goto out; if (lpath) *lpath = path; out: loc_wipe(&loc); return ret; } int glfs_resolve_base(struct glfs *fs, xlator_t *subvol, inode_t *inode, struct iatt *iatt) { loc_t loc = { 0, }; int ret = -1; char *path = NULL; loc.inode = inode_ref(inode); gf_uuid_copy(loc.gfid, inode->gfid); ret = inode_path(loc.inode, NULL, &path); loc.path = path; if (ret < 0) goto out; ret = syncop_lookup(subvol, &loc, iatt, NULL, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: loc_wipe(&loc); return ret; } /* * This function can be used to call named lookup on root. * If you use glfs_resolve_base, that will be a nameless lookup. */ static int glfs_resolve_root(struct glfs *fs, xlator_t *subvol, inode_t *inode, struct iatt *iatt) { loc_t loc = { 0, }; int ret = -1; char *path = NULL; loc.inode = inode_ref(inode); ret = inode_path(loc.inode, ".", &path); loc.path = path; loc.name = "."; /* Having a value in loc.name will help to bypass md-cache check for * nameless lookup. * TODO: Re-visit on nameless lookup and md-cache. * Github issue : https://github.com/gluster/glusterfs/issues/232 */ loc.parent = inode_ref(inode); if (ret < 0) goto out; ret = syncop_lookup(subvol, &loc, iatt, NULL, NULL, NULL); DECODE_SYNCOP_ERR(ret); out: loc_wipe(&loc); return ret; } inode_t * glfs_resolve_component(struct glfs *fs, xlator_t *subvol, inode_t *parent, const char *component, struct iatt *iatt, int force_lookup) { loc_t loc = { 0, }; inode_t *inode = NULL; inode_t *temp_parent = NULL; int reval = 0; int ret = -1; int glret = -1; struct iatt ciatt = { 0, }; uuid_t gfid; dict_t *xattr_req = NULL; dict_t *xattr_rsp = NULL; uint64_t ctx_value = LOOKUP_NOT_NEEDED; loc.parent = inode_ref(parent); gf_uuid_copy(loc.pargfid, parent->gfid); if (__is_root_gfid(parent->gfid) && ((strcmp(component, ".") == 0) || (strcmp(component, "..") == 0) || (strcmp(component, "") == 0))) { if (!force_lookup) { inode = inode_ref(parent); } else { ret = glfs_resolve_root(fs, subvol, parent, &ciatt); if (!ret) inode = inode_ref(parent); } goto found; } /* * * if the component name is either "." or "..", it will try to * resolve that if inode has a proper parent (named lookup). * * Below condition works like this * * Example 1 : * Path /out_dir/dir/in_dir/. * In put values : * parent = in_dir * component : "." * * Out put values: * parent : dir * component : "in_dir" * * Example 2 : * Path /out_dir/dir/in_dir/.. * In put values : * parent = in_dir * component : ".." * * Out put values: * parent : output_dir * component : "dir" * * In case of nameless lookup, both "." and ".." retained */ if (strcmp(component, ".") == 0) { loc.inode = inode_ref(parent); temp_parent = inode_parent(loc.inode, 0, 0); if (temp_parent) { inode_unref(loc.parent); loc.parent = temp_parent; gf_uuid_copy(loc.pargfid, temp_parent->gfid); inode_find_directory_name(loc.inode, &loc.name); } } else if (strcmp(component, "..") == 0) { loc.inode = inode_parent(parent, 0, 0); if (loc.inode) { temp_parent = inode_parent(loc.inode, 0, 0); if (temp_parent) { inode_unref(loc.parent); loc.parent = temp_parent; gf_uuid_copy(loc.pargfid, temp_parent->gfid); inode_find_directory_name(loc.inode, &loc.name); } else if (__is_root_gfid(loc.inode->gfid)) { inode_unref(loc.parent); loc.parent = inode_ref(loc.inode); gf_uuid_copy(loc.pargfid, loc.inode->gfid); loc.name = "."; } else { inode_unref(loc.inode); loc.inode = NULL; } } } else loc.inode = inode_grep(parent->table, parent, component); if (!loc.name) loc.name = component; if (loc.inode) { gf_uuid_copy(loc.gfid, loc.inode->gfid); reval = 1; if (!(force_lookup || inode_needs_lookup(loc.inode, THIS))) { inode = inode_ref(loc.inode); goto found; } } else { gf_uuid_generate(gfid); loc.inode = inode_new(parent->table); if (!loc.inode) { errno = ENOMEM; goto out; } xattr_req = dict_new(); if (!xattr_req) { errno = ENOMEM; goto out; } ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { errno = ENOMEM; goto out; } ret = dict_set_int32_sizen(xattr_req, GF_NAMESPACE_KEY, 1); if (ret) { errno = ENOMEM; goto out; } } glret = priv_glfs_loc_touchup(&loc); if (glret < 0) { ret = -1; goto out; } ret = syncop_lookup(subvol, &loc, &ciatt, NULL, xattr_req, &xattr_rsp); if (ret && reval) { /* * A stale mapping might exist for a dentry/inode that has been * removed from another client. */ if (-ret == ENOENT) { inode_unlink(loc.inode, loc.parent, loc.name); if (!inode_has_dentry(loc.inode)) inode_forget(loc.inode, 0); } inode_unref(loc.inode); gf_uuid_clear(loc.gfid); loc.inode = inode_new(parent->table); if (!loc.inode) { errno = ENOMEM; goto out; } if (xattr_rsp) { /* It is possible that this is set in previous call */ dict_unref(xattr_rsp); xattr_rsp = NULL; } if (xattr_req) { /* It is possible that this is created earlier */ dict_unref(xattr_req); xattr_req = NULL; } xattr_req = dict_new(); if (!xattr_req) { errno = ENOMEM; goto out; } gf_uuid_generate(gfid); ret = dict_set_gfuuid(xattr_req, "gfid-req", gfid, true); if (ret) { errno = ENOMEM; goto out; } ret = dict_set_int32_sizen(xattr_req, GF_NAMESPACE_KEY, 1); if (ret) { errno = ENOMEM; goto out; } ret = syncop_lookup(subvol, &loc, &ciatt, NULL, xattr_req, &xattr_rsp); } DECODE_SYNCOP_ERR(ret); if (ret) goto out; inode = inode_link(loc.inode, loc.parent, component, &ciatt); if (!inode) { gf_smsg(subvol->name, GF_LOG_WARNING, errno, API_MSG_INODE_LINK_FAILED, "gfid=%s", uuid_utoa((unsigned char *)&ciatt.ia_gfid), NULL); goto out; } else if (inode == loc.inode) inode_ctx_set(inode, THIS, &ctx_value); found: if (inode) { ciatt.ia_type = inode->ia_type; inode_lookup(inode); if (xattr_rsp && dict_get_sizen(xattr_rsp, GF_NAMESPACE_KEY)) { /* This inode onwards we will set namespace */ inode_set_namespace_inode(inode, inode); } } if (iatt) *iatt = ciatt; out: if (xattr_req) dict_unref(xattr_req); if (xattr_rsp) dict_unref(xattr_rsp); loc_wipe(&loc); return inode; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_resolve_at, 3.4.0) int priv_glfs_resolve_at(struct glfs *fs, xlator_t *subvol, inode_t *at, const char *origpath, loc_t *loc, struct iatt *iatt, int follow, int reval) { inode_t *inode = NULL; inode_t *parent = NULL; char *saveptr = NULL; char *path = NULL; char *component = NULL; char *next_component = NULL; int ret = -1; struct iatt ciatt = { 0, }; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); if (origpath[0] == '\0') { errno = EINVAL; goto invalid_fs; } /* better to prevent a crash by someone passing a null loc */ if (!loc) { errno = EINVAL; goto invalid_fs; } parent = NULL; if (at && origpath[0] != '/') { /* A relative resolution of a path which starts with '/' is equal to an absolute path resolution. */ inode = inode_ref(at); } else { inode = inode_ref(subvol->itable->root); if (strcmp(origpath, "/") == 0) glfs_resolve_root(fs, subvol, inode, &ciatt); } path = gf_strdup(origpath); if (!path) goto invalid_fs; for (component = strtok_r(path, "/", &saveptr); component; component = next_component) { next_component = strtok_r(NULL, "/", &saveptr); if (parent) inode_unref(parent); parent = inode; inode = glfs_resolve_component(fs, subvol, parent, component, &ciatt, /* force hard lookup on the last component, as the caller wants proper iatt filled */ (reval || (!next_component && iatt))); if (!inode) { ret = -1; break; } if (IA_ISLNK(ciatt.ia_type) && (next_component || follow)) { /* If the component is not the last piece, then following it is necessary even if not requested by the caller */ char *lpath = NULL; loc_t sym_loc = { 0, }; if (follow > GLFS_SYMLINK_MAX_FOLLOW) { errno = ELOOP; ret = -1; if (inode) { inode_unref(inode); inode = NULL; } break; } ret = glfs_resolve_symlink(fs, subvol, inode, &lpath); inode_unref(inode); inode = NULL; if (ret < 0) break; ret = priv_glfs_resolve_at(fs, subvol, parent, lpath, &sym_loc, /* followed iatt becomes the component iatt */ &ciatt, /* always recurisvely follow while following symlink */ follow + 1, reval); if (ret == 0) inode = inode_ref(sym_loc.inode); loc_wipe(&sym_loc); GF_FREE(lpath); } if (!next_component) break; if (!IA_ISDIR(ciatt.ia_type)) { /* next_component exists and this component is not a directory */ inode_unref(inode); inode = NULL; ret = -1; errno = ENOTDIR; break; } } if (parent && next_component) /* resolution failed mid-way */ goto out; /* At this point, all components up to the last parent directory have been resolved successfully (@parent). Resolution of basename might have failed (@inode) if at all. */ loc->parent = parent; if (parent) { gf_uuid_copy(loc->pargfid, parent->gfid); loc->name = component; } loc->inode = inode; if (inode) { gf_uuid_copy(loc->gfid, inode->gfid); if (iatt) *iatt = ciatt; ret = 0; } if (priv_glfs_loc_touchup(loc) < 0) { ret = -1; } out: GF_FREE(path); __GLFS_EXIT_FS; /* do NOT loc_wipe here as only last component might be missing */ invalid_fs: return ret; } int glfs_resolve_path(struct glfs *fs, xlator_t *subvol, const char *origpath, loc_t *loc, struct iatt *iatt, int follow, int reval) { int ret = -1; inode_t *cwd = NULL; if (origpath[0] == '/') return priv_glfs_resolve_at(fs, subvol, NULL, origpath, loc, iatt, follow, reval); cwd = glfs_cwd_get(fs); if (NULL == cwd) { gf_smsg(subvol->name, GF_LOG_WARNING, EIO, API_MSG_GET_CWD_FAILED, NULL); errno = EIO; goto out; } ret = priv_glfs_resolve_at(fs, subvol, cwd, origpath, loc, iatt, follow, reval); if (cwd) inode_unref(cwd); out: return ret; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_resolve, 3.7.0) int priv_glfs_resolve(struct glfs *fs, xlator_t *subvol, const char *origpath, loc_t *loc, struct iatt *iatt, int reval) { int ret = -1; ret = glfs_resolve_path(fs, subvol, origpath, loc, iatt, 1, reval); return ret; } int glfs_lresolve(struct glfs *fs, xlator_t *subvol, const char *origpath, loc_t *loc, struct iatt *iatt, int reval) { int ret = -1; ret = glfs_resolve_path(fs, subvol, origpath, loc, iatt, 0, reval); return ret; } int glfs_migrate_fd_locks_safe(struct glfs *fs, xlator_t *oldsubvol, fd_t *oldfd, xlator_t *newsubvol, fd_t *newfd) { dict_t *lockinfo = NULL; int ret = 0; char uuid1[64]; if (!oldfd->lk_ctx || fd_lk_ctx_empty(oldfd->lk_ctx)) return 0; newfd->lk_ctx = fd_lk_ctx_ref(oldfd->lk_ctx); ret = syncop_fgetxattr(oldsubvol, oldfd, &lockinfo, GF_XATTR_LOCKINFO_KEY, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret < 0) { gf_smsg(fs->volname, GF_LOG_WARNING, errno, API_MSG_FGETXATTR_FAILED, "gfid=%s", uuid_utoa_r(oldfd->inode->gfid, uuid1), "err=%s", strerror(errno), "subvol=%s", graphid_str(oldsubvol), "id=%d", oldsubvol->graph->id, NULL); goto out; } if (!dict_get(lockinfo, GF_XATTR_LOCKINFO_KEY)) { gf_smsg(fs->volname, GF_LOG_WARNING, 0, API_MSG_LOCKINFO_KEY_MISSING, "gfid=%s", uuid_utoa_r(oldfd->inode->gfid, uuid1), "subvol=%s", graphid_str(oldsubvol), "id=%d", oldsubvol->graph->id, NULL); goto out; } ret = syncop_fsetxattr(newsubvol, newfd, lockinfo, 0, NULL, NULL); DECODE_SYNCOP_ERR(ret); if (ret < 0) { gf_smsg(fs->volname, GF_LOG_WARNING, 0, API_MSG_FSETXATTR_FAILED, "gfid=%s", uuid_utoa_r(newfd->inode->gfid, uuid1), "err=%s", strerror(errno), "subvol=%s", graphid_str(newsubvol), "id=%d", newsubvol->graph->id, NULL); goto out; } out: if (lockinfo) dict_unref(lockinfo); return ret; } fd_t * glfs_migrate_fd_safe(struct glfs *fs, xlator_t *newsubvol, fd_t *oldfd) { fd_t *newfd = NULL; inode_t *oldinode = NULL; inode_t *newinode = NULL; xlator_t *oldsubvol = NULL; int ret = -1; loc_t loc = { 0, }; char uuid1[64]; dict_t *xdata = NULL; oldinode = oldfd->inode; oldsubvol = oldinode->table->xl; if (oldsubvol == newsubvol) return fd_ref(oldfd); if (!oldsubvol->switched) { xdata = dict_new(); if (!xdata || dict_set_int8(xdata, "last-fsync", 1)) { gf_smsg(fs->volname, GF_LOG_WARNING, ENOMEM, API_MSG_FSYNC_FAILED, "err=%s", "last-fsync set failed", "gfid=%s", uuid_utoa_r(oldfd->inode->gfid, uuid1), "subvol=%s", graphid_str(oldsubvol), "id=%d", oldsubvol->graph->id, NULL); } ret = syncop_fsync(oldsubvol, oldfd, 0, NULL, NULL, xdata, NULL); DECODE_SYNCOP_ERR(ret); if (ret) { gf_smsg(fs->volname, GF_LOG_WARNING, errno, API_MSG_FSYNC_FAILED, "err=%s", strerror(errno), "gfid=%s", uuid_utoa_r(oldfd->inode->gfid, uuid1), "subvol=%s", graphid_str(oldsubvol), "id=%d", oldsubvol->graph->id, NULL); } } newinode = glfs_refresh_inode_safe(newsubvol, oldinode, _gf_false); if (!newinode) { gf_smsg(fs->volname, GF_LOG_WARNING, errno, API_MSG_INODE_REFRESH_FAILED, "gfid=%s", uuid_utoa_r(oldinode->gfid, uuid1), "err=%s", strerror(errno), "subvol=%s", graphid_str(newsubvol), "id=%d", newsubvol->graph->id, NULL); goto out; } newfd = fd_create(newinode, getpid()); if (!newfd) { gf_smsg(fs->volname, GF_LOG_WARNING, errno, API_MSG_FDCREATE_FAILED_ON_GRAPH, "gfid=%s", uuid_utoa_r(newinode->gfid, uuid1), "err=%s", strerror(errno), "subvol=%s", graphid_str(newsubvol), "id=%d", newsubvol->graph->id, NULL); goto out; } loc.inode = inode_ref(newinode); ret = inode_path(oldfd->inode, NULL, (char **)&loc.path); if (ret < 0) { gf_smsg(fs->volname, GF_LOG_INFO, 0, API_MSG_INODE_PATH_FAILED, NULL); goto out; } gf_uuid_copy(loc.gfid, oldinode->gfid); if (IA_ISDIR(oldinode->ia_type)) ret = syncop_opendir(newsubvol, &loc, newfd, NULL, NULL); else ret = syncop_open(newsubvol, &loc, oldfd->flags & ~(O_TRUNC | O_EXCL | O_CREAT), newfd, NULL, NULL); DECODE_SYNCOP_ERR(ret); loc_wipe(&loc); if (ret) { gf_smsg(fs->volname, GF_LOG_WARNING, errno, API_MSG_SYNCOP_OPEN_FAILED, "type=%s", IA_ISDIR(oldinode->ia_type) ? "dir" : "", "gfid=%s", uuid_utoa_r(newinode->gfid, uuid1), "err=%s", strerror(errno), "subvol=%s", graphid_str(newsubvol), "id=%d", newsubvol->graph->id, NULL); goto out; } ret = glfs_migrate_fd_locks_safe(fs, oldsubvol, oldfd, newsubvol, newfd); if (ret) { gf_smsg(fs->volname, GF_LOG_WARNING, errno, API_MSG_LOCK_MIGRATE_FAILED, "gfid=%s", uuid_utoa_r(newinode->gfid, uuid1), "err=%s", strerror(errno), "subvol=%s", graphid_str(newsubvol), "id=%d", newsubvol->graph->id, NULL); goto out; } newfd->flags = oldfd->flags; fd_bind(newfd); out: if (newinode) inode_unref(newinode); if (ret) { fd_unref(newfd); newfd = NULL; } if (xdata) dict_unref(xdata); return newfd; } fd_t * __glfs_migrate_fd(struct glfs *fs, xlator_t *newsubvol, struct glfs_fd *glfd) { fd_t *oldfd = NULL; fd_t *newfd = NULL; oldfd = glfd->fd; fs->migration_in_progress = 1; pthread_mutex_unlock(&fs->mutex); { newfd = glfs_migrate_fd_safe(fs, newsubvol, oldfd); } pthread_mutex_lock(&fs->mutex); fs->migration_in_progress = 0; pthread_cond_broadcast(&fs->cond); /* wake up other waiting tasks */ __GLFS_SYNCTASK_WAKE(fs); return newfd; } fd_t * __glfs_resolve_fd(struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd) { fd_t *fd = NULL; if (glfd->fd->inode->table->xl == subvol) return fd_ref(glfd->fd); fd = __glfs_migrate_fd(fs, subvol, glfd); if (!fd) return NULL; if (subvol == fs->active_subvol) { fd_unref(glfd->fd); glfd->fd = fd_ref(fd); } return fd; } fd_t * glfs_resolve_fd(struct glfs *fs, xlator_t *subvol, struct glfs_fd *glfd) { fd_t *fd = NULL; glfs_lock(fs, _gf_true); { fd = __glfs_resolve_fd(fs, subvol, glfd); } glfs_unlock(fs); return fd; } void __glfs_migrate_openfds(struct glfs *fs, xlator_t *subvol) { struct glfs_fd *glfd = NULL; fd_t *fd = NULL; list_for_each_entry(glfd, &fs->openfds, openfds) { if (gf_uuid_is_null(glfd->fd->inode->gfid)) { gf_smsg(fs->volname, GF_LOG_INFO, 0, API_MSG_OPENFD_SKIPPED, "glfd=%p", glfd, "glfd->fd=%p", glfd->fd, "subvol=%s", graphid_str(subvol), "id=%d", subvol->graph->id, NULL); /* create in progress, defer */ continue; } fd = __glfs_migrate_fd(fs, subvol, glfd); if (fd) { fd_unref(glfd->fd); glfd->fd = fd; } } } /* Note that though it appears that this function executes under fs->mutex, * it is not fully executed under fs->mutex. i.e. there are functions like * __glfs_first_lookup, __glfs_refresh_inode, __glfs_migrate_openfds which * unlocks fs->mutex before sending any network fop, and reacquire fs->mutex * once the fop is complete. Hence the variable read from fs at the start of the * function need not have the same value by the end of the function. */ xlator_t * __glfs_active_subvol(struct glfs *fs) { xlator_t *new_subvol = NULL; int ret = -1; inode_t *new_cwd = NULL; if (!fs->next_subvol) return fs->active_subvol; new_subvol = fs->mip_subvol = fs->next_subvol; fs->next_subvol = NULL; ret = __glfs_first_lookup(fs, new_subvol); if (ret) { gf_smsg(fs->volname, GF_LOG_INFO, errno, API_MSG_FIRST_LOOKUP_GRAPH_FAILED, "subvol=%s", graphid_str(new_subvol), "id=%d", new_subvol->graph->id, "err=%s", strerror(errno), NULL); return NULL; } if (fs->cwd) { new_cwd = __glfs_refresh_inode(fs, new_subvol, fs->cwd, _gf_false); if (!new_cwd) { char buf1[64]; gf_smsg(fs->volname, GF_LOG_INFO, errno, API_MSG_CWD_GRAPH_REF_FAILED, "buf=%s", uuid_utoa_r(fs->cwd->gfid, buf1), "subvol=%s", graphid_str(new_subvol), "id=%d", new_subvol->graph->id, "err=%s", strerror(errno), NULL); return NULL; } } __glfs_migrate_openfds(fs, new_subvol); /* TODO: Migrate the fds and inodes which have leases to the new graph * (issue #350)*/ /* switching @active_subvol and @cwd should be atomic */ fs->old_subvol = fs->active_subvol; fs->active_subvol = fs->mip_subvol; fs->mip_subvol = NULL; if (new_cwd) { __glfs_cwd_set(fs, new_cwd); inode_unref(new_cwd); } gf_smsg(fs->volname, GF_LOG_INFO, 0, API_MSG_SWITCHED_GRAPH, "subvol=%s", graphid_str(new_subvol), "id=%d", new_subvol->graph->id, NULL); return new_subvol; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_subvol_done, 3.4.0) void priv_glfs_subvol_done(struct glfs *fs, xlator_t *subvol) { int ref = 0; xlator_t *active_subvol = NULL; if (!subvol) return; /* For decrementing subvol->wind ref count we need not check/wait for * migration-in-progress flag. * Also glfs_subvol_done is called in call-back path therefore waiting * for migration-in-progress flag can lead to dead-lock. */ glfs_lock(fs, _gf_false); { ref = (--subvol->winds); active_subvol = fs->active_subvol; } glfs_unlock(fs); if (ref == 0) { assert(subvol != active_subvol); xlator_notify(subvol, GF_EVENT_PARENT_DOWN, subvol, NULL); } } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_active_subvol, 3.4.0) xlator_t * priv_glfs_active_subvol(struct glfs *fs) { xlator_t *subvol = NULL; xlator_t *old_subvol = NULL; glfs_lock(fs, _gf_true); { subvol = __glfs_active_subvol(fs); if (subvol) subvol->winds++; if (fs->old_subvol) { old_subvol = fs->old_subvol; fs->old_subvol = NULL; old_subvol->switched = 1; } } glfs_unlock(fs); if (old_subvol) priv_glfs_subvol_done(fs, old_subvol); return subvol; } int __glfs_cwd_set(struct glfs *fs, inode_t *inode) { if (inode->table->xl != fs->active_subvol) { inode = __glfs_refresh_inode(fs, fs->active_subvol, inode, _gf_false); if (!inode) return -1; } else { inode_ref(inode); } if (fs->cwd) inode_unref(fs->cwd); fs->cwd = inode; return 0; } int glfs_cwd_set(struct glfs *fs, inode_t *inode) { int ret = 0; glfs_lock(fs, _gf_true); { ret = __glfs_cwd_set(fs, inode); } glfs_unlock(fs); return ret; } inode_t * __glfs_cwd_get(struct glfs *fs) { inode_t *cwd = NULL; if (!fs->cwd) return NULL; if (fs->cwd->table->xl == fs->active_subvol) { cwd = inode_ref(fs->cwd); return cwd; } cwd = __glfs_refresh_inode(fs, fs->active_subvol, fs->cwd, _gf_false); return cwd; } inode_t * glfs_cwd_get(struct glfs *fs) { inode_t *cwd = NULL; glfs_lock(fs, _gf_true); { cwd = __glfs_cwd_get(fs); } glfs_unlock(fs); return cwd; } inode_t * __glfs_resolve_inode(struct glfs *fs, xlator_t *subvol, struct glfs_object *object) { inode_t *inode = NULL; gf_boolean_t lookup_needed = _gf_false; lookup_needed = inode_needs_lookup(object->inode, THIS); if (!lookup_needed && object->inode->table->xl == subvol) return inode_ref(object->inode); inode = __glfs_refresh_inode(fs, fs->active_subvol, object->inode, lookup_needed); if (!inode) return NULL; if (subvol == fs->active_subvol) { inode_unref(object->inode); object->inode = inode_ref(inode); } return inode; } inode_t * glfs_resolve_inode(struct glfs *fs, xlator_t *subvol, struct glfs_object *object) { inode_t *inode = NULL; glfs_lock(fs, _gf_true); { inode = __glfs_resolve_inode(fs, subvol, object); } glfs_unlock(fs); return inode; } int glfs_create_object(loc_t *loc, struct glfs_object **retobject) { struct glfs_object *object = NULL; object = GF_CALLOC(1, sizeof(struct glfs_object), glfs_mt_glfs_object_t); if (object == NULL) { errno = ENOMEM; return -1; } object->inode = loc->inode; gf_uuid_copy(object->gfid, object->inode->gfid); /* we hold the reference */ loc->inode = NULL; *retobject = object; return 0; } struct glfs_object * glfs_h_resolve_symlink(struct glfs *fs, struct glfs_object *object) { xlator_t *subvol = NULL; loc_t sym_loc = { 0, }; struct iatt iatt = { 0, }; char *lpath = NULL; int ret = 0; struct glfs_object *target_object = NULL; subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } ret = glfs_resolve_symlink(fs, subvol, object->inode, &lpath); if (ret < 0) goto out; ret = glfs_resolve_at(fs, subvol, NULL, lpath, &sym_loc, &iatt, /* always recurisvely follow while following symlink */ 1, 0); if (ret == 0) ret = glfs_create_object(&sym_loc, &target_object); out: loc_wipe(&sym_loc); GF_FREE(lpath); return target_object; } void fd_to_loc(struct glfs_fd *glfd, loc_t *loc) { loc->inode = inode_ref(glfd->fd->inode); loc->parent = inode_parent(glfd->fd->inode, NULL, NULL); uuid_copy(loc->gfid, glfd->fd->inode->gfid); } glusterfs-11.2/api/src/glfs.c000066400000000000000000001242361503123251100161130ustar00rootroot00000000000000/* Copyright (c) 2012-2018 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ /* TODO: - set proper pid/lk_owner to call frames (currently buried in syncop) - fix logging.c/h to store logfp and loglevel in glusterfs_ctx_t and reach it via THIS. - update syncop functions to accept/return xdata. ??? - protocol/client to reconnect immediately after portmap disconnect. - handle SEEK_END failure in _lseek() - handle umask (per filesystem?) - make itables LRU based - 0-copy for readv/writev - reconcile the open/creat mess */ #include #include #include #include #include #include #include #include #ifdef GF_LINUX_HOST_OS #include #endif #include #include #include #include "glfs-mem-types.h" #include #include "rpc-clnt.h" #include #include #include "gfapi-messages.h" #include "glfs.h" #include "glfs-internal.h" static gf_boolean_t vol_assigned(cmd_args_t *args) { return args->volfile || args->volfile_server; } static int glusterfs_ctx_defaults_init(glusterfs_ctx_t *ctx) { call_pool_t *pool = NULL; int ret = -1; if (!ctx) { goto err; } ret = xlator_mem_acct_init(THIS, glfs_mt_end + 1); if (ret != 0) { gf_smsg(THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_MEM_ACCT_INIT_FAILED, NULL); return ret; } /* reset ret to -1 so that we don't need to explicitly * set it in all error paths before "goto err" */ ret = -1; ctx->process_uuid = generate_glusterfs_ctx_id(); if (!ctx->process_uuid) { goto err; } ctx->page_size = 128 * GF_UNIT_KB; ctx->iobuf_pool = iobuf_pool_new(); if (!ctx->iobuf_pool) { goto err; } ctx->event_pool = gf_event_pool_new(DEFAULT_EVENT_POOL_SIZE, STARTING_EVENT_THREADS); if (!ctx->event_pool) { goto err; } ctx->env = syncenv_new(0, 0, 0); if (!ctx->env) { goto err; } pool = GF_CALLOC(1, sizeof(call_pool_t), glfs_mt_call_pool_t); if (!pool) { goto err; } /* frame_mem_pool size 112 * 4k */ pool->frame_mem_pool = mem_pool_new(call_frame_t, 4096); if (!pool->frame_mem_pool) { goto err; } /* stack_mem_pool size 256 * 1024 */ pool->stack_mem_pool = mem_pool_new(call_stack_t, 1024); if (!pool->stack_mem_pool) { goto err; } ctx->dict_pool = mem_pool_new(dict_t, GF_MEMPOOL_COUNT_OF_DICT_T); if (!ctx->dict_pool) goto err; ctx->dict_data_pool = mem_pool_new(data_t, GF_MEMPOOL_COUNT_OF_DATA_T); if (!ctx->dict_data_pool) goto err; ctx->logbuf_pool = mem_pool_new(log_buf_t, GF_MEMPOOL_COUNT_OF_LRU_BUF_T); if (!ctx->logbuf_pool) goto err; INIT_LIST_HEAD(&pool->all_frames); INIT_LIST_HEAD(&ctx->cmd_args.xlator_options); INIT_LIST_HEAD(&ctx->cmd_args.volfile_servers); LOCK_INIT(&pool->lock); ctx->pool = pool; ret = 0; err: if (ret && pool) { if (pool->frame_mem_pool) mem_pool_destroy(pool->frame_mem_pool); if (pool->stack_mem_pool) mem_pool_destroy(pool->stack_mem_pool); GF_FREE(pool); } if (ret && ctx) { if (ctx->dict_pool) mem_pool_destroy(ctx->dict_pool); if (ctx->dict_data_pool) mem_pool_destroy(ctx->dict_data_pool); if (ctx->logbuf_pool) mem_pool_destroy(ctx->logbuf_pool); } return ret; } static int create_primary(struct glfs *fs) { int ret = 0; xlator_t *primary = NULL; primary = GF_CALLOC(1, sizeof(*primary), glfs_mt_xlator_t); if (!primary) goto err; primary->name = gf_strdup("gfapi"); if (!primary->name) goto err; if (xlator_set_type(primary, "mount/api") == -1) { gf_smsg("glfs", GF_LOG_ERROR, 0, API_MSG_PRIMARY_XLATOR_INIT_FAILED, "name=%s", fs->volname, NULL); goto err; } primary->ctx = fs->ctx; primary->private = fs; primary->options = dict_new(); if (!primary->options) goto err; ret = xlator_init(primary); if (ret) { gf_smsg("glfs", GF_LOG_ERROR, 0, API_MSG_GFAPI_XLATOR_INIT_FAILED, NULL); goto err; } fs->ctx->root = primary; THIS = primary; return 0; err: if (primary) { xlator_destroy(primary); } return -1; } static FILE * get_volfp(struct glfs *fs) { cmd_args_t *cmd_args = NULL; FILE *specfp = NULL; cmd_args = &fs->ctx->cmd_args; if ((specfp = fopen(cmd_args->volfile, "r")) == NULL) { gf_smsg("glfs", GF_LOG_ERROR, errno, API_MSG_VOLFILE_OPEN_FAILED, "file=%s", cmd_args->volfile, "err=%s", strerror(errno), NULL); return NULL; } gf_msg_debug("glfs", 0, "loading volume file %s", cmd_args->volfile); return specfp; } int glfs_volumes_init(struct glfs *fs) { FILE *fp = NULL; cmd_args_t *cmd_args = NULL; int ret = 0; cmd_args = &fs->ctx->cmd_args; if (!vol_assigned(cmd_args)) return -1; if (sys_access(SECURE_ACCESS_FILE, F_OK) == 0) { fs->ctx->secure_mgmt = 1; fs->ctx->ssl_cert_depth = glusterfs_read_secure_access_file(); } if (cmd_args->volfile_server) { ret = glfs_mgmt_init(fs); goto out; } fp = get_volfp(fs); if (!fp) { gf_smsg("glfs", GF_LOG_ERROR, ENOENT, API_MSG_VOL_SPEC_FILE_ERROR, NULL); ret = -1; goto out; } ret = glfs_process_volfp(fs, fp); if (ret) goto out; out: return ret; } /////////////////////////////////////////////////////////////////////////////// GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_xlator_option, 3.4.0) int pub_glfs_set_xlator_option(struct glfs *fs, const char *xlator, const char *key, const char *value) { xlator_cmdline_option_t *option = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); option = GF_CALLOC(1, sizeof(*option), glfs_mt_xlator_cmdline_option_t); if (!option) goto enomem; INIT_LIST_HEAD(&option->cmd_args); option->volume = gf_strdup(xlator); if (!option->volume) goto enomem; option->key = gf_strdup(key); if (!option->key) goto enomem; option->value = gf_strdup(value); if (!option->value) goto enomem; list_add(&option->cmd_args, &fs->ctx->cmd_args.xlator_options); __GLFS_EXIT_FS; return 0; enomem: errno = ENOMEM; if (!option) { __GLFS_EXIT_FS; return -1; } GF_FREE(option->volume); GF_FREE(option->key); GF_FREE(option->value); GF_FREE(option); __GLFS_EXIT_FS; invalid_fs: return -1; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_unset_volfile_server, 3.5.1) int pub_glfs_unset_volfile_server(struct glfs *fs, const char *transport, const char *host, const int port) { cmd_args_t *cmd_args = NULL; server_cmdline_t *server = NULL; server_cmdline_t *tmp = NULL; char *transport_val = NULL; int port_val = 0; int ret = -1; if (!fs || !host) { errno = EINVAL; return ret; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); cmd_args = &fs->ctx->cmd_args; if (transport) { transport_val = gf_strdup(transport); } else { transport_val = gf_strdup(GF_DEFAULT_VOLFILE_TRANSPORT); } if (!transport_val) { errno = ENOMEM; goto out; } if (port) { port_val = port; } else { port_val = GF_DEFAULT_BASE_PORT; } list_for_each_entry_safe(server, tmp, &cmd_args->curr_server->list, list) { if (!server->volfile_server || !server->transport) continue; if ((!strcmp(server->volfile_server, host) && !strcmp(server->transport, transport_val) && (server->port == port_val))) { list_del(&server->list); ret = 0; goto out; } } out: GF_FREE(transport_val); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_volfile_server, 3.4.0) int pub_glfs_set_volfile_server(struct glfs *fs, const char *transport, const char *host, int port) { cmd_args_t *cmd_args = NULL; int ret = -1; char *server_host = NULL; char *server_transport = NULL; if (!fs || !host) { errno = EINVAL; return ret; } DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); cmd_args = &fs->ctx->cmd_args; cmd_args->max_connect_attempts = 1; server_host = gf_strdup(host); if (!server_host) { errno = ENOMEM; goto out; } if (transport) { /* volfile fetch support over tcp|unix only */ if (!strcmp(transport, "tcp") || !strcmp(transport, "unix")) { server_transport = gf_strdup(transport); } else if (!strcmp(transport, "rdma")) { server_transport = gf_strdup(GF_DEFAULT_VOLFILE_TRANSPORT); gf_smsg("glfs", GF_LOG_WARNING, EINVAL, API_MSG_TRANS_RDMA_DEP, NULL); } else { gf_smsg("glfs", GF_LOG_TRACE, EINVAL, API_MSG_TRANS_NOT_SUPPORTED, "transport=%s", transport, NULL); goto out; } } else { server_transport = gf_strdup(GF_DEFAULT_VOLFILE_TRANSPORT); } if (!server_transport) { errno = ENOMEM; goto out; } if (!port) { port = GF_DEFAULT_BASE_PORT; } if (!strcmp(server_transport, "unix")) { port = 0; } ret = gf_set_volfile_server_common(cmd_args, server_host, server_transport, port); if (ret) { gf_log("glfs", GF_LOG_ERROR, "failed to set volfile server: %s", strerror(errno)); } out: if (server_host) { GF_FREE(server_host); } if (server_transport) { GF_FREE(server_transport); } __GLFS_EXIT_FS; invalid_fs: return ret; } /* * * Used to free the arguments allocated by glfs_set_volfile_server() */ static void glfs_free_volfile_servers(cmd_args_t *cmd_args) { server_cmdline_t *server = NULL; server_cmdline_t *tmp = NULL; GF_VALIDATE_OR_GOTO(THIS->name, cmd_args, out); list_for_each_entry_safe(server, tmp, &cmd_args->volfile_servers, list) { list_del_init(&server->list); GF_FREE(server->volfile_server); GF_FREE(server->transport); GF_FREE(server); } cmd_args->curr_server = NULL; out: return; } static void glfs_free_xlator_options(cmd_args_t *cmd_args) { xlator_cmdline_option_t *xo = NULL; xlator_cmdline_option_t *tmp_xo = NULL; GF_VALIDATE_OR_GOTO(THIS->name, cmd_args, out); list_for_each_entry_safe(xo, tmp_xo, &cmd_args->xlator_options, cmd_args) { list_del_init(&xo->cmd_args); GF_FREE(xo->volume); GF_FREE(xo->key); GF_FREE(xo->value); GF_FREE(xo); } out: return; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setfsuid, 3.4.2) int pub_glfs_setfsuid(uid_t fsuid) { /* TODO: * - Set the THIS and restore it appropriately */ return syncopctx_setfsuid(&fsuid); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setfsgid, 3.4.2) int pub_glfs_setfsgid(gid_t fsgid) { /* TODO: * - Set the THIS and restore it appropriately */ return syncopctx_setfsgid(&fsgid); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setfsgroups, 3.4.2) int pub_glfs_setfsgroups(size_t size, const gid_t *list) { /* TODO: * - Set the THIS and restore it appropriately */ return syncopctx_setfsgroups(size, list); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_setfsleaseid, 4.0.0) int pub_glfs_setfsleaseid(glfs_leaseid_t leaseid) { int ret = -1; char *gleaseid = NULL; gleaseid = gf_leaseid_get(); if (gleaseid) { if (leaseid) memcpy(gleaseid, leaseid, LEASE_ID_SIZE); else /* reset leaseid */ memset(gleaseid, 0, LEASE_ID_SIZE); ret = 0; } if (ret) gf_log("glfs", GF_LOG_ERROR, "failed to set leaseid: %s", strerror(errno)); return ret; } int get_fop_attr_glfd(dict_t **fop_attr, struct glfs_fd *glfd) { char *leaseid = NULL; int ret = 0; gf_boolean_t dict_create = _gf_false; leaseid = GF_MALLOC(LEASE_ID_SIZE, gf_common_mt_char); GF_CHECK_ALLOC_AND_LOG("gfapi", leaseid, ret, "lease id alloc failed", out); memcpy(leaseid, glfd->lease_id, LEASE_ID_SIZE); if (*fop_attr == NULL) { *fop_attr = dict_new(); dict_create = _gf_true; } GF_CHECK_ALLOC_AND_LOG("gfapi", *fop_attr, ret, "dict_new failed", out); ret = dict_set_bin(*fop_attr, "lease-id", leaseid, LEASE_ID_SIZE); out: if (ret) { GF_FREE(leaseid); if (dict_create) { if (*fop_attr) dict_unref(*fop_attr); *fop_attr = NULL; } } return ret; } int set_fop_attr_glfd(struct glfs_fd *glfd) { char *lease_id = NULL; int ret = -1; lease_id = gf_existing_leaseid(); if (lease_id) { memcpy(glfd->lease_id, lease_id, LEASE_ID_SIZE); ret = 0; } return ret; } int get_fop_attr_thrd_key(dict_t **fop_attr) { char *existing_leaseid = NULL, *leaseid = NULL; int ret = 0; gf_boolean_t dict_create = _gf_false; existing_leaseid = gf_existing_leaseid(); if (existing_leaseid) { leaseid = GF_MALLOC(LEASE_ID_SIZE, gf_common_mt_char); GF_CHECK_ALLOC_AND_LOG("gfapi", leaseid, ret, "lease id alloc failed", out); memcpy(leaseid, existing_leaseid, LEASE_ID_SIZE); if (*fop_attr == NULL) { *fop_attr = dict_new(); dict_create = _gf_true; } GF_CHECK_ALLOC_AND_LOG("gfapi", *fop_attr, ret, "dict_new failed", out); ret = dict_set_bin(*fop_attr, "lease-id", leaseid, LEASE_ID_SIZE); } out: if (ret) { GF_FREE(leaseid); if (dict_create) { if (*fop_attr) dict_unref(*fop_attr); *fop_attr = NULL; } } return ret; } void unset_fop_attr(dict_t **fop_attr) { char *lease_id = NULL; lease_id = gf_existing_leaseid(); if (lease_id) memset(lease_id, 0, LEASE_ID_SIZE); if (*fop_attr) { dict_unref(*fop_attr); *fop_attr = NULL; } } int validate_open_flags(int flags, ia_type_t ia_type) { int ret = 0; if (IA_ISDIR(ia_type)) { if ((flags & (O_RDWR | O_WRONLY)) || ((flags & O_CREAT) && !(flags & O_DIRECTORY))) { ret = -1; errno = EISDIR; } } else { if (flags & O_DIRECTORY) { ret = -1; errno = ENOTDIR; } else if (!IA_ISREG(ia_type)) { ret = -1; errno = EINVAL; } } return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_from_glfd, 3.4.0) struct glfs * pub_glfs_from_glfd(struct glfs_fd *glfd) { if (glfd == NULL) { errno = EBADF; return NULL; } return glfd->fs; } static void glfs_fd_destroy(struct glfs_fd *glfd) { if (!glfd) return; glfs_lock(glfd->fs, _gf_true); { list_del_init(&glfd->openfds); } glfs_unlock(glfd->fs); if (glfd->fd) { fd_unref(glfd->fd); glfd->fd = NULL; } GF_FREE(glfd->readdirbuf); LOCK_DESTROY(&glfd->lock); GF_FREE(glfd); } struct glfs_fd * glfs_fd_new(struct glfs *fs) { struct glfs_fd *glfd = NULL; glfd = GF_CALLOC(1, sizeof(*glfd), glfs_mt_glfs_fd_t); if (!glfd) return NULL; glfd->fs = fs; INIT_LIST_HEAD(&glfd->openfds); INIT_LIST_HEAD(&glfd->entries); GF_REF_INIT(glfd, glfs_fd_destroy); LOCK_INIT(&glfd->lock); return glfd; } void glfs_fd_bind(struct glfs_fd *glfd) { struct glfs *fs = NULL; fs = glfd->fs; glfs_lock(fs, _gf_true); { list_add_tail(&glfd->openfds, &fs->openfds); } glfs_unlock(fs); } static void * glfs_poller(void *data) { struct glfs *fs = NULL; fs = data; gf_event_dispatch(fs->ctx->event_pool); return NULL; } static struct glfs * glfs_new_fs(const char *volname) { struct glfs *fs = NULL; fs = CALLOC(1, sizeof(*fs)); if (!fs) return NULL; INIT_LIST_HEAD(&fs->openfds); INIT_LIST_HEAD(&fs->upcall_list); INIT_LIST_HEAD(&fs->waitq); PTHREAD_MUTEX_INIT(&fs->mutex, NULL, fs->pthread_flags, GLFS_INIT_MUTEX, err); PTHREAD_COND_INIT(&fs->cond, NULL, fs->pthread_flags, GLFS_INIT_COND, err); PTHREAD_COND_INIT(&fs->child_down_cond, NULL, fs->pthread_flags, GLFS_INIT_COND_CHILD, err); PTHREAD_MUTEX_INIT(&fs->upcall_list_mutex, NULL, fs->pthread_flags, GLFS_INIT_MUTEX_UPCALL, err); fs->volname = strdup(volname); if (!fs->volname) goto err; fs->pin_refcnt = 0; fs->upcall_events = 0; fs->up_cbk = NULL; fs->up_data = NULL; return fs; err: glfs_free_from_ctx(fs); return NULL; } extern xlator_t global_xlator; extern glusterfs_ctx_t *global_ctx; extern pthread_mutex_t global_ctx_mutex; static int glfs_init_global_ctx(void) { int ret = 0; glusterfs_ctx_t *ctx = NULL; pthread_mutex_lock(&global_ctx_mutex); { if (global_xlator.ctx) goto unlock; ctx = glusterfs_ctx_new(); if (!ctx) { ret = -1; goto unlock; } gf_log_globals_init(ctx, GF_LOG_NONE); global_ctx = ctx; global_xlator.ctx = global_ctx; ret = glusterfs_ctx_defaults_init(ctx); if (ret) { global_ctx = NULL; global_xlator.ctx = NULL; goto unlock; } } unlock: pthread_mutex_unlock(&global_ctx_mutex); if (ret) FREE(ctx); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_new, 3.4.0) struct glfs * pub_glfs_new(const char *volname) { if (!volname) { errno = EINVAL; return NULL; } struct glfs *fs = NULL; int i = 0; int ret = -1; glusterfs_ctx_t *ctx = NULL; xlator_t *old_THIS = NULL; char pname[16] = ""; char msg[32] = ""; if (volname[0] == '/' || volname[0] == '-') { if (strncmp(volname, "/snaps/", 7) == 0) { goto label; } errno = EINVAL; return NULL; } for (i = 0; i < strlen(volname); i++) { if (!isalnum(volname[i]) && (volname[i] != '_') && (volname[i] != '-')) { errno = EINVAL; return NULL; } } label: /* * Do this as soon as possible in case something else depends on * pool allocations. */ mem_pools_init(); fs = glfs_new_fs(volname); if (!fs) goto out; ctx = glusterfs_ctx_new(); if (!ctx) goto out; /* first globals init, for gf_mem_acct_enable_set () */ ret = glusterfs_globals_init(ctx); if (ret) goto out; old_THIS = THIS; ret = glfs_init_global_ctx(); if (ret) goto out; /* then ctx_defaults_init, for xlator_mem_acct_init(THIS) */ ret = glusterfs_ctx_defaults_init(ctx); if (ret) goto out; fs->ctx = ctx; fs->ctx->process_mode = GF_CLIENT_PROCESS; ret = glfs_set_logging(fs, "/dev/null", 0); if (ret) goto out; fs->ctx->cmd_args.volfile_id = gf_strdup(volname); if (!(fs->ctx->cmd_args.volfile_id)) { ret = -1; goto out; } ret = -1; #ifdef GF_LINUX_HOST_OS ret = prctl(PR_GET_NAME, (unsigned long)pname, 0, 0, 0); #endif if (ret) fs->ctx->cmd_args.process_name = gf_strdup("gfapi"); else { snprintf(msg, sizeof(msg), "gfapi.%s", pname); fs->ctx->cmd_args.process_name = gf_strdup(msg); } ret = 0; out: if (ret) { if (fs) { glfs_fini(fs); fs = NULL; } else { /* glfs_fini() calls mem_pools_fini() too */ mem_pools_fini(); } } if (old_THIS) THIS = old_THIS; return fs; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_new_from_ctx, 3.7.0) struct glfs * priv_glfs_new_from_ctx(glusterfs_ctx_t *ctx) { struct glfs *fs = NULL; if (!ctx) goto out; fs = glfs_new_fs(""); if (!fs) goto out; fs->ctx = ctx; out: return fs; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_free_from_ctx, 3.7.0) void priv_glfs_free_from_ctx(struct glfs *fs) { upcall_entry *u_list = NULL; upcall_entry *tmp = NULL; if (!fs) return; /* cleanup upcall structures */ list_for_each_entry_safe(u_list, tmp, &fs->upcall_list, upcall_list) { list_del_init(&u_list->upcall_list); GF_FREE(u_list->upcall_data.data); GF_FREE(u_list); } PTHREAD_MUTEX_DESTROY(&fs->mutex, fs->pthread_flags, GLFS_INIT_MUTEX); PTHREAD_COND_DESTROY(&fs->cond, fs->pthread_flags, GLFS_INIT_COND); PTHREAD_COND_DESTROY(&fs->child_down_cond, fs->pthread_flags, GLFS_INIT_COND_CHILD); PTHREAD_MUTEX_DESTROY(&fs->upcall_list_mutex, fs->pthread_flags, GLFS_INIT_MUTEX_UPCALL); if (fs->oldvolfile) FREE(fs->oldvolfile); FREE(fs->volname); FREE(fs); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_volfile, 3.4.0) int pub_glfs_set_volfile(struct glfs *fs, const char *volfile) { cmd_args_t *cmd_args = NULL; cmd_args = &fs->ctx->cmd_args; if (vol_assigned(cmd_args)) return -1; cmd_args->volfile = gf_strdup(volfile); if (!cmd_args->volfile) return -1; return 0; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_logging, 3.4.0) int pub_glfs_set_logging(struct glfs *fs, const char *logfile, int loglevel) { int ret = -1; char *tmplog = NULL; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); if (!logfile) { ret = gf_set_log_file_path(&fs->ctx->cmd_args, fs->ctx); if (ret) goto out; tmplog = fs->ctx->cmd_args.log_file; } else { tmplog = (char *)logfile; } /* finish log set parameters before init */ if (loglevel >= 0) gf_log_set_loglevel(fs->ctx, loglevel); ret = gf_log_init(fs->ctx, tmplog, NULL); if (ret) goto out; ret = gf_log_inject_timer_event(fs->ctx); if (ret) goto out; out: __GLFS_EXIT_FS; invalid_fs: return ret; } int glfs_init_wait(struct glfs *fs) { int ret = -1; /* Always a top-down call, use glfs_lock() */ glfs_lock(fs, _gf_true); { while (!fs->init) pthread_cond_wait(&fs->cond, &fs->mutex); ret = fs->ret; errno = fs->err; } glfs_unlock(fs); return ret; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_init_done, 3.4.0) void priv_glfs_init_done(struct glfs *fs, int ret) { glfs_init_cbk init_cbk; if (!fs) { gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_GLFS_FSOBJ_NULL, NULL); goto out; } init_cbk = fs->init_cbk; /* Always a bottom-up call, use mutex_lock() */ pthread_mutex_lock(&fs->mutex); { fs->init = 1; fs->ret = ret; fs->err = errno; if (!init_cbk) pthread_cond_broadcast(&fs->cond); } pthread_mutex_unlock(&fs->mutex); if (init_cbk) init_cbk(fs, ret); out: return; } int glfs_init_common(struct glfs *fs) { int ret = -1; ret = create_primary(fs); if (ret) return ret; ret = gf_thread_create(&fs->poller, NULL, glfs_poller, fs, "glfspoll"); if (ret) return ret; ret = glfs_volumes_init(fs); if (ret) return ret; fs->dev_id = gf_dm_hashfn(fs->volname, strlen(fs->volname)); return ret; } int glfs_init_async(struct glfs *fs, glfs_init_cbk cbk) { int ret = -1; if (!fs || !fs->ctx) { gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_FS_NOT_INIT, NULL); errno = EINVAL; return ret; } fs->init_cbk = cbk; ret = glfs_init_common(fs); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_init, 3.4.0) int pub_glfs_init(struct glfs *fs) { int ret = -1; DECLARE_OLD_THIS; if (!fs || !fs->ctx) { gf_smsg("glfs", GF_LOG_ERROR, EINVAL, API_MSG_FS_NOT_INIT, NULL); errno = EINVAL; return ret; } __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); ret = glfs_init_common(fs); if (ret) goto out; ret = glfs_init_wait(fs); out: __GLFS_EXIT_FS; /* Set the initial current working directory to "/" */ if (ret >= 0) { ret = glfs_chdir(fs, "/"); } invalid_fs: return ret; } static int glusterfs_ctx_destroy(glusterfs_ctx_t *ctx) { call_pool_t *pool = NULL; int ret = 0; glusterfs_graph_t *trav_graph = NULL; glusterfs_graph_t *tmp = NULL; if (ctx == NULL) return 0; if (ctx->cmd_args.curr_server) glfs_free_volfile_servers(&ctx->cmd_args); glfs_free_xlator_options(&ctx->cmd_args); /* For all the graphs, crawl through the xlator_t structs and free * all its members except for the mem_acct member, * as GF_FREE will be referencing it. */ list_for_each_entry_safe(trav_graph, tmp, &ctx->graphs, list) { xlator_tree_free_members(trav_graph->first); } /* Free the memory pool */ if (ctx->dict_pool) mem_pool_destroy(ctx->dict_pool); if (ctx->dict_data_pool) mem_pool_destroy(ctx->dict_data_pool); if (ctx->logbuf_pool) mem_pool_destroy(ctx->logbuf_pool); pool = ctx->pool; if (pool) { if (pool->frame_mem_pool) mem_pool_destroy(pool->frame_mem_pool); if (pool->stack_mem_pool) mem_pool_destroy(pool->stack_mem_pool); LOCK_DESTROY(&pool->lock); GF_FREE(pool); } /* Free the event pool */ ret = gf_event_pool_destroy(ctx->event_pool); /* Free the iobuf pool */ iobuf_pool_destroy(ctx->iobuf_pool); GF_FREE(ctx->process_uuid); GF_FREE(ctx->cmd_args.volfile_id); GF_FREE(ctx->cmd_args.process_name); LOCK_DESTROY(&ctx->lock); LOCK_DESTROY(&ctx->volfile_lock); pthread_mutex_destroy(&ctx->notify_lock); pthread_cond_destroy(&ctx->notify_cond); /* Free all the graph structs and its containing xlator_t structs * from this point there should be no reference to GF_FREE/GF_CALLOC * as it will try to access mem_acct and the below function would * have freed the same. */ list_for_each_entry_safe(trav_graph, tmp, &ctx->graphs, list) { glusterfs_graph_destroy_residual(trav_graph); } GF_FREE(ctx->statedump_path); FREE(ctx->hostname); FREE(ctx); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_fini, 3.4.0) int pub_glfs_fini(struct glfs *fs) { int ret = -1; int countdown = 100; xlator_t *subvol = NULL; glusterfs_ctx_t *ctx = NULL; glusterfs_graph_t *graph = NULL; call_pool_t *call_pool = NULL; int fs_init = 0; int err = -1; struct synctask *waittask = NULL; DECLARE_OLD_THIS; if (!fs) { errno = EINVAL; goto invalid_fs; } ctx = fs->ctx; if (!ctx) { goto free_fs; } THIS = fs->ctx->root; if (ctx->mgmt) { rpc_clnt_disable(ctx->mgmt); } call_pool = fs->ctx->pool; /* Wake up any suspended synctasks */ while (!list_empty(&fs->waitq)) { waittask = list_entry(fs->waitq.next, struct synctask, waitq); list_del_init(&waittask->waitq); synctask_wake(waittask); } while (countdown--) { /* give some time for background frames to finish */ pthread_mutex_lock(&fs->mutex); { /* Do we need to increase countdown? */ if ((!call_pool->cnt) && (!fs->pin_refcnt)) { gf_msg_trace("glfs", 0, "call_pool_cnt - %" PRId64 "," "pin_refcnt - %d", call_pool->cnt, fs->pin_refcnt); ctx->cleanup_started = 1; pthread_mutex_unlock(&fs->mutex); break; } } pthread_mutex_unlock(&fs->mutex); gf_nanosleep(100000 * GF_US_IN_NS); } /* leaked frames may exist, we ignore */ /*We deem glfs_fini as successful if there are no pending frames in the call *pool*/ ret = (call_pool->cnt == 0) ? 0 : -1; pthread_mutex_lock(&fs->mutex); { fs_init = fs->init; } pthread_mutex_unlock(&fs->mutex); if (fs_init != 0) { subvol = glfs_active_subvol(fs); if (subvol) { /* PARENT_DOWN within glfs_subvol_done() is issued only on graph switch (new graph should activiate and decrement the extra @winds count taken in glfs_graph_setup() Since we are explicitly destroying, PARENT_DOWN is necessary */ xlator_notify(subvol, GF_EVENT_PARENT_DOWN, subvol, 0); /* Here we wait for GF_EVENT_CHILD_DOWN before exiting, in case of asynchrnous cleanup */ graph = subvol->graph; err = pthread_mutex_lock(&fs->mutex); if (err != 0) { gf_smsg("glfs", GF_LOG_ERROR, err, API_MSG_FSMUTEX_LOCK_FAILED, "error=%s", strerror(err), NULL); goto fail; } /* check and wait for CHILD_DOWN for active subvol*/ { while (graph->used) { err = pthread_cond_wait(&fs->child_down_cond, &fs->mutex); if (err != 0) gf_smsg("glfs", GF_LOG_INFO, err, API_MSG_COND_WAIT_FAILED, "name=%s", subvol->name, "err=%s", strerror(err), NULL); } } err = pthread_mutex_unlock(&fs->mutex); if (err != 0) { gf_smsg("glfs", GF_LOG_ERROR, err, API_MSG_FSMUTEX_UNLOCK_FAILED, "error=%s", strerror(err), NULL); goto fail; } } glfs_subvol_done(fs, subvol); } ctx->cleanup_started = 1; if (fs_init != 0) { /* Destroy all the inode tables of all the graphs. * NOTE: * - inode objects should be destroyed before calling fini() * of each xlator, as fini() and forget() of the xlators * can share few common locks or data structures, calling * fini first might destroy those required by forget * ( eg: in quick-read) * - The call to inode_table_destroy_all is not required when * the cleanup during graph switch is implemented to perform * inode table destroy. */ inode_table_destroy_all(ctx); /* Call fini() of all the xlators in the active graph * NOTE: * - xlator fini() should be called before destroying any of * the threads. (eg: fini() in protocol-client uses timer * thread) */ glusterfs_graph_deactivate(ctx->active); /* Join the syncenv_processor threads and cleanup * syncenv resources*/ syncenv_destroy(ctx->env); /* Join the poller thread */ if (gf_event_dispatch_destroy(ctx->event_pool) < 0) ret = -1; } /* Avoid dispatching events to mgmt after freed, * unreference mgmt after the event_dispatch_destroy */ if (ctx->mgmt) { rpc_clnt_unref(ctx->mgmt); ctx->mgmt = NULL; } /* log infra has to be brought down before destroying * timer registry, as logging uses timer infra */ if (gf_log_fini(ctx) != 0) ret = -1; /* Join the timer thread */ if (fs_init != 0) { gf_timer_registry_destroy(ctx); } /* Destroy the context and the global pools */ if (glusterfs_ctx_destroy(ctx) != 0) ret = -1; free_fs: glfs_free_from_ctx(fs); /* * Do this as late as possible in case anything else has (or * grows) a dependency on mem-pool allocations. */ mem_pools_fini(); fail: if (!ret) ret = err; __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_get_volfile, 3.6.0) ssize_t pub_glfs_get_volfile(struct glfs *fs, void *buf, size_t len) { ssize_t res = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); glfs_lock(fs, _gf_true); if (len >= fs->oldvollen) { gf_msg_trace("glfs", 0, "copying %zu to %p", len, buf); memcpy(buf, fs->oldvolfile, len); res = len; } else { res = len - fs->oldvollen; gf_msg_trace("glfs", 0, "buffer is %zd too short", -res); } glfs_unlock(fs); __GLFS_EXIT_FS; invalid_fs: return res; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_ipc, 3.12.0) int priv_glfs_ipc(struct glfs *fs, int opcode, void *xd_in, void **xd_out) { xlator_t *subvol = NULL; int ret = -1; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); subvol = glfs_active_subvol(fs); if (!subvol) { ret = -1; errno = EIO; goto out; } ret = syncop_ipc(subvol, opcode, (dict_t *)xd_in, (dict_t **)xd_out); DECODE_SYNCOP_ERR(ret); out: glfs_subvol_done(fs, subvol); __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_setfspid, 6.1) int priv_glfs_setfspid(struct glfs *fs, pid_t pid) { cmd_args_t *cmd_args = NULL; int ret = 0; cmd_args = &fs->ctx->cmd_args; cmd_args->client_pid = pid; cmd_args->client_pid_set = 1; ret = syncopctx_setfspid(&pid); return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_free, 3.7.16) void pub_glfs_free(void *ptr) { GLFS_FREE(ptr); } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_get_fs, 3.7.16) struct glfs * pub_glfs_upcall_get_fs(struct glfs_upcall *arg) { return arg->fs; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_get_reason, 3.7.16) enum glfs_upcall_reason pub_glfs_upcall_get_reason(struct glfs_upcall *arg) { return arg->reason; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_get_event, 3.7.16) void * pub_glfs_upcall_get_event(struct glfs_upcall *arg) { return arg->event; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_object, 3.7.16) struct glfs_object * pub_glfs_upcall_inode_get_object(struct glfs_upcall_inode *arg) { return arg->object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_flags, 3.7.16) uint64_t pub_glfs_upcall_inode_get_flags(struct glfs_upcall_inode *arg) { return arg->flags; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_stat, 3.7.16) struct stat * pub_glfs_upcall_inode_get_stat(struct glfs_upcall_inode *arg) { return &arg->buf; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_expire, 3.7.16) uint64_t pub_glfs_upcall_inode_get_expire(struct glfs_upcall_inode *arg) { return arg->expire_time_attr; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_pobject, 3.7.16) struct glfs_object * pub_glfs_upcall_inode_get_pobject(struct glfs_upcall_inode *arg) { return arg->p_object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_pstat, 3.7.16) struct stat * pub_glfs_upcall_inode_get_pstat(struct glfs_upcall_inode *arg) { return &arg->p_buf; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_oldpobject, 3.7.16) struct glfs_object * pub_glfs_upcall_inode_get_oldpobject(struct glfs_upcall_inode *arg) { return arg->oldp_object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_inode_get_oldpstat, 3.7.16) struct stat * pub_glfs_upcall_inode_get_oldpstat(struct glfs_upcall_inode *arg) { return &arg->oldp_buf; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_lease_get_object, 4.1.6) struct glfs_object * pub_glfs_upcall_lease_get_object(struct glfs_upcall_lease *arg) { return arg->object; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_lease_get_lease_type, 4.1.6) uint32_t pub_glfs_upcall_lease_get_lease_type(struct glfs_upcall_lease *arg) { return arg->lease_type; } /* definitions of the GLFS_SYSRQ_* chars are in glfs.h */ static struct glfs_sysrq_help { char sysrq; char *msg; } glfs_sysrq_help[] = {{GLFS_SYSRQ_HELP, "(H)elp"}, {GLFS_SYSRQ_STATEDUMP, "(S)tatedump"}, {0, NULL}}; GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_sysrq, 3.10.0) int pub_glfs_sysrq(struct glfs *fs, char sysrq) { glusterfs_ctx_t *ctx = NULL; int ret = 0; int msg_len; char msg[1024] = { 0, }; /* should not exceed 1024 chars */ if (!fs || !fs->ctx) { ret = -1; errno = EINVAL; goto out; } ctx = fs->ctx; switch (sysrq) { case GLFS_SYSRQ_HELP: { struct glfs_sysrq_help *usage = NULL; for (usage = glfs_sysrq_help; usage->sysrq; usage++) { msg_len = strlen(msg); snprintf(msg + msg_len, /* append to msg */ sizeof(msg) - msg_len - 2, /* - 2 for the " " + terminating \0 */ " %s", usage->msg); } /* not really an 'error', but make sure it gets logged */ gf_log("glfs", GF_LOG_ERROR, "available events: %s", msg); break; } case GLFS_SYSRQ_STATEDUMP: gf_proc_dump_info(SIGUSR1, ctx); break; default: gf_smsg("glfs", GF_LOG_ERROR, ENOTSUP, API_MSG_INVALID_SYSRQ, "sysrq=%c", sysrq, NULL); errno = ENOTSUP; ret = -1; } out: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_register, 3.13.0) int pub_glfs_upcall_register(struct glfs *fs, uint32_t event_list, glfs_upcall_cbk cbk, void *data) { int ret = 0; /* list of supported upcall events */ uint32_t up_events = (GLFS_EVENT_INODE_INVALIDATE | GLFS_EVENT_RECALL_LEASE); DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); GF_VALIDATE_OR_GOTO(THIS->name, cbk, out); /* Event list should be either GLFS_EVENT_ANY * or list of supported individual events (up_events) */ if ((event_list != GLFS_EVENT_ANY) && (event_list & ~up_events)) { errno = EINVAL; ret = -1; gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ARG, "event_list=(0x%08x)", event_list, NULL); goto out; } /* in case other thread does unregister */ pthread_mutex_lock(&fs->mutex); { if (event_list & GLFS_EVENT_INODE_INVALIDATE) { /* @todo: Check if features.cache-invalidation is * enabled. */ fs->upcall_events |= GF_UPCALL_CACHE_INVALIDATION; ret |= GLFS_EVENT_INODE_INVALIDATE; } if (event_list & GLFS_EVENT_RECALL_LEASE) { /* @todo: Check if features.leases is enabled */ fs->upcall_events |= GF_UPCALL_RECALL_LEASE; ret |= GLFS_EVENT_RECALL_LEASE; } /* Override cbk function if existing */ fs->up_cbk = cbk; fs->up_data = data; fs->cache_upcalls = _gf_true; } pthread_mutex_unlock(&fs->mutex); out: __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_unregister, 3.13.0) int pub_glfs_upcall_unregister(struct glfs *fs, uint32_t event_list) { int ret = 0; /* list of supported upcall events */ uint32_t up_events = (GLFS_EVENT_INODE_INVALIDATE | GLFS_EVENT_RECALL_LEASE); DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); /* Event list should be either GLFS_EVENT_ANY * or list of supported individual events (up_events) */ if ((event_list != GLFS_EVENT_ANY) && (event_list & ~up_events)) { errno = EINVAL; ret = -1; gf_smsg(THIS->name, GF_LOG_ERROR, errno, API_MSG_INVALID_ARG, "event_list=(0x%08x)", event_list, NULL); goto out; } pthread_mutex_lock(&fs->mutex); { /* We already checked if event_list contains list of supported * upcall events. No other specific checks needed as of now for * unregister */ fs->upcall_events &= ~(event_list); ret |= ((event_list == GLFS_EVENT_ANY) ? up_events : event_list); /* If there are no upcall events registered, reset cbk */ if (fs->upcall_events == 0) { fs->up_cbk = NULL; fs->up_data = NULL; fs->cache_upcalls = _gf_false; } } pthread_mutex_unlock(&fs->mutex); out: __GLFS_EXIT_FS; invalid_fs: return ret; } GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_set_statedump_path, 7.0) int pub_glfs_set_statedump_path(struct glfs *fs, const char *path) { struct stat st; int ret; DECLARE_OLD_THIS; __GLFS_ENTRY_VALIDATE_FS(fs, invalid_fs); if (!path) { gf_log("glfs", GF_LOG_ERROR, "path is NULL"); errno = EINVAL; goto err; } /* If path is not present OR, if it is directory AND has enough permission * to create files, then proceed */ ret = sys_stat(path, &st); if (ret && errno != ENOENT) { gf_log("glfs", GF_LOG_ERROR, "%s: not a valid path (%s)", path, strerror(errno)); errno = EINVAL; goto err; } if (!ret) { /* file is present, now check other things */ if (!S_ISDIR(st.st_mode)) { gf_log("glfs", GF_LOG_ERROR, "%s: path is not directory", path); errno = EINVAL; goto err; } if (sys_access(path, W_OK | X_OK) < 0) { gf_log("glfs", GF_LOG_ERROR, "%s: path doesn't have write permission", path); errno = EPERM; goto err; } } /* If set, it needs to be freed, so we don't have leak */ GF_FREE(fs->ctx->statedump_path); fs->ctx->statedump_path = gf_strdup(path); if (!fs->ctx->statedump_path) { gf_log("glfs", GF_LOG_ERROR, "%s: failed to set statedump path, no memory", path); errno = ENOMEM; goto err; } __GLFS_EXIT_FS; return 0; err: __GLFS_EXIT_FS; invalid_fs: return -1; } glusterfs-11.2/api/src/glfs.h000066400000000000000000001405021503123251100161120ustar00rootroot00000000000000/* Copyright (c) 2012-2018 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #ifndef _GLFS_H #define _GLFS_H /* Enforce the following flags as libgfapi is built with them, and we want programs linking against them to also be built with these flags. This is necessary as it affects some of the structures defined in libc headers (like struct stat) and those definitions need to be consistently compiled in both the library and the application. */ /* Values for valid flags to be used when using XXXsetattr, to set multiple attribute values passed via the related stat structure. */ #define GFAPI_SET_ATTR_MODE 0x1 #define GFAPI_SET_ATTR_UID 0x2 #define GFAPI_SET_ATTR_GID 0x4 #define GFAPI_SET_ATTR_SIZE 0x8 #define GFAPI_SET_ATTR_ATIME 0x10 #define GFAPI_SET_ATTR_MTIME 0x20 #ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 #endif #ifndef __USE_FILE_OFFSET64 #define __USE_FILE_OFFSET64 #endif #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include /* * For off64_t to be defined, we need both * __USE_LARGEFILE64 to be true and __off64_t_defnined to be * false. But, making __USE_LARGEFILE64 true causes other issues * such as redinition of stat and fstat to stat64 and fstat64 * respectively which again causes compilation issues. * Without off64_t being defined, this will not compile as * copy_file_range uses off64_t. Hence define it here. First * check whether __off64_t_defined is true or not. * sets that flag when it defines off64_t. If __off64_t_defined * is false and __USE_FILE_OFFSET64 is true, then go on to define * off64_t using __off64_t. */ #ifndef GF_BSD_HOST_OS #if defined(__USE_FILE_OFFSET64) && !defined(__off64_t_defined) typedef __off64_t off64_t; #endif /* defined(__USE_FILE_OFFSET64) && !defined(__off64_t_defined) */ #else #include #ifndef _OFF64_T_DECLARED /* * Including (done above) should actually define * _OFF64_T_DECLARED with off64_t data type being available * for consumption. But, off64_t data type is not recognizable * for FreeBSD versions less than 11. Hence, int64_t is typedefed * to off64_t. */ #define _OFF64_T_DECLARED typedef int64_t off64_t; #endif /* _OFF64_T_DECLARED */ #endif /* GF_BSD_HOST_OS */ #if defined(HAVE_SYS_ACL_H) || (defined(USE_POSIX_ACLS) && USE_POSIX_ACLS) #include #else typedef void *acl_t; typedef int acl_type_t; #endif /* Portability non glibc c++ build systems */ #ifndef __THROW #if defined __cplusplus #define __THROW throw() #else #define __THROW #endif #endif #ifndef GF_DARWIN_HOST_OS #define GFAPI_PUBLIC(sym, ver) /**/ #define GFAPI_PRIVATE(sym, ver) /**/ #else #define GFAPI_PUBLIC(sym, ver) __asm("_" __STRING(sym) "$GFAPI_" __STRING(ver)) #define GFAPI_PRIVATE(sym, ver) \ __asm("_" __STRING(sym) "$GFAPI_PRIVATE_" __STRING(ver)) #endif __BEGIN_DECLS /* The filesystem object. One object per 'virtual mount' */ struct glfs; typedef struct glfs glfs_t; /* SYNOPSIS glfs_new: Create a new 'virtual mount' object. DESCRIPTION This is most likely the very first function you will use. This function will create a new glfs_t (virtual mount) object in memory. On this newly created glfs_t, you need to be either set a volfile path (glfs_set_volfile) or a volfile server (glfs_set_volfile_server). The glfs_t object needs to be initialized with glfs_init() before you can start issuing file operations on it. PARAMETERS @volname: Name of the volume. This identifies the server-side volume and the fetched volfile (equivalent of --volfile-id command line parameter to glusterfsd). When used with glfs_set_volfile() the @volname has no effect (except for appearing in log messages). RETURN VALUES NULL : Out of memory condition. Others : Pointer to the newly created glfs_t virtual mount object. */ glfs_t * glfs_new(const char *volname) __THROW GFAPI_PUBLIC(glfs_new, 3.4.0); /* SYNOPSIS glfs_set_volfile: Specify the path to the volume specification file. DESCRIPTION If you are using a static volume specification file (without dynamic volume management abilities from the CLI), then specify the path to the volume specification file. This is incompatible with glfs_set_volfile_server(). PARAMETERS @fs: The 'virtual mount' object to be configured with the volume specification file. @volfile: Path to the locally available volume specification file. RETURN VALUES 0 : Success. -1 : Failure. @errno will be set with the type of failure. */ int glfs_set_volfile(glfs_t *fs, const char *volfile) __THROW GFAPI_PUBLIC(glfs_set_volfile, 3.4.0); /* SYNOPSIS glfs_set_volfile_server: Specify the list of addresses for management server. DESCRIPTION This function specifies the list of addresses for the management server (glusterd) to connect, and establish the volume configuration. The @volname parameter passed to glfs_new() is the volume which will be virtually mounted as the glfs_t object. All operations performed by the CLI at the management server will automatically be reflected in the 'virtual mount' object as it maintains a connection to glusterd and polls on configuration change notifications. This is incompatible with glfs_set_volfile(). PARAMETERS @fs: The 'virtual mount' object to be configured with the volume specification file. @transport: String specifying the transport used to connect to the management daemon. Specifying NULL will result in the usage of the default (tcp) transport type. Permitted values are "tcp" or "unix". @host: String specifying the address where to find the management daemon. Socket path, while using Unix domain socket as transport type. This would either be - FQDN (e.g : "storage01.company.com") or - ASCII (e.g : "192.168.22.1") or - Socket path (e.g : "/var/run/glusterd.socket") NOTE: This API is special, multiple calls to this function with different volfile servers, port or transport-type would create a list of volfile servers which would be polled during `volfile_fetch_attempts()` @port: The TCP port number where gluster management daemon is listening. Specifying 0 uses the default port number GF_DEFAULT_BASE_PORT. This parameter is unused if you are using a UNIX domain socket. RETURN VALUES 0 : Success. -1 : Failure. @errno will be set with the type of failure. */ int glfs_set_volfile_server(glfs_t *fs, const char *transport, const char *host, int port) __THROW GFAPI_PUBLIC(glfs_set_volfile_server, 3.4.0); int glfs_unset_volfile_server(glfs_t *fs, const char *transport, const char *host, int port) __THROW GFAPI_PUBLIC(glfs_unset_volfile_server, 3.5.1); /* SYNOPSIS glfs_set_logging: Specify logging parameters. DESCRIPTION This function specifies logging parameters for the virtual mount. Default log file is /dev/null. PARAMETERS @fs: The 'virtual mount' object to be configured with the logging parameters. @logfile: The logfile to be used for logging. Will be created if it does not already exist (provided system permissions allow). If NULL, a new logfile will be created in default log directory associated with the glusterfs installation. @loglevel: Numerical value specifying the degree of verbosity. Higher the value, more verbose the logging. RETURN VALUES 0 : Success. -1 : Failure. @errno will be set with the type of failure. */ int glfs_set_logging(glfs_t *fs, const char *logfile, int loglevel) __THROW GFAPI_PUBLIC(glfs_set_logging, 3.4.0); /* SYNOPSIS glfs_init: Initialize the 'virtual mount' DESCRIPTION This function initializes the glfs_t object. This consists of many steps: - Spawn a poll-loop thread. - Establish connection to management daemon and receive volume specification. - Construct translator graph and initialize graph. - Wait for initialization (connecting to all bricks) to complete. PARAMETERS @fs: The 'virtual mount' object to be initialized. RETURN VALUES 0 : Success. -1 : Failure. @errno will be set with the type of failure. */ int glfs_init(glfs_t *fs) __THROW GFAPI_PUBLIC(glfs_init, 3.4.0); /* SYNOPSIS glfs_fini: Cleanup and destroy the 'virtual mount' DESCRIPTION This function attempts to gracefully destroy glfs_t object. An attempt is made to wait for all background processing to complete before returning. glfs_fini() must be called after all operations on glfs_t is finished. IMPORTANT IT IS NECESSARY TO CALL glfs_fini() ON ALL THE INITIALIZED glfs_t OBJECTS BEFORE TERMINATING THE PROGRAM. THERE MAY BE CACHED AND UNWRITTEN / INCOMPLETE OPERATIONS STILL IN PROGRESS EVEN THOUGH THE API CALLS HAVE RETURNED. glfs_fini() WILL WAIT FOR BACKGROUND OPERATIONS TO COMPLETE BEFORE RETURNING, THEREBY MAKING IT SAFE FOR THE PROGRAM TO EXIT. PARAMETERS @fs: The 'virtual mount' object to be destroyed. RETURN VALUES 0 : Success. */ int glfs_fini(glfs_t *fs) __THROW GFAPI_PUBLIC(glfs_fini, 3.4.0); /* SYNOPSIS glfs_get_volfile: Get the volfile associated with a 'virtual mount' DESCRIPTION Sometimes it's useful e.g. for scripts to see the volfile, so that they can parse it and find subvolumes to do things like split-brain resolution or custom layouts. The API here was specifically intended to make access e.g. from Python as simple as possible. Note that the volume must be started (not necessarily mounted) for this to work. PARAMETERS @fs: The 'virtual mount' object for which a volfile is desired @buf: Pointer to a place for the volfile length to be stored @len: Length of @buf RETURN VALUES >0: filled N bytes of buffer 0: no volfile available <0: volfile length exceeds @len by N bytes (@buf unchanged) */ ssize_t glfs_get_volfile(glfs_t *fs, void *buf, size_t len) __THROW GFAPI_PUBLIC(glfs_get_volfile, 3.6.0); /* SYNOPSIS glfs_get_volumeid: Copy the Volume UUID stored in the glfs object fs. DESCRIPTION This function when invoked for the first time sends RPC call to the the management server (glusterd) to fetch volume uuid and stores it in the glusterfs_context linked to the glfs object fs which can be used in the subsequent calls. Later it parses that UUID to convert it from canonical string format into an opaque byte array and copy it into the volid array. In case if either of the input parameters, volid or size, is NULL, number of bytes required to copy the volume UUID is returned. PARAMETERS @fs: The 'virtual mount' object to be used to retrieve and store volume's UUID. @volid: Pointer to a place for the volume UUID to be stored @size: Length of @volid RETURN VALUES -1 : Failure. @errno will be set with the type of failure. Others : length of the volume UUID stored. */ int glfs_get_volumeid(glfs_t *fs, char *volid, size_t size) __THROW GFAPI_PUBLIC(glfs_get_volumeid, 3.5.0); /* * FILE OPERATION * * What follows are filesystem operations performed on the * 'virtual mount'. The calls here are kept as close to * the POSIX system calls as possible. * * Notes: * * - All paths specified, even if absolute, are relative to the * root of the virtual mount and not the system root (/). * */ /* The file descriptor object. One per open file/directory. */ struct glfs_fd; typedef struct glfs_fd glfs_fd_t; /* * Mask for request/result items in the struct glfs_stat. * * Query request/result mask for glfs_stat() (family of functions) and * struct glfs_stat::glfs_st_mask. * * These bits should be set in the mask argument of glfs_stat() (family of * functions) to request particular items when calling glfs_stat(). * * NOTE: Lower order 32 bits are used to reflect statx(2) bits. For Gluster * specific attrs/extensions, use higher order 32 bits. * */ #define GLFS_STAT_TYPE 0x0000000000000001U /* Want/got stx_mode & S_IFMT */ #define GLFS_STAT_MODE 0x0000000000000002U /* Want/got stx_mode & ~S_IFMT */ #define GLFS_STAT_NLINK 0x0000000000000004U /* Want/got stx_nlink */ #define GLFS_STAT_UID 0x0000000000000008U /* Want/got stx_uid */ #define GLFS_STAT_GID 0x0000000000000010U /* Want/got stx_gid */ #define GLFS_STAT_ATIME 0x0000000000000020U /* Want/got stx_atime */ #define GLFS_STAT_MTIME 0x0000000000000040U /* Want/got stx_mtime */ #define GLFS_STAT_CTIME 0x0000000000000080U /* Want/got stx_ctime */ #define GLFS_STAT_INO 0x0000000000000100U /* Want/got stx_ino */ #define GLFS_STAT_SIZE 0x0000000000000200U /* Want/got stx_size */ #define GLFS_STAT_BLOCKS 0x0000000000000400U /* Want/got stx_blocks */ #define GLFS_STAT_BASIC_STATS \ 0x00000000000007ffU /* Items in the normal stat struct */ #define GLFS_STAT_BTIME 0x0000000000000800U /* Want/got stx_btime */ #define GLFS_STAT_ALL 0x0000000000000fffU /* All currently supported flags */ #define GLFS_STAT_RESERVED \ 0x8000000000000000U /* Reserved to denote future expansion */ /* Macros for checking validity of struct glfs_stat members.*/ #define GLFS_STAT_TYPE_VALID(stmask) (stmask & GLFS_STAT_TYPE) #define GLFS_STAT_MODE_VALID(stmask) (stmask & GLFS_STAT_MODE) #define GLFS_STAT_NLINK_VALID(stmask) (stmask & GLFS_STAT_NLINK) #define GLFS_STAT_UID_VALID(stmask) (stmask & GLFS_STAT_UID) #define GLFS_STAT_GID_VALID(stmask) (stmask & GLFS_STAT_GID) #define GLFS_STAT_ATIME_VALID(stmask) (stmask & GLFS_STAT_ATIME) #define GLFS_STAT_MTIME_VALID(stmask) (stmask & GLFS_STAT_MTIME) #define GLFS_STAT_CTIME_VALID(stmask) (stmask & GLFS_STAT_CTIME) #define GLFS_STAT_INO_VALID(stmask) (stmask & GLFS_STAT_INO) #define GLFS_STAT_SIZE_VALID(stmask) (stmask & GLFS_STAT_SIZE) #define GLFS_STAT_BLOCKS_VALID(stmask) (stmask & GLFS_STAT_BLOCKS) #define GLFS_STAT_BTIME_VALID(stmask) (stmask & GLFS_STAT_BTIME) #define GLFS_STAT_GFID_VALID(stmask) (stmask & GLFS_STAT_GFID) /* * Attributes to be found in glfs_st_attributes and masked in * glfs_st_attributes_mask. * * These give information about the features or the state of a file that might * be of use to programs. * * NOTE: Lower order 32 bits are used to reflect statx(2) attribute bits. For * Gluster specific attrs, use higher order 32 bits. * * NOTE: We do not support any file attributes or state as yet! */ #define GLFS_STAT_ATTR_RESERVED \ 0x8000000000000000U /* Reserved to denote future expansion */ /* Extended file attribute structure. * * The caller passes a mask of what they're specifically interested in as a * parameter to glfs_stat(). What glfs_stat() actually got will be indicated * in glfs_st_mask upon return. * * For each bit in the mask argument: * * - if the datum is not supported: * * - the bit will be cleared, and * * - the datum value is undefined * * - otherwise, if explicitly requested: * * - the field will be filled in and the bit will be set; * * - otherwise, if not requested, but available in, it will be filled in * anyway, and the bit will be set upon return; * * - otherwise the field and the bit will be cleared before returning. * */ struct glfs_stat { uint64_t glfs_st_mask; /* What results were written [uncond] */ uint64_t glfs_st_attributes; /* Flags conveying information about the file [uncond] */ uint64_t glfs_st_attributes_mask; /* Mask to show what's supported in st_attributes [ucond] */ struct timespec glfs_st_atime; /* Last access time */ struct timespec glfs_st_btime; /* File creation time */ struct timespec glfs_st_ctime; /* Last attribute change time */ struct timespec glfs_st_mtime; /* Last data modification time */ ino_t glfs_st_ino; /* Inode number */ off_t glfs_st_size; /* File size */ blkcnt_t glfs_st_blocks; /* Number of 512-byte blocks allocated */ uint32_t glfs_st_rdev_major; /* Device ID of special file [if bdev/cdev] */ uint32_t glfs_st_rdev_minor; uint32_t glfs_st_dev_major; /* ID of device containing file [uncond] */ uint32_t glfs_st_dev_minor; blksize_t glfs_st_blksize; /* Preferred general I/O size [uncond] */ nlink_t glfs_st_nlink; /* Number of hard links */ uid_t glfs_st_uid; /* User ID of owner */ gid_t glfs_st_gid; /* Group ID of owner */ mode_t glfs_st_mode; /* File mode */ }; #define GLFS_LEASE_ID_SIZE 16 /* 128bits */ typedef char glfs_leaseid_t[GLFS_LEASE_ID_SIZE]; /* * PER THREAD IDENTITY MODIFIERS * * The following operations enable to set a per thread identity context * for the glfs APIs to perform operations as. The calls here are kept as close * to POSIX equivalents as possible. * * NOTES: * * - setgroups is a per thread setting, hence this is named as fsgroups to be * close in naming to the fs(u/g)id APIs * - Typical mode of operation is to set the IDs as required, with the * supplementary groups being optionally set, make the glfs call and post the * glfs operation set them back to eu/gid or uid/gid as appropriate to the * caller * - The groups once set, need to be unset by setting the size to 0 (in which * case the list argument is a do not care) * - In case of leases feature enables, setfsleaseid is used to set and reset * leaseid before and after every I/O operation. * - Once a process for a thread of operation choses to set the IDs, all glfs * calls made from that thread would default to the IDs set for the thread. * As a result use these APIs with care and ensure that the set IDs are * reverted to global process defaults as required. * */ int glfs_setfsuid(uid_t fsuid) __THROW GFAPI_PUBLIC(glfs_setfsuid, 3.4.2); int glfs_setfsgid(gid_t fsgid) __THROW GFAPI_PUBLIC(glfs_setfsgid, 3.4.2); int glfs_setfsgroups(size_t size, const gid_t *list) __THROW GFAPI_PUBLIC(glfs_setfsgroups, 3.4.2); int glfs_setfsleaseid(glfs_leaseid_t leaseid) __THROW GFAPI_PUBLIC(glfs_setfsleaseid, 4.0.0); /* SYNOPSIS glfs_open: Open a file. DESCRIPTION This function opens a file on a virtual mount. PARAMETERS @fs: The 'virtual mount' object to be initialized. @path: Path of the file within the virtual mount. @flags: Open flags. See open(2). O_CREAT is not supported. Use glfs_creat() for creating files. RETURN VALUES NULL : Failure. @errno will be set with the type of failure. Others : Pointer to the opened glfs_fd_t. */ glfs_fd_t * glfs_open(glfs_t *fs, const char *path, int flags) __THROW GFAPI_PUBLIC(glfs_open, 3.4.0); /* SYNOPSIS glfs_openat: Open a file, with reference to a fd (man 2 openat). DESCRIPTION This function opens a file on a virtual mount. PARAMETERS @glfd: The 'gluster fd' object (should belong to the directory). @path: Path of the file with reference to the fd (of the directory) @flags: Open flags. See openat(2). @mode: Permission of the file to be created. RETURN VALUES NULL : Failure. @errno will be set with the type of failure. Others : Pointer to the opened glfs_fd_t. */ glfs_fd_t * glfs_openat(struct glfs_fd *glfd, const char *path, int flags, mode_t mode) __THROW GFAPI_PUBLIC(glfs_openat, 11.0); /* SYNOPSIS glfs_creat: Create a file. DESCRIPTION This function opens a file on a virtual mount. PARAMETERS @fs: The 'virtual mount' object to be initialized. @path: Path of the file within the virtual mount. @mode: Permission of the file to be created. @flags: Create flags. See open(2). O_EXCL is supported. RETURN VALUES NULL : Failure. @errno will be set with the type of failure. Others : Pointer to the opened glfs_fd_t. */ glfs_fd_t * glfs_creat(glfs_t *fs, const char *path, int flags, mode_t mode) __THROW GFAPI_PUBLIC(glfs_creat, 3.4.0); int glfs_close(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_close, 3.4.0); glfs_t * glfs_from_glfd(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_from_glfd, 3.4.0); int glfs_set_xlator_option(glfs_t *fs, const char *xlator, const char *key, const char *value) __THROW GFAPI_PUBLIC(glfs_set_xlator_option, 3.4.0); /* glfs_io_cbk The following is the function type definition of the callback function pointer which has to be provided by the caller to the *_async() versions of the IO calls. The callback function is called on completion of the requested IO, and the appropriate return value is returned in @ret. In case of an error in completing the IO, @ret will be -1 and @errno will be set with the appropriate error. @ret will be same as the return value of the non _async() variant of the particular call @data is the same context pointer provided by the caller at the time of issuing the async IO call. This can be used by the caller to differentiate different instances of the async requests in a common callback function. @prestat and @poststat are allocated on the stack, that are auto destroyed post the callback function returns. */ typedef void (*glfs_io_cbk)(glfs_fd_t *fd, ssize_t ret, struct glfs_stat *prestat, struct glfs_stat *poststat, void *data); // glfs_{read,write}[_async] ssize_t glfs_read(glfs_fd_t *fd, void *buf, size_t count, int flags) __THROW GFAPI_PUBLIC(glfs_read, 3.4.0); ssize_t glfs_write(glfs_fd_t *fd, const void *buf, size_t count, int flags) __THROW GFAPI_PUBLIC(glfs_write, 3.4.0); int glfs_read_async(glfs_fd_t *fd, void *buf, size_t count, int flags, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_read_async, 6.0); int glfs_write_async(glfs_fd_t *fd, const void *buf, size_t count, int flags, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_write_async, 6.0); // glfs_{read,write}v[_async] ssize_t glfs_readv(glfs_fd_t *fd, const struct iovec *iov, int iovcnt, int flags) __THROW GFAPI_PUBLIC(glfs_readv, 3.4.0); ssize_t glfs_writev(glfs_fd_t *fd, const struct iovec *iov, int iovcnt, int flags) __THROW GFAPI_PUBLIC(glfs_writev, 3.4.0); int glfs_readv_async(glfs_fd_t *fd, const struct iovec *iov, int count, int flags, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_readv_async, 6.0); int glfs_writev_async(glfs_fd_t *fd, const struct iovec *iov, int count, int flags, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_writev_async, 6.0); // glfs_p{read,write}[_async] ssize_t glfs_pread(glfs_fd_t *fd, void *buf, size_t count, off_t offset, int flags, struct glfs_stat *poststat) __THROW GFAPI_PUBLIC(glfs_pread, 6.0); ssize_t glfs_pwrite(glfs_fd_t *fd, const void *buf, size_t count, off_t offset, int flags, struct glfs_stat *prestat, struct glfs_stat *poststat) __THROW GFAPI_PUBLIC(glfs_pwrite, 6.0); int glfs_pread_async(glfs_fd_t *fd, void *buf, size_t count, off_t offset, int flags, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_pread_async, 6.0); int glfs_pwrite_async(glfs_fd_t *fd, const void *buf, int count, off_t offset, int flags, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_pwrite_async, 6.0); // glfs_p{read,write}v[_async] ssize_t glfs_preadv(glfs_fd_t *fd, const struct iovec *iov, int iovcnt, off_t offset, int flags) __THROW GFAPI_PUBLIC(glfs_preadv, 3.4.0); ssize_t glfs_pwritev(glfs_fd_t *fd, const struct iovec *iov, int iovcnt, off_t offset, int flags) __THROW GFAPI_PUBLIC(glfs_pwritev, 3.4.0); int glfs_preadv_async(glfs_fd_t *fd, const struct iovec *iov, int count, off_t offset, int flags, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_preadv_async, 6.0); int glfs_pwritev_async(glfs_fd_t *fd, const struct iovec *iov, int count, off_t offset, int flags, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_pwritev_async, 6.0); off_t glfs_lseek(glfs_fd_t *fd, off_t offset, int whence) __THROW GFAPI_PUBLIC(glfs_lseek, 3.4.0); ssize_t glfs_copy_file_range(struct glfs_fd *glfd_in, off64_t *off_in, struct glfs_fd *glfd_out, off64_t *off_out, size_t len, unsigned int flags, struct glfs_stat *statbuf, struct glfs_stat *prestat, struct glfs_stat *poststat) __THROW GFAPI_PUBLIC(glfs_copy_file_range, 6.0); int glfs_truncate(glfs_t *fs, const char *path, off_t length) __THROW GFAPI_PUBLIC(glfs_truncate, 3.7.15); int glfs_ftruncate(glfs_fd_t *fd, off_t length, struct glfs_stat *prestat, struct glfs_stat *poststat) __THROW GFAPI_PUBLIC(glfs_ftruncate, 6.0); int glfs_ftruncate_async(glfs_fd_t *fd, off_t length, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_ftruncate_async, 6.0); int glfs_lstat(glfs_t *fs, const char *path, struct stat *buf) __THROW GFAPI_PUBLIC(glfs_lstat, 3.4.0); int glfs_stat(glfs_t *fs, const char *path, struct stat *buf) __THROW GFAPI_PUBLIC(glfs_stat, 3.4.0); int glfs_fstatat(struct glfs_fd *glfd, const char *path, struct stat *buf, int flags) __THROW GFAPI_PUBLIC(glfs_fstatat, 11.0); int glfs_fstat(glfs_fd_t *fd, struct stat *buf) __THROW GFAPI_PUBLIC(glfs_fstat, 3.4.0); int glfs_fsync(glfs_fd_t *fd, struct glfs_stat *prestat, struct glfs_stat *poststat) __THROW GFAPI_PUBLIC(glfs_fsync, 6.0); int glfs_fsync_async(glfs_fd_t *fd, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_fsync_async, 6.0); int glfs_fdatasync(glfs_fd_t *fd, struct glfs_stat *prestat, struct glfs_stat *poststat) __THROW GFAPI_PUBLIC(glfs_fdatasync, 6.0); int glfs_fdatasync_async(glfs_fd_t *fd, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_fdatasync_async, 6.0); int glfs_access(glfs_t *fs, const char *path, int mode) __THROW GFAPI_PUBLIC(glfs_access, 3.4.0); int glfs_faccessat(struct glfs_fd *glfd, const char *path, int mode, int flags) __THROW GFAPI_PUBLIC(glfs_faccessat, 11.0); int glfs_symlink(glfs_t *fs, const char *oldpath, const char *newpath) __THROW GFAPI_PUBLIC(glfs_symlink, 3.4.0); int glfs_symlinkat(const char *oldpath, struct glfs_fd *glfd, const char *newpath) __THROW GFAPI_PUBLIC(glfs_symlinkat, 11.0); int glfs_readlink(glfs_t *fs, const char *path, char *buf, size_t bufsiz) __THROW GFAPI_PUBLIC(glfs_readlink, 3.4.0); int glfs_readlinkat(struct glfs_fd *glfd, const char *path, char *buf, size_t bufsiz) __THROW GFAPI_PUBLIC(glfs_readlinkat, 11.0); int glfs_mknod(glfs_t *fs, const char *path, mode_t mode, dev_t dev) __THROW GFAPI_PUBLIC(glfs_mknod, 3.4.0); int glfs_mknodat(struct glfs_fd *glfd, const char *path, mode_t mode, dev_t dev) __THROW GFAPI_PUBLIC(glfs_mknod, 11.0); int glfs_mkdir(glfs_t *fs, const char *path, mode_t mode) __THROW GFAPI_PUBLIC(glfs_mkdir, 3.4.0); int glfs_mkdirat(struct glfs_fd *glfd, const char *path, mode_t mode) __THROW GFAPI_PUBLIC(glfs_mkdirat, 11.0); int glfs_unlink(glfs_t *fs, const char *path) __THROW GFAPI_PUBLIC(glfs_unlink, 3.4.0); int glfs_unlinkat(struct glfs_fd *glfd, const char *path, int flags) __THROW GFAPI_PUBLIC(glfs_unlinkat, 11.0); int glfs_rmdir(glfs_t *fs, const char *path) __THROW GFAPI_PUBLIC(glfs_rmdir, 3.4.0); int glfs_rename(glfs_t *fs, const char *oldpath, const char *newpath) __THROW GFAPI_PUBLIC(glfs_rename, 3.4.0); int glfs_renameat(struct glfs_fd *oldglfd, const char *oldpath, struct glfs_fd *newglfd, const char *newpath) __THROW GFAPI_PUBLIC(glfs_renameat, 11.0); int glfs_renameat2(struct glfs_fd *oldglfd, const char *oldpath, struct glfs_fd *newglfd, const char *newpath, int flags) __THROW GFAPI_PUBLIC(glfs_renameat2, 11.0); int glfs_link(glfs_t *fs, const char *oldpath, const char *newpath) __THROW GFAPI_PUBLIC(glfs_link, 3.4.0); int glfs_linkat(struct glfs_fd *oldglfd, const char *oldpath, struct glfs_fd *newglfd, const char *newpath, int flags) __THROW GFAPI_PUBLIC(glfs_linkat, 11.0); glfs_fd_t * glfs_opendir(glfs_t *fs, const char *path) __THROW GFAPI_PUBLIC(glfs_opendir, 3.4.0); /* * @glfs_readdir_r and @glfs_readdirplus_r ARE thread safe AND re-entrant, * but the interface has ambiguity about the size of @dirent to be allocated * before calling the APIs. 512 byte buffer (for @dirent) is sufficient for * all known systems which are tested againt glusterfs/gfapi, but may be * insufficient in the future. */ int glfs_readdir_r(glfs_fd_t *fd, struct dirent *dirent, struct dirent **result) __THROW GFAPI_PUBLIC(glfs_readdir_r, 3.4.0); int glfs_readdirplus_r(glfs_fd_t *fd, struct stat *stat, struct dirent *dirent, struct dirent **result) __THROW GFAPI_PUBLIC(glfs_readdirplus_r, 3.4.0); /* * @glfs_readdir and @glfs_readdirplus are NEITHER thread safe NOR re-entrant * when called on the same directory handle. However they ARE thread safe * AND re-entrant when called on different directory handles (which may be * referring to the same directory too.) */ struct dirent * glfs_readdir(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_readdir, 3.5.0); struct dirent * glfs_readdirplus(glfs_fd_t *fd, struct stat *stat) __THROW GFAPI_PUBLIC(glfs_readdirplus, 3.5.0); long glfs_telldir(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_telldir, 3.4.0); void glfs_seekdir(glfs_fd_t *fd, long offset) __THROW GFAPI_PUBLIC(glfs_seekdir, 3.4.0); int glfs_closedir(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_closedir, 3.4.0); int glfs_statvfs(glfs_t *fs, const char *path, struct statvfs *buf) __THROW GFAPI_PUBLIC(glfs_statvfs, 3.4.0); int glfs_chmod(glfs_t *fs, const char *path, mode_t mode) __THROW GFAPI_PUBLIC(glfs_chmod, 3.4.0); int glfs_fchmod(glfs_fd_t *fd, mode_t mode) __THROW GFAPI_PUBLIC(glfs_fchmod, 3.4.0); int glfs_fchmodat(struct glfs_fd *glfd, const char *path, mode_t mode, int flags) __THROW GFAPI_PUBLIC(glfs_fchmodat, 11.0); int glfs_chown(glfs_t *fs, const char *path, uid_t uid, gid_t gid) __THROW GFAPI_PUBLIC(glfs_chown, 3.4.0); int glfs_lchown(glfs_t *fs, const char *path, uid_t uid, gid_t gid) __THROW GFAPI_PUBLIC(glfs_lchown, 3.4.0); int glfs_fchown(glfs_fd_t *fd, uid_t uid, gid_t gid) __THROW GFAPI_PUBLIC(glfs_fchown, 3.4.0); int glfs_fchownat(struct glfs_fd *glfd, const char *path, uid_t uid, gid_t gid, int flags) __THROW GFAPI_PUBLIC(glfs_fchownat, 11.0); int glfs_utimens(glfs_t *fs, const char *path, const struct timespec times[2]) __THROW GFAPI_PUBLIC(glfs_utimens, 3.4.0); int glfs_lutimens(glfs_t *fs, const char *path, const struct timespec times[2]) __THROW GFAPI_PUBLIC(glfs_lutimens, 3.4.0); int glfs_futimens(glfs_fd_t *fd, const struct timespec times[2]) __THROW GFAPI_PUBLIC(glfs_futimens, 3.4.0); ssize_t glfs_getxattr(glfs_t *fs, const char *path, const char *name, void *value, size_t size) __THROW GFAPI_PUBLIC(glfs_getxattr, 3.4.0); ssize_t glfs_lgetxattr(glfs_t *fs, const char *path, const char *name, void *value, size_t size) __THROW GFAPI_PUBLIC(glfs_lgetxattr, 3.4.0); ssize_t glfs_fgetxattr(glfs_fd_t *fd, const char *name, void *value, size_t size) __THROW GFAPI_PUBLIC(glfs_fgetxattr, 3.4.0); ssize_t glfs_listxattr(glfs_t *fs, const char *path, void *value, size_t size) __THROW GFAPI_PUBLIC(glfs_listxattr, 3.4.0); ssize_t glfs_llistxattr(glfs_t *fs, const char *path, void *value, size_t size) __THROW GFAPI_PUBLIC(glfs_llistxattr, 3.4.0); ssize_t glfs_flistxattr(glfs_fd_t *fd, void *value, size_t size) __THROW GFAPI_PUBLIC(glfs_flistxattr, 3.4.0); int glfs_setxattr(glfs_t *fs, const char *path, const char *name, const void *value, size_t size, int flags) __THROW GFAPI_PUBLIC(glfs_setxattr, 3.4.0); int glfs_lsetxattr(glfs_t *fs, const char *path, const char *name, const void *value, size_t size, int flags) __THROW GFAPI_PUBLIC(glfs_lsetxattr, 3.4.0); int glfs_fsetxattr(glfs_fd_t *fd, const char *name, const void *value, size_t size, int flags) __THROW GFAPI_PUBLIC(glfs_fsetxattr, 3.4.0); int glfs_removexattr(glfs_t *fs, const char *path, const char *name) __THROW GFAPI_PUBLIC(glfs_removexattr, 3.4.0); int glfs_lremovexattr(glfs_t *fs, const char *path, const char *name) __THROW GFAPI_PUBLIC(glfs_lremovexattr, 3.4.0); int glfs_fremovexattr(glfs_fd_t *fd, const char *name) __THROW GFAPI_PUBLIC(glfs_fremovexattr, 3.4.0); int glfs_fallocate(glfs_fd_t *fd, int keep_size, off_t offset, size_t len) __THROW GFAPI_PUBLIC(glfs_fallocate, 3.5.0); int glfs_discard(glfs_fd_t *fd, off_t offset, size_t len) __THROW GFAPI_PUBLIC(glfs_discard, 3.5.0); int glfs_discard_async(glfs_fd_t *fd, off_t length, size_t lent, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_discard_async, 6.0); int glfs_zerofill(glfs_fd_t *fd, off_t offset, off_t len) __THROW GFAPI_PUBLIC(glfs_zerofill, 3.5.0); int glfs_zerofill_async(glfs_fd_t *fd, off_t length, off_t len, glfs_io_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_zerofill_async, 6.0); char * glfs_getcwd(glfs_t *fs, char *buf, size_t size) __THROW GFAPI_PUBLIC(glfs_getcwd, 3.4.0); int glfs_chdir(glfs_t *fs, const char *path) __THROW GFAPI_PUBLIC(glfs_chdir, 3.4.0); int glfs_fchdir(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_fchdir, 3.4.0); char * glfs_realpath(glfs_t *fs, const char *path, char *resolved_path) __THROW GFAPI_PUBLIC(glfs_realpath, 3.7.17); /* * @cmd and @flock are as specified in man fcntl(2). */ int glfs_posix_lock(glfs_fd_t *fd, int cmd, struct flock *flock) __THROW GFAPI_PUBLIC(glfs_posix_lock, 3.4.0); /* SYNOPSIS glfs_file_lock: Request extended byte range lock on a file DESCRIPTION This function is capable of requesting either advisory or mandatory type byte range locks on a file. Note: To set a unique owner key for locks based on a particular file descriptor, make use of glfs_fd_set_lkowner() api to do so before requesting lock via this api. This owner key will be further consumed by other incoming data modifying file operations via the same file descriptor. PARAMETERS @fd: File descriptor @cmd: As specified in man fcntl(2). @flock: As specified in man fcntl(2). @lk_mode: Required lock type from options available with the enum glfs_lock_mode_t defined below. RETURN VALUES 0 : Success. Lock has been granted. -1 : Failure. @errno will be set indicating the type of failure. */ /* Lock modes used by glfs_file_lock() */ enum glfs_lock_mode { GLFS_LK_ADVISORY = 0, GLFS_LK_MANDATORY }; typedef enum glfs_lock_mode glfs_lock_mode_t; int glfs_file_lock(glfs_fd_t *fd, int cmd, struct flock *flock, glfs_lock_mode_t lk_mode) __THROW GFAPI_PUBLIC(glfs_file_lock, 3.13.0); glfs_fd_t * glfs_dup(glfs_fd_t *fd) __THROW GFAPI_PUBLIC(glfs_dup, 3.4.0); void glfs_free(void *ptr) __THROW GFAPI_PUBLIC(glfs_free, 3.7.16); /* * glfs_sysrq: send a system request to the @fs instance * * Different commands for @sysrq are possible, the defines for these are listed * below the function definition. * * This function always returns success if the @sysrq is recognized. The return * value does not way anythin about the result of the @sysrq execution. Not all * @sysrq command will be able to return a success/failure status. */ int glfs_sysrq(glfs_t *fs, char sysrq) __THROW GFAPI_PUBLIC(glfs_sysrq, 3.10.0); #define GLFS_SYSRQ_HELP 'h' /* log a message with supported sysrq commands */ #define GLFS_SYSRQ_STATEDUMP 's' /* create a statedump */ /* * Structure returned as part of xreaddirplus */ struct glfs_xreaddirp_stat; typedef struct glfs_xreaddirp_stat glfs_xreaddirp_stat_t; /* Request flags to be used in XREADDIRP operation */ #define GFAPI_XREADDIRP_NULL \ 0x00000000 /* by default, no stat will be fetched */ #define GFAPI_XREADDIRP_STAT 0x00000001 /* Get stat */ #define GFAPI_XREADDIRP_HANDLE 0x00000002 /* Get object handle */ /* * This stat structure returned gets freed as part of glfs_free(xstat) */ struct stat * glfs_xreaddirplus_get_stat(glfs_xreaddirp_stat_t *xstat) __THROW GFAPI_PUBLIC(glfs_xreaddirplus_get_stat, 3.11.0); /* * SYNOPSIS * * glfs_xreaddirplus_r: Extended Readirplus operation * * DESCRIPTION * * This API does readdirplus operation, but along with stat it can fetch other * extra information like object handles etc for each of the dirents returned * based on requested flags. On success it returns the set of flags successfully * processed. * * Note that there are chances that some of the requested information may not be * available or returned (for example if reached EOD). Ensure to validate the * returned value to determine what flags have been successfully processed * & set. * * PARAMETERS * * INPUT: * @glfd: GFAPI file descriptor of the directory * @flags: Flags determining xreaddirp_stat requested * Current available values are: * GFAPI_XREADDIRP_NULL * GFAPI_XREADDIRP_STAT * GFAPI_XREADDIRP_HANDLE * @ext: Dirent struture to copy the values to * (though optional recommended to be allocated by application * esp., in multi-threaded environment) * * OUTPUT: * @res: to store the next dirent value. If NULL and return value is '0', * it means it reached end of the directory. * @xstat_p: Pointer to contain all the requested data returned * for that dirent. Application should make use of glfs_free() API * to free this pointer and the variables returned by * glfs_xreaddirplus_get_*() APIs. * * RETURN VALUE: * >=0: SUCCESS (value contains the flags successfully processed) * -1: FAILURE */ int glfs_xreaddirplus_r(glfs_fd_t *glfd, uint32_t flags, glfs_xreaddirp_stat_t **xstat_p, struct dirent *ext, struct dirent **res) __THROW GFAPI_PUBLIC(glfs_xreaddirplus_r, 3.11.0); #define GFAPI_MAX_LOCK_OWNER_LEN 255 /* * * DESCRIPTION * * This API allows application to set lk_owner on a fd. * A glfd can be associated with only single lk_owner. In case if there * is need to set another lk_owner, applications can make use of * 'glfs_dup' to get duplicate glfd and set new lk_owner on that second * glfd. * * Also its not recommended to override or clear lk_owner value as the * same shall be used to flush any outstanding locks while closing the fd. * * PARAMETERS * * INPUT: * @glfd: GFAPI file descriptor * @len: Size of lk_owner buffer. Max value can be GFAPI_MAX_LOCK_OWNER_LEN * @data: lk_owner data buffer. * * OUTPUT: * 0: SUCCESS * -1: FAILURE */ int glfs_fd_set_lkowner(glfs_fd_t *glfd, void *data, int len) __THROW GFAPI_PUBLIC(glfs_fd_set_lkowner, 3.10.7); /* * Applications (currently NFS-Ganesha) can make use of this * structure to read upcall notifications sent by server either * by polling or registering a callback function. * * On success, applications need to check for 'reason' to decide * if any upcall event is received. * * Currently supported upcall_events - * GLFS_UPCALL_INODE_INVALIDATE - * 'event_arg' - glfs_upcall_inode * * After processing the event, applications need to free 'event_arg' with * glfs_free(). * * Also similar to I/Os, the application should ideally stop polling * or unregister upcall_cbk function before calling glfs_fini(..). * Hence making an assumption that 'fs' & ctx structures cannot be * freed while in this routine. */ struct glfs_upcall; typedef struct glfs_upcall glfs_upcall_t; glfs_t * glfs_upcall_get_fs(glfs_upcall_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_get_fs, 3.7.16); enum glfs_upcall_reason { GLFS_UPCALL_EVENT_NULL = 0, GLFS_UPCALL_INODE_INVALIDATE, /* invalidate cache entry */ GLFS_UPCALL_RECALL_LEASE, /* recall lease */ }; typedef enum glfs_upcall_reason glfs_upcall_reason_t; glfs_upcall_reason_t glfs_upcall_get_reason(glfs_upcall_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_get_reason, 3.7.16); /* * Applications first need to make use of above API i.e, * "glfs_upcall_get_reason" to determine which upcall event it has * received. Post that below API - "glfs_upcall_get_event" should * be used to get corresponding upcall event object. * * Below are the upcall_reason and corresponding upcall_event objects: * ========================================================== * glfs_upcall_reason - event_object * ========================================================== * GLFS_UPCALL_EVENT_NULL - NULL * GLFS_UPCALL_INODE_INVALIDATE - struct glfs_upcall_inode * GLFS_UPCALL_RECALL_LEASE - struct glfs_upcall_lease * * After processing upcall event, glfs_free() should be called on the * glfs_upcall. */ void * glfs_upcall_get_event(glfs_upcall_t *arg) __THROW GFAPI_PUBLIC(glfs_upcall_get_event, 3.7.16); /* * SYNOPSIS * * glfs_upcall_cbk: Upcall callback definition * * This is function type definition of the callback function pointer * which has to be provided by the caller while registering for any * upcall events. * * This function is called whenever any upcall which the application * has registered for is received from the server. * * @up_arg: Upcall structure whose contents need to be interpreted by * making use of glfs_upcall_* helper routines. * * @data: The same context pointer provided by the caller at the time of * registering of upcall events. This may be used by the caller for any * of its internal use while processing upcalls. */ typedef void (*glfs_upcall_cbk)(glfs_upcall_t *up_arg, void *data); /* * List of upcall events supported by gluster/gfapi */ #define GLFS_EVENT_INODE_INVALIDATE 0x00000001 /* invalidate cache entry */ #define GLFS_EVENT_RECALL_LEASE 0x00000002 /* Recall lease */ #define GLFS_EVENT_ANY 0xffffffff /* for all the above events */ /* * SYNOPSIS * * glfs_upcall_register: Register for upcall events * * DESCRIPTION * * This function is used to register for various upcall events application * is interested in and the callback function to be invoked when such * events are triggered. * * Multiple calls of this routine shall override cbk function. That means * only one cbk function can be used for all the upcall events registered * and that shall be the one last updated. * * PARAMETERS: * * INPUT: * @fs: The 'virtual mount' object * * @event_list: List of upcall events to be registered. * Current available values are: * - GLFS_EVENT_INODE_INVALIDATE * - GLFS_EVENT_RECALL_LEASE * * @cbk: The cbk routine to be invoked in case of any upcall received * @data: Any opaque pointer provided by caller which shall be using while * making cbk calls. This pointer may be used by caller for any of its * internal use while processing upcalls. Can be NULL. * * RETURN VALUE: * >0: SUCCESS (value contains the events successfully registered) * -1: FAILURE */ int glfs_upcall_register(glfs_t *fs, uint32_t event_list, glfs_upcall_cbk cbk, void *data) __THROW GFAPI_PUBLIC(glfs_upcall_register, 3.13.0); /* * SYNOPSIS * * glfs_upcall_unregister: Unregister for upcall events * * DESCRIPTION * * This function is used to unregister the upcall events application * is not interested in. In case if the caller unregisters all the events * it has registered for, it shall no more receive any upcall event. * * PARAMETERS: * * INPUT: * @fs: The 'virtual mount' object * * @event_list: List of upcall events to be unregistered. * Current available values are: * - GLFS_EVENT_INODE_INVALIDATE * - GLFS_EVENT_RECALL_LEASE * RETURN VALUE: * >0: SUCCESS (value contains the events successfully unregistered) * -1: FAILURE */ int glfs_upcall_unregister(glfs_t *fs, uint32_t event_list) __THROW GFAPI_PUBLIC(glfs_upcall_unregister, 3.13.0); /* Lease Types */ enum glfs_lease_types { GLFS_LEASE_NONE = 0, GLFS_RD_LEASE = 1, GLFS_RW_LEASE = 2, }; typedef enum glfs_lease_types glfs_lease_types_t; /* Lease cmds */ enum glfs_lease_cmds { GLFS_GET_LEASE = 1, GLFS_SET_LEASE = 2, GLFS_UNLK_LEASE = 3, }; typedef enum glfs_lease_cmds glfs_lease_cmds_t; struct glfs_lease { glfs_lease_cmds_t cmd; glfs_lease_types_t lease_type; glfs_leaseid_t lease_id; unsigned int lease_flags; }; typedef struct glfs_lease glfs_lease_t; typedef void (*glfs_recall_cbk)(glfs_lease_t lease, void *data); /* SYNOPSIS glfs_lease: Takes a lease on a file. DESCRIPTION This function takes lease on an open file. PARAMETERS @glfd: The fd of the file on which lease should be taken, this fd is returned by glfs_open/glfs_create. @lease: Struct that defines the lease operation to be performed on the file. @lease.cmd - Can be one of the following values GF_GET_LEASE: Get the lease type currently present on the file, lease.lease_type will contain GF_RD_LEASE or GF_RW_LEASE or 0 if no leases. GF_SET_LEASE: Set the lease of given lease.lease_type on the file. GF_UNLK_LEASE: Unlock the lease present on the given fd. Note that the every lease request should have a corresponding unlk_lease. @lease.lease_type - Can be one of the following values GF_RD_LEASE: Read lease on a file, shared lease. GF_RW_LEASE: Read-Write lease on a file, exclusive lease. @lease.lease_id - A unique identification of lease, 128bits. @fn: This is the function that is invoked when the lease has to be recalled @data: It is a cookie, this pointer is returned as a part of recall fn and data field are stored as a part of glfs_fd, hence if there are multiple glfs_lease calls, each of them updates the fn and data fields. glfs_recall_cbk will be invoked with the last updated fn and data RETURN VALUES 0: Successful completion <0: Failure. @errno will be set with the type of failure */ int glfs_lease(glfs_fd_t *glfd, glfs_lease_t *lease, glfs_recall_cbk fn, void *data) __THROW GFAPI_PUBLIC(glfs_lease, 4.0.0); /* SYNOPSIS glfs_fsetattr: Function to set attributes. glfs_setattr: Function to set attributes DESCRIPTION The functions are used to set attributes on the file. PARAMETERS @glfs_fsetattr @glfd: The fd of the file for which the attributes are to be set, this fd is returned by glfs_open/glfs_create. @glfs_setattr @fs: File object. @path: The path of the file that is being operated on. @follow: Flag used to resolve symlink. @stat: Struct that has information about the file. @valid: This is the mask bit, that accepts GFAPI_SET_ATTR* masks. Refer glfs.h to see the mask definitions. Both functions are similar in functionality, just that the func setattr() uses file path whereas the func fsetattr() uses the fd. RETURN VALUES 0: Successful completion <0: Failure. @errno will be set with the type of failure */ int glfs_fsetattr(struct glfs_fd *glfd, struct glfs_stat *stat) __THROW GFAPI_PUBLIC(glfs_fsetattr, 6.0); int glfs_setattr(struct glfs *fs, const char *path, struct glfs_stat *stat, int follow) __THROW GFAPI_PUBLIC(glfs_setattr, 6.0); /* SYNOPSIS glfs_set_statedump_path: Function to set statedump path. DESCRIPTION This function is used to set statedump directory PARAMETERS @fs: The 'virtual mount' object to be configured with the volume specification file. @path: statedump path. Should be a directory. But the API won't fail if the directory doesn't exist yet, as one may create it later. RETURN VALUES 0 : Success. -1 : Failure. @errno will be set with the type of failure. */ int glfs_set_statedump_path(struct glfs *fs, const char *path) __THROW GFAPI_PUBLIC(glfs_set_statedump_path, 7.0); __END_DECLS #endif /* !_GLFS_H */ glusterfs-11.2/autogen.sh000077500000000000000000000041161503123251100154470ustar00rootroot00000000000000#!/bin/sh echo echo ... GlusterFS autogen ... echo ## Check all dependencies are present MISSING="" # Check for aclocal env aclocal --version > /dev/null 2>&1 if [ $? -eq 0 ]; then ACLOCAL=aclocal else MISSING="$MISSING aclocal" fi # Check for autoconf env autoconf --version > /dev/null 2>&1 if [ $? -eq 0 ]; then AUTOCONF=autoconf else MISSING="$MISSING autoconf" fi # Check for autoheader env autoheader --version > /dev/null 2>&1 if [ $? -eq 0 ]; then AUTOHEADER=autoheader else MISSING="$MISSING autoheader" fi # Check for automake env automake --version > /dev/null 2>&1 if [ $? -eq 0 ]; then AUTOMAKE=automake else MISSING="$MISSING automake" fi # Check for pkg-config, if it is missing # configure generates some weird error messages # that are not at all helpful. # See: https://tirania.org/blog/archive/2012/Oct-20.html env pkg-config --version > /dev/null 2>&1 if [ $? -ne 0 ]; then MISSING="$MISSING pkg-config" fi # Check for libtoolize or glibtoolize env libtoolize --version > /dev/null 2>&1 if [ $? -eq 0 ]; then # libtoolize was found, so use it TOOL=libtoolize else # libtoolize wasn't found, so check for glibtoolize env glibtoolize --version > /dev/null 2>&1 if [ $? -eq 0 ]; then TOOL=glibtoolize else MISSING="$MISSING libtoolize/glibtoolize" fi fi # Check for tar env tar -cf /dev/null /dev/null > /dev/null 2>&1 if [ $? -ne 0 ]; then MISSING="$MISSING tar" fi ## If dependencies are missing, warn the user and abort if [ "x$MISSING" != "x" ]; then echo "Aborting." echo echo "The following build tools are missing:" echo for pkg in $MISSING; do echo " * $pkg" done echo echo "Please install them and try again." echo exit 1 fi ## Do the autogeneration echo Running ${ACLOCAL}... $ACLOCAL -I ./contrib/aclocal echo Running ${AUTOHEADER}... $AUTOHEADER echo Running ${TOOL}... $TOOL --automake --copy --force echo Running ${AUTOCONF}... $AUTOCONF echo Running ${AUTOMAKE}... $AUTOMAKE --add-missing --force-missing --copy # Instruct user on next steps echo echo "Please proceed with configuring, compiling, and installing." glusterfs-11.2/build-aux/000077500000000000000000000000001503123251100153365ustar00rootroot00000000000000glusterfs-11.2/build-aux/checkpatch.pl000077500000000000000000004466011503123251100200060ustar00rootroot00000000000000#!/usr/bin/perl -w # (c) 2001, Dave Jones. (the file handling bit) # (c) 2005, Joel Schopp (the ugly bit) # (c) 2007,2008, Andy Whitcroft (new conditions, test suite) # (c) 2008-2010 Andy Whitcroft # (c) 2014 Gluster Community # Licensed under the terms of the GNU GPL License version 2 use strict; use POSIX; my $P = $0; $P =~ s@.*/@@g; my $V = '0.32.1'; use Getopt::Long qw(:config no_auto_abbrev); my $quiet = 0; my $tree = 1; my $chk_signoff = 1; my $chk_patch = 1; my $tst_only; my $emacs = 0; my $terse = 0; my $file = 0; my $check = 0; my $check_orig = 0; my $summary = 1; my $mailback = 0; my $summary_file = 0; my $show_types = 0; my $fix = 0; my $fix_inplace = 0; my $root; my %debug; my %camelcase = (); my %use_type = (); my @use = (); my %ignore_type = (); my @ignore = (); my $help = 0; my $configuration_file = ".checkpatch.conf"; my $max_line_length = 80; my $ignore_perl_version = 0; my $minimum_perl_version = 5.10.0; my $gerrit_url = $ENV{GERRIT_URL}; sub help { my ($exitcode) = @_; print << "EOM"; Usage: $P [OPTION]... [FILE]... Version: $V Options: -q, --quiet quiet --patch treat FILE as patchfile (default) --emacs emacs compile window format --gerrit-url=STRING URL the patch was reviewed at --terse one line per report -f, --file treat FILE as regular source file --subjective, --strict enable more subjective tests --types TYPE(,TYPE2...) show only these comma separated message types --ignore TYPE(,TYPE2...) ignore various comma separated message types --max-line-length=n set the maximum line length, if exceeded, warn --show-types show the message "types" in the output --root=PATH PATH to the glusterfs tree root --no-summary suppress the per-file summary --mailback only produce a report in case of warnings/errors --summary-file include the filename in summary --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of 'values', 'possible', 'type', and 'attr' (default is all off) --test-only=WORD report only warnings/errors containing WORD literally --fix EXPERIMENTAL - may create horrible results If correctable single-line errors exist, create ".EXPERIMENTAL-checkpatch-fixes" with potential errors corrected to the preferred checkpatch style --fix-inplace EXPERIMENTAL - may create horrible results Is the same as --fix, but overwrites the input file. It's your fault if there's no backup or git --ignore-perl-version override checking of perl version. expect runtime errors. -h, --help, --version display this help and exit When FILE is - read standard input. EOM exit($exitcode); } my $conf = which_conf($configuration_file); if (-f $conf) { my @conf_args; open(my $conffile, '<', "$conf") or warn "$P: Can't find a readable $configuration_file file $!\n"; while (<$conffile>) { my $line = $_; $line =~ s/\s*\n?$//g; $line =~ s/^\s*//g; $line =~ s/\s+/ /g; next if ($line =~ m/^\s*#/); next if ($line =~ m/^\s*$/); my @words = split(" ", $line); foreach my $word (@words) { last if ($word =~ m/^#/); push (@conf_args, $word); } } close($conffile); unshift(@ARGV, @conf_args) if @conf_args; } GetOptions( 'q|quiet+' => \$quiet, 'patch!' => \$chk_patch, 'emacs!' => \$emacs, 'gerrit-url=s' => \$gerrit_url, 'terse!' => \$terse, 'f|file!' => \$file, 'subjective!' => \$check, 'strict!' => \$check, 'ignore=s' => \@ignore, 'types=s' => \@use, 'show-types!' => \$show_types, 'max-line-length=i' => \$max_line_length, 'root=s' => \$root, 'summary!' => \$summary, 'mailback!' => \$mailback, 'summary-file!' => \$summary_file, 'fix!' => \$fix, 'fix-inplace!' => \$fix_inplace, 'ignore-perl-version!' => \$ignore_perl_version, 'debug=s' => \%debug, 'test-only=s' => \$tst_only, 'h|help' => \$help, 'version' => \$help ) or help(1); help(0) if ($help); $fix = 1 if ($fix_inplace); $check_orig = $check; my $exit = 0; if ($^V && $^V lt $minimum_perl_version) { printf "$P: requires at least perl version %vd\n", $minimum_perl_version; if (!$ignore_perl_version) { exit(1); } } if ($#ARGV < 0) { print "$P: no input files\n"; exit(1); } sub hash_save_array_words { my ($hashRef, $arrayRef) = @_; my @array = split(/,/, join(',', @$arrayRef)); foreach my $word (@array) { $word =~ s/\s*\n?$//g; $word =~ s/^\s*//g; $word =~ s/\s+/ /g; $word =~ tr/[a-z]/[A-Z]/; next if ($word =~ m/^\s*#/); next if ($word =~ m/^\s*$/); $hashRef->{$word}++; } } sub hash_show_words { my ($hashRef, $prefix) = @_; if ($quiet == 0 && keys %$hashRef) { print "NOTE: $prefix message types:"; foreach my $word (sort keys %$hashRef) { print " $word"; } print "\n\n"; } } hash_save_array_words(\%ignore_type, \@ignore); hash_save_array_words(\%use_type, \@use); my $dbg_values = 0; my $dbg_possible = 0; my $dbg_type = 0; my $dbg_attr = 0; for my $key (keys %debug) { ## no critic eval "\${dbg_$key} = '$debug{$key}';"; die "$@" if ($@); } my $rpt_cleaners = 0; if ($terse) { $emacs = 1; $quiet++; } if ($tree) { if (defined $root) { if (!top_of_glusterfs_tree($root)) { die "$P: $root: --root does not point at a valid tree\n"; } } else { if (top_of_glusterfs_tree('.')) { $root = '.'; } elsif ($0 =~ m@(.*)/extras/[^/]*$@ && top_of_glusterfs_tree($1)) { $root = $1; } } if (!defined $root) { print "Must be run from the top-level dir. of a GlusterFS tree\n"; exit(2); } } my $emitted_corrupt = 0; our $Ident = qr{ [A-Za-z_][A-Za-z\d_]* (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* }x; our $Storage = qr{extern|static|asmlinkage}; our $Sparse = qr{ __user| __kernel| __force| __iomem| __must_check| __init_refok| __kprobes| __ref| __rcu }x; our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; # Notes to $Attribute: # We need \b after 'init' otherwise 'initconst' will cause a false positive in a check our $Attribute = qr{ const| __percpu| __nocast| __safe| __bitwise__| __packed__| __packed2__| __naked| __maybe_unused| __always_unused| __noreturn| __used| __cold| __noclone| __deprecated| __read_mostly| __kprobes| $InitAttribute| ____cacheline_aligned| ____cacheline_aligned_in_smp| ____cacheline_internodealigned_in_smp| __weak }x; our $Modifier; our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; our $Binary = qr{(?i)0b[01]+$Int_type?}; our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; our $Int = qr{[0-9]+$Int_type?}; our $Octal = qr{0[0-7]+$Int_type?}; our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; our $Float = qr{$Float_hex|$Float_dec|$Float_int}; our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; our $Compare = qr{<=|>=|==|!=|<|(?}; our $Arithmetic = qr{\+|-|\*|\/|%}; our $Operators = qr{ <=|>=|==|!=| =>|->|<<|>>|<|>|!|~| &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic }x; our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; our $NonptrType; our $NonptrTypeWithAttr; our $Type; our $Declare; our $NON_ASCII_UTF8 = qr{ [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 }x; our $UTF8 = qr{ [\x09\x0A\x0D\x20-\x7E] # ASCII | $NON_ASCII_UTF8 }x; our $typeTypedefs = qr{(?x: (?:__)?(?:u|s|be|le)(?:8|16|32|64)| atomic_t )}; our $logFunctions = qr{(?x: printk(?:_ratelimited|_once|)| (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| WARN(?:_RATELIMIT|_ONCE|)| panic| MODULE_[A-Z_]+| seq_vprintf|seq_printf|seq_puts )}; our $signature_tags = qr{(?xi: Signed-off-by:| Acked-by:| Tested-by:| Reviewed-by:| Reviewed-on:| Reported-by:| Original-author:| Original-Author:| Original-Authors:| Suggested-by:| To:| Cc: )}; our $url_tags = qr{http:|https:}; our @typeList = ( qr{void}, qr{(?:unsigned\s+)?char}, qr{(?:unsigned\s+)?short}, qr{(?:unsigned\s+)?int}, qr{(?:unsigned\s+)?long}, qr{(?:unsigned\s+)?long\s+int}, qr{(?:unsigned\s+)?long\s+long}, qr{(?:unsigned\s+)?long\s+long\s+int}, qr{unsigned}, qr{float}, qr{double}, qr{bool}, qr{struct\s+$Ident}, qr{union\s+$Ident}, qr{enum\s+$Ident}, qr{${Ident}_t}, qr{${Ident}_handler}, qr{${Ident}_handler_fn}, ); our @typeListWithAttr = ( @typeList, qr{struct\s+$InitAttribute\s+$Ident}, qr{union\s+$InitAttribute\s+$Ident}, ); our @modifierList = ( qr{fastcall}, ); our @mode_permission_funcs = ( ["module_param", 3], ["module_param_(?:array|named|string)", 4], ["module_param_array_named", 5], ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], ["proc_create(?:_data|)", 2], ["(?:CLASS|DEVICE|SENSOR)_ATTR", 2], ); #Create a search pattern for all these functions to speed up a loop below our $mode_perms_search = ""; foreach my $entry (@mode_permission_funcs) { $mode_perms_search .= '|' if ($mode_perms_search ne ""); $mode_perms_search .= $entry->[0]; } our $declaration_macros = qr{(?x: (?:$Storage\s+)?(?:DECLARE|DEFINE)_[A-Z]+\s*\(| (?:$Storage\s+)?LIST_HEAD\s*\( )}; our $allowed_asm_includes = qr{(?x: irq| memory )}; # memory.h: ARM has a custom one sub build_types { my $mods = "(?x:\n" . join("|\n ", @modifierList) . "\n)"; my $all = "(?x:\n" . join("|\n ", @typeList) . "\n)"; my $allWithAttr = "(?x:\n" . join("|\n ", @typeListWithAttr) . "\n)"; $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; $NonptrType = qr{ (?:$Modifier\s+|const\s+)* (?: (?:typeof|__typeof__)\s*\([^\)]*\)| (?:$typeTypedefs\b)| (?:${all}\b) ) (?:\s+$Modifier|\s+const)* }x; $NonptrTypeWithAttr = qr{ (?:$Modifier\s+|const\s+)* (?: (?:typeof|__typeof__)\s*\([^\)]*\)| (?:$typeTypedefs\b)| (?:${allWithAttr}\b) ) (?:\s+$Modifier|\s+const)* }x; $Type = qr{ $NonptrType (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*|\[\])+|(?:\s*\[\s*\])+)? (?:\s+$Inline|\s+$Modifier)* }x; $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; } build_types(); our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; # Using $balanced_parens, $LvalOrFunc, or $FuncArg # requires at least perl version v5.10.0 # Any use must be runtime checked with $^V our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant)}; sub deparenthesize { my ($string) = @_; return "" if (!defined($string)); while ($string =~ /^\s*\(.*\)\s*$/) { $string =~ s@^\s*\(\s*@@; $string =~ s@\s*\)\s*$@@; } $string =~ s@\s+@ @g; return $string; } sub seed_camelcase_file { my ($file) = @_; return if (!(-f $file)); local $/; open(my $include_file, '<', "$file") or warn "$P: Can't read '$file' $!\n"; my $text = <$include_file>; close($include_file); my @lines = split('\n', $text); foreach my $line (@lines) { next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { $camelcase{$1} = 1; } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { $camelcase{$1} = 1; } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { $camelcase{$1} = 1; } } } my $camelcase_seeded = 0; sub seed_camelcase_includes { return if ($camelcase_seeded); my $files; my $camelcase_cache = ""; my @include_files = (); $camelcase_seeded = 1; if (-e ".git") { my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`; chomp $git_last_include_commit; $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; } else { my $last_mod_date = 0; $files = `find $root/include -name "*.h"`; @include_files = split('\n', $files); foreach my $file (@include_files) { my $date = POSIX::strftime("%Y%m%d%H%M", localtime((stat $file)[9])); $last_mod_date = $date if ($last_mod_date < $date); } $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; } if ($camelcase_cache ne "" && -f $camelcase_cache) { open(my $camelcase_file, '<', "$camelcase_cache") or warn "$P: Can't read '$camelcase_cache' $!\n"; while (<$camelcase_file>) { chomp; $camelcase{$_} = 1; } close($camelcase_file); return; } if (-e ".git") { $files = `git ls-files "include/*.h"`; @include_files = split('\n', $files); } foreach my $file (@include_files) { seed_camelcase_file($file); } if ($camelcase_cache ne "") { unlink glob ".checkpatch-camelcase.*"; open(my $camelcase_file, '>', "$camelcase_cache") or warn "$P: Can't write '$camelcase_cache' $!\n"; foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { print $camelcase_file ("$_\n"); } close($camelcase_file); } } $chk_signoff = 0 if ($file); my @rawlines = (); my @lines = (); my @fixed = (); my $vname; for my $filename (@ARGV) { my $FILE; if ($file) { open($FILE, '-|', "diff -u /dev/null $filename") || die "$P: $filename: diff failed - $!\n"; } elsif ($filename eq '-') { open($FILE, '<&STDIN'); } else { open($FILE, '<', "$filename") || die "$P: $filename: open failed - $!\n"; } if ($filename eq '-') { $vname = 'Your patch'; } else { $vname = $filename; } while (<$FILE>) { chomp; push(@rawlines, $_); } close($FILE); if (!process($filename)) { $exit = 1; } @rawlines = (); @lines = (); @fixed = (); } exit($exit); sub top_of_glusterfs_tree { my ($root) = @_; # Add here if the tree changes my @tree_check = ( "api", "AUTHORS", "autogen.sh", "build-aux", "ChangeLog", "cli", "configure.ac", "contrib", "CONTRIBUTING", "COPYING-GPLV2", "COPYING-LGPLV3", "doc", "extras", "geo-replication", "glusterfs-api.pc.in", "glusterfsd", "glusterfs.spec.in", "heal", "INSTALL", "libgfchangelog.pc.in", "libglusterfs", "MAINTAINERS", "Makefile.am", "NEWS", "README.md", "rfc.sh", "rpc", "run-tests.sh", "tests", "THANKS", "xlators", ); foreach my $check (@tree_check) { if (! -e $root . '/' . $check) { return 0; } } return 1; } sub parse_email { my ($formatted_email) = @_; my $name = ""; my $address = ""; my $comment = ""; if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) { $name = $1; $address = $2; $comment = $3 if defined $3; } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) { $address = $1; $comment = $2 if defined $2; } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { $address = $1; $comment = $2 if defined $2; $formatted_email =~ s/$address.*$//; $name = $formatted_email; $name = trim($name); $name =~ s/^\"|\"$//g; # If there's a name left after stripping spaces and # leading quotes, and the address doesn't have both # leading and trailing angle brackets, the address # is invalid. ie: # "joe smith joe@smith.com" bad # "joe smith ]+>$/) { $name = ""; $address = ""; $comment = ""; } } $name = trim($name); $name =~ s/^\"|\"$//g; $address = trim($address); $address =~ s/^\<|\>$//g; if ($name =~ /[^\w \-]/i) { ##has "must quote" chars $name =~ s/(?"; } return $formatted_email; } sub which_conf { my ($conf) = @_; foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { if (-e "$path/$conf") { return "$path/$conf"; } } return ""; } sub expand_tabs { my ($str) = @_; my $res = ''; my $n = 0; for my $c (split(//, $str)) { if ($c eq "\t") { $res .= ' '; $n++; for (; ($n % 8) != 0; $n++) { $res .= ' '; } next; } $res .= $c; $n++; } return $res; } sub copy_spacing { (my $res = shift) =~ tr/\t/ /c; return $res; } sub line_stats { my ($line) = @_; # Drop the diff line leader and expand tabs $line =~ s/^.//; $line = expand_tabs($line); # Pick the indent from the front of the line. my ($white) = ($line =~ /^(\s*)/); return (length($line), length($white)); } my $sanitise_quote = ''; sub sanitise_line_reset { my ($in_comment) = @_; if ($in_comment) { $sanitise_quote = '*/'; } else { $sanitise_quote = ''; } } sub sanitise_line { my ($line) = @_; my $res = ''; my $l = ''; my $qlen = 0; my $off = 0; my $c; # Always copy over the diff marker. $res = substr($line, 0, 1); for ($off = 1; $off < length($line); $off++) { $c = substr($line, $off, 1); # Comments we are wacking completly including the begin # and end, all to $;. if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { $sanitise_quote = '*/'; substr($res, $off, 2, "$;$;"); $off++; next; } if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { $sanitise_quote = ''; substr($res, $off, 2, "$;$;"); $off++; next; } if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { $sanitise_quote = '//'; substr($res, $off, 2, $sanitise_quote); $off++; next; } # A \ in a string means ignore the next character. if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && $c eq "\\") { substr($res, $off, 2, 'XX'); $off++; next; } # Regular quotes. if ($c eq "'" || $c eq '"') { if ($sanitise_quote eq '') { $sanitise_quote = $c; substr($res, $off, 1, $c); next; } elsif ($sanitise_quote eq $c) { $sanitise_quote = ''; } } #print "c<$c> SQ<$sanitise_quote>\n"; if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { substr($res, $off, 1, $;); } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { substr($res, $off, 1, $;); } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { substr($res, $off, 1, 'X'); } else { substr($res, $off, 1, $c); } } if ($sanitise_quote eq '//') { $sanitise_quote = ''; } # The pathname on a #include may be surrounded by '<' and '>'. if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { my $clean = 'X' x length($1); $res =~ s@\<.*\>@<$clean>@; # The whole of a #error is a string. } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { my $clean = 'X' x length($1); $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; } return $res; } sub get_quoted_string { my ($line, $rawline) = @_; return "" if ($line !~ m/(\"[X]+\")/g); return substr($rawline, $-[0], $+[0] - $-[0]); } sub ctx_statement_block { my ($linenr, $remain, $off) = @_; my $line = $linenr - 1; my $blk = ''; my $soff = $off; my $coff = $off - 1; my $coff_set = 0; my $loff = 0; my $type = ''; my $level = 0; my @stack = (); my $p; my $c; my $len = 0; my $remainder; while (1) { @stack = (['', 0]) if ($#stack == -1); #warn "CSB: blk<$blk> remain<$remain>\n"; # If we are about to drop off the end, pull in more # context. if ($off >= $len) { for (; $remain > 0; $line++) { last if (!defined $lines[$line]); next if ($lines[$line] =~ /^-/); $remain--; $loff = $len; $blk .= $lines[$line] . "\n"; $len = length($blk); $line++; last; } # Bail if there is no further context. #warn "CSB: blk<$blk> off<$off> len<$len>\n"; if ($off >= $len) { last; } if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) { $level++; $type = '#'; } } $p = $c; $c = substr($blk, $off, 1); $remainder = substr($blk, $off); #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; # Handle nested #if/#else. if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { push(@stack, [ $type, $level ]); } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { ($type, $level) = @{$stack[$#stack - 1]}; } elsif ($remainder =~ /^#\s*endif\b/) { ($type, $level) = @{pop(@stack)}; } # Statement ends at the ';' or a close '}' at the # outermost level. if ($level == 0 && $c eq ';') { last; } # An else is really a conditional as long as its not else if if ($level == 0 && $coff_set == 0 && (!defined($p) || $p =~ /(?:\s|\}|\+)/) && $remainder =~ /^(else)(?:\s|{)/ && $remainder !~ /^else\s+if\b/) { $coff = $off + length($1) - 1; $coff_set = 1; #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; } if (($type eq '' || $type eq '(') && $c eq '(') { $level++; $type = '('; } if ($type eq '(' && $c eq ')') { $level--; $type = ($level != 0)? '(' : ''; if ($level == 0 && $coff < $soff) { $coff = $off; $coff_set = 1; #warn "CSB: mark coff<$coff>\n"; } } if (($type eq '' || $type eq '{') && $c eq '{') { $level++; $type = '{'; } if ($type eq '{' && $c eq '}') { $level--; $type = ($level != 0)? '{' : ''; if ($level == 0) { if (substr($blk, $off + 1, 1) eq ';') { $off++; } last; } } # Preprocessor commands end at the newline unless escaped. if ($type eq '#' && $c eq "\n" && $p ne "\\") { $level--; $type = ''; $off++; last; } $off++; } # We are truly at the end, so shuffle to the next line. if ($off == $len) { $loff = $len + 1; $line++; $remain--; } my $statement = substr($blk, $soff, $off - $soff + 1); my $condition = substr($blk, $soff, $coff - $soff + 1); #warn "STATEMENT<$statement>\n"; #warn "CONDITION<$condition>\n"; #print "coff<$coff> soff<$off> loff<$loff>\n"; return ($statement, $condition, $line, $remain + 1, $off - $loff + 1, $level); } sub statement_lines { my ($stmt) = @_; # Strip the diff line prefixes and rip blank lines at start and end. $stmt =~ s/(^|\n)./$1/g; $stmt =~ s/^\s*//; $stmt =~ s/\s*$//; my @stmt_lines = ($stmt =~ /\n/g); return $#stmt_lines + 2; } sub statement_rawlines { my ($stmt) = @_; my @stmt_lines = ($stmt =~ /\n/g); return $#stmt_lines + 2; } sub statement_block_size { my ($stmt) = @_; $stmt =~ s/(^|\n)./$1/g; $stmt =~ s/^\s*{//; $stmt =~ s/}\s*$//; $stmt =~ s/^\s*//; $stmt =~ s/\s*$//; my @stmt_lines = ($stmt =~ /\n/g); my @stmt_statements = ($stmt =~ /;/g); my $stmt_lines = $#stmt_lines + 2; my $stmt_statements = $#stmt_statements + 1; if ($stmt_lines > $stmt_statements) { return $stmt_lines; } else { return $stmt_statements; } } sub ctx_statement_full { my ($linenr, $remain, $off) = @_; my ($statement, $condition, $level); my (@chunks); # Grab the first conditional/block pair. ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); #print "F: c<$condition> s<$statement> remain<$remain>\n"; push(@chunks, [ $condition, $statement ]); if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { return ($level, $linenr, @chunks); } # Pull in the following conditional/block pairs and see if they # could continue the statement. for (;;) { ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); #print "C: c<$condition> s<$statement> remain<$remain>\n"; last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); #print "C: push\n"; push(@chunks, [ $condition, $statement ]); } return ($level, $linenr, @chunks); } sub ctx_block_get { my ($linenr, $remain, $outer, $open, $close, $off) = @_; my $line; my $start = $linenr - 1; my $blk = ''; my @o; my @c; my @res = (); my $level = 0; my @stack = ($level); for ($line = $start; $remain > 0; $line++) { next if ($rawlines[$line] =~ /^-/); $remain--; $blk .= $rawlines[$line]; # Handle nested #if/#else. if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { push(@stack, $level); } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { $level = $stack[$#stack - 1]; } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { $level = pop(@stack); } foreach my $c (split(//, $lines[$line])) { ##print "C<$c>L<$level><$open$close>O<$off>\n"; if ($off > 0) { $off--; next; } if ($c eq $close && $level > 0) { $level--; last if ($level == 0); } elsif ($c eq $open) { $level++; } } if (!$outer || $level <= 1) { push(@res, $rawlines[$line]); } last if ($level == 0); } return ($level, @res); } sub ctx_block_outer { my ($linenr, $remain) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); return @r; } sub ctx_block { my ($linenr, $remain) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); return @r; } sub ctx_statement { my ($linenr, $remain, $off) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); return @r; } sub ctx_block_level { my ($linenr, $remain) = @_; return ctx_block_get($linenr, $remain, 0, '{', '}', 0); } sub ctx_statement_level { my ($linenr, $remain, $off) = @_; return ctx_block_get($linenr, $remain, 0, '(', ')', $off); } sub ctx_locate_comment { my ($first_line, $end_line) = @_; # Catch a comment on the end of the line itself. my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); return $current_comment if (defined $current_comment); # Look through the context and try and figure out if there is a # comment. my $in_comment = 0; $current_comment = ''; for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { my $line = $rawlines[$linenr - 1]; #warn " $line\n"; if ($linenr == $first_line and $line =~ m@^.\s*\*@) { $in_comment = 1; } if ($line =~ m@/\*@) { $in_comment = 1; } if (!$in_comment && $current_comment ne '') { $current_comment = ''; } $current_comment .= $line . "\n" if ($in_comment); if ($line =~ m@\*/@) { $in_comment = 0; } } chomp($current_comment); return($current_comment); } sub ctx_has_comment { my ($first_line, $end_line) = @_; my $cmt = ctx_locate_comment($first_line, $end_line); ##print "LINE: $rawlines[$end_line - 1 ]\n"; ##print "CMMT: $cmt\n"; return ($cmt ne ''); } sub raw_line { my ($linenr, $cnt) = @_; my $offset = $linenr - 1; $cnt++; my $line; while ($cnt) { $line = $rawlines[$offset++]; next if (defined($line) && $line =~ /^-/); $cnt--; } return $line; } sub cat_vet { my ($vet) = @_; my ($res, $coded); $res = ''; while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { $res .= $1; if ($2 ne '') { $coded = sprintf("^%c", unpack('C', $2) + 64); $res .= $coded; } } $res =~ s/$/\$/; return $res; } my $av_preprocessor = 0; my $av_pending; my @av_paren_type; my $av_pend_colon; sub annotate_reset { $av_preprocessor = 0; $av_pending = '_'; @av_paren_type = ('E'); $av_pend_colon = 'O'; } sub annotate_values { my ($stream, $type) = @_; my $res; my $var = '_' x length($stream); my $cur = $stream; print "$stream\n" if ($dbg_values > 1); while (length($cur)) { @av_paren_type = ('E') if ($#av_paren_type < 0); print " <" . join('', @av_paren_type) . "> <$type> <$av_pending>" if ($dbg_values > 1); if ($cur =~ /^(\s+)/o) { print "WS($1)\n" if ($dbg_values > 1); if ($1 =~ /\n/ && $av_preprocessor) { $type = pop(@av_paren_type); $av_preprocessor = 0; } } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { print "CAST($1)\n" if ($dbg_values > 1); push(@av_paren_type, $type); $type = 'c'; } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { print "DECLARE($1)\n" if ($dbg_values > 1); $type = 'T'; } elsif ($cur =~ /^($Modifier)\s*/) { print "MODIFIER($1)\n" if ($dbg_values > 1); $type = 'T'; } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { print "DEFINE($1,$2)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); if ($2 ne '') { $av_pending = 'N'; } $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { print "UNDEF($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { print "PRE_START($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); push(@av_paren_type, $type); $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { print "PRE_RESTART($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $av_paren_type[$#av_paren_type]); $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:endif))/o) { print "PRE_END($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; # Assume all arms of the conditional end as this # one does, and continue as if the #endif was not here. pop(@av_paren_type); push(@av_paren_type, $type); $type = 'E'; } elsif ($cur =~ /^(\\\n)/o) { print "PRECONT($1)\n" if ($dbg_values > 1); } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { print "ATTR($1)\n" if ($dbg_values > 1); $av_pending = $type; $type = 'N'; } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { print "SIZEOF($1)\n" if ($dbg_values > 1); if (defined $2) { $av_pending = 'V'; } $type = 'N'; } elsif ($cur =~ /^(if|while|for)\b/o) { print "COND($1)\n" if ($dbg_values > 1); $av_pending = 'E'; $type = 'N'; } elsif ($cur =~/^(case)/o) { print "CASE($1)\n" if ($dbg_values > 1); $av_pend_colon = 'C'; $type = 'N'; } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { print "KEYWORD($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(\()/o) { print "PAREN('$1')\n" if ($dbg_values > 1); push(@av_paren_type, $av_pending); $av_pending = '_'; $type = 'N'; } elsif ($cur =~ /^(\))/o) { my $new_type = pop(@av_paren_type); if ($new_type ne '_') { $type = $new_type; print "PAREN('$1') -> $type\n" if ($dbg_values > 1); } else { print "PAREN('$1')\n" if ($dbg_values > 1); } } elsif ($cur =~ /^($Ident)\s*\(/o) { print "FUNC($1)\n" if ($dbg_values > 1); $type = 'V'; $av_pending = 'V'; } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { if (defined $2 && $type eq 'C' || $type eq 'T') { $av_pend_colon = 'B'; } elsif ($type eq 'E') { $av_pend_colon = 'L'; } print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); $type = 'V'; } elsif ($cur =~ /^($Ident|$Constant)/o) { print "IDENT($1)\n" if ($dbg_values > 1); $type = 'V'; } elsif ($cur =~ /^($Assignment)/o) { print "ASSIGN($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~/^(;|{|})/) { print "END($1)\n" if ($dbg_values > 1); $type = 'E'; $av_pend_colon = 'O'; } elsif ($cur =~/^(,)/) { print "COMMA($1)\n" if ($dbg_values > 1); $type = 'C'; } elsif ($cur =~ /^(\?)/o) { print "QUESTION($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(:)/o) { print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); substr($var, length($res), 1, $av_pend_colon); if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { $type = 'E'; } else { $type = 'N'; } $av_pend_colon = 'O'; } elsif ($cur =~ /^(\[)/o) { print "CLOSE($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { my $variant; print "OPV($1)\n" if ($dbg_values > 1); if ($type eq 'V') { $variant = 'B'; } else { $variant = 'U'; } substr($var, length($res), 1, $variant); $type = 'N'; } elsif ($cur =~ /^($Operators)/o) { print "OP($1)\n" if ($dbg_values > 1); if ($1 ne '++' && $1 ne '--') { $type = 'N'; } } elsif ($cur =~ /(^.)/o) { print "C($1)\n" if ($dbg_values > 1); } if (defined $1) { $cur = substr($cur, length($1)); $res .= $type x length($1); } } return ($res, $var); } sub possible { my ($possible, $line) = @_; my $notPermitted = qr{(?: ^(?: $Modifier| $Storage| $Type| DEFINE_\S+ )$| ^(?: goto| return| case| else| asm|__asm__| do| \#| \#\#| )(?:\s|$)| ^(?:typedef|struct|enum)\b )}x; warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); if ($possible !~ $notPermitted) { # Check for modifiers. $possible =~ s/\s*$Storage\s*//g; $possible =~ s/\s*$Sparse\s*//g; if ($possible =~ /^\s*$/) { } elsif ($possible =~ /\s/) { $possible =~ s/\s*$Type\s*//g; for my $modifier (split(' ', $possible)) { if ($modifier !~ $notPermitted) { warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); push(@modifierList, $modifier); } } } else { warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); push(@typeList, $possible); } build_types(); } else { warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); } } my $prefix = ''; sub show_type { my ($type) = @_; return defined $use_type{$type} if (scalar keys %use_type > 0); return !defined $ignore_type{$type}; } sub report { my ($level, $type, $msg) = @_; if (!show_type($type) || (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { return 0; } my $line; if ($show_types) { $line = "$prefix$level:$type: $msg\n"; } else { $line = "$prefix$level: $msg\n"; } $line = (split('\n', $line))[0] . "\n" if ($terse); if ($quiet == 0) { push(our @report, $line); } return 1; } sub report_dump { our @report; } sub ERROR { my ($type, $msg) = @_; if (report("ERROR", $type, $msg)) { our $clean = 0; our $cnt_error++; return 1; } return 0; } sub WARN { my ($type, $msg) = @_; if (report("WARNING", $type, $msg)) { ## Warning is okay to submit our $clean = 0; our $cnt_warn++; return 1; } return 0; } sub CHK { my ($type, $msg) = @_; if ($check && report("CHECK", $type, $msg)) { our $clean = 0; our $cnt_chk++; return 1; } return 0; } sub check_absolute_file { my ($absolute, $herecurr) = @_; my $file = $absolute; ##print "absolute<$absolute>\n"; # See if any suffix of this path is a path within the tree. while ($file =~ s@^[^/]*/@@) { if (-f "$root/$file") { ##print "file<$file>\n"; last; } } if (! -f _) { return 0; } # It is, so see if the prefix is acceptable. my $prefix = $absolute; substr($prefix, -length($file)) = ''; ##print "prefix<$prefix>\n"; if ($prefix ne ".../") { WARN("USE_RELATIVE_PATH", "use relative pathname instead of absolute in changelog text\n" . $herecurr); } } sub trim { my ($string) = @_; $string =~ s/^\s+|\s+$//g; return $string; } sub ltrim { my ($string) = @_; $string =~ s/^\s+//; return $string; } sub rtrim { my ($string) = @_; $string =~ s/\s+$//; return $string; } sub string_find_replace { my ($string, $find, $replace) = @_; $string =~ s/$find/$replace/g; return $string; } sub tabify { my ($leading) = @_; my $source_indent = 8; my $max_spaces_before_tab = $source_indent - 1; my $spaces_to_tab = " " x $source_indent; #convert leading spaces to tabs 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; #Remove spaces before a tab 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; return "$leading"; } sub pos_last_openparen { my ($line) = @_; my $pos = 0; my $opens = $line =~ tr/\(/\(/; my $closes = $line =~ tr/\)/\)/; my $last_openparen = 0; if (($opens == 0) || ($closes >= $opens)) { return -1; } my $len = length($line); for ($pos = 0; $pos < $len; $pos++) { my $string = substr($line, $pos); if ($string =~ /^($FuncArg|$balanced_parens)/) { $pos += length($1) - 1; } elsif (substr($line, $pos, 1) eq '(') { $last_openparen = $pos; } elsif (index($string, '(') == -1) { last; } } return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; } sub process { my $filename = shift; my $linenr=0; my $prevline=""; my $prevrawline=""; my $stashline=""; my $stashrawline=""; my $length; my $indent; my $previndent=0; my $stashindent=0; our $clean = 1; my $signoff = 0; my $subject_trailing_dot = 0; my $is_patch = 0; my $in_header_lines = 1; my $in_commit_log = 0; #Scanning lines before patch my $non_utf8_charset = 0; our @report = (); our $cnt_lines = 0; our $cnt_error = 0; our $cnt_warn = 0; our $cnt_chk = 0; # Trace the real file/line as we go. my $realfile = ''; my $realline = 0; my $realcnt = 0; my $here = ''; my $in_comment = 0; my $comment_edge = 0; my $first_line = 0; my $p1_prefix = ''; my $prev_values = 'E'; # suppression flags my %suppress_ifbraces; my %suppress_whiletrailers; my %suppress_export; my $suppress_statement = 0; my %signatures = (); # Pre-scan the patch sanitizing the lines. # Pre-scan the patch looking for any __setup documentation. # my @setup_docs = (); my $setup_docs = 0; my $camelcase_file_seeded = 0; sanitise_line_reset(); my $line; foreach my $rawline (@rawlines) { $linenr++; $line = $rawline; push(@fixed, $rawline) if ($fix); if ($rawline=~/^\+\+\+\s+(\S+)/) { $setup_docs = 0; if ($1 =~ m@Documentation/kernel-parameters.txt$@) { $setup_docs = 1; } #next; } if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $realline=$1-1; if (defined $2) { $realcnt=$3+1; } else { $realcnt=1+1; } $in_comment = 0; # Guestimate if this is a continuing comment. Run # the context looking for a comment "edge". If this # edge is a close comment then we must be in a comment # at context start. my $edge; my $cnt = $realcnt; for (my $ln = $linenr + 1; $cnt > 0; $ln++) { next if (defined $rawlines[$ln - 1] && $rawlines[$ln - 1] =~ /^-/); $cnt--; #print "RAW<$rawlines[$ln - 1]>\n"; last if (!defined $rawlines[$ln - 1]); if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { ($edge) = $1; last; } } if (defined $edge && $edge eq '*/') { $in_comment = 1; } # Guestimate if this is a continuing comment. If this # is the start of a diff block and this line starts # ' *' then it is very likely a comment. if (!defined $edge && $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) { $in_comment = 1; } ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; sanitise_line_reset($in_comment); } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { # Standardise the strings and chars within the input to # simplify matching -- only bother with positive lines. $line = sanitise_line($rawline); } push(@lines, $line); if ($realcnt > 1) { $realcnt-- if ($line =~ /^(?:\+| |$)/); } else { $realcnt = 0; } #print "==>$rawline\n"; #print "-->$line\n"; if ($setup_docs && $line =~ /^\+/) { push(@setup_docs, $line); } } $prefix = ''; $realcnt = 0; $linenr = 0; foreach my $line (@lines) { $linenr++; my $sline = $line; #copy of $line $sline =~ s/$;/ /g; #with comments as spaces my $rawline = $rawlines[$linenr - 1]; #extract the line range in the file after the patch is applied if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $is_patch = 1; $first_line = $linenr + 1; $realline=$1-1; if (defined $2) { $realcnt=$3+1; } else { $realcnt=1+1; } annotate_reset(); $prev_values = 'E'; %suppress_ifbraces = (); %suppress_whiletrailers = (); %suppress_export = (); $suppress_statement = 0; next; # track the line number as we move through the hunk, note that # new versions of GNU diff omit the leading space on completely # blank context lines so we need to count that too. } elsif ($line =~ /^( |\+|$)/) { $realline++; $realcnt-- if ($realcnt != 0); # Measure the line length and indent. ($length, $indent) = line_stats($rawline); # Track the previous line. ($prevline, $stashline) = ($stashline, $line); ($previndent, $stashindent) = ($stashindent, $indent); ($prevrawline, $stashrawline) = ($stashrawline, $rawline); #warn "line<$line>\n"; } elsif ($realcnt == 1) { $realcnt--; } my $hunk_line = ($realcnt != 0); #make up the handle for any error we report on this line $prefix = "$filename:$realline: " if ($emacs && $file); $prefix = "$filename:$linenr: " if ($emacs && !$file); $here = "#$linenr: " if (!$file); $here = "#$realline: " if ($file); my $found_file = 0; # extract the filename as it passes if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); $in_commit_log = 0; $found_file = 1; } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); $in_commit_log = 0; $p1_prefix = $1; if (!$file && $tree && $p1_prefix ne '' && -e "$root/$p1_prefix") { WARN("PATCH_PREFIX", "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); } $found_file = 1; } if ($found_file) { if ($realfile =~ m@^(drivers/net/|net/)@) { $check = 1; } else { $check = $check_orig; } next; } $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); my $hereline = "$here\n$rawline\n"; my $herecurr = "$here\n$rawline\n"; my $hereprev = "$here\n$prevrawline\n$rawline\n"; $cnt_lines++ if ($realcnt != 0); # Check for incorrect file permissions if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { my $permhere = $here . "FILE: $realfile\n"; if ($realfile !~ m@scripts/@ && $realfile !~ /\.(py|pl|awk|sh|t)$/) { ERROR("EXECUTE_PERMISSIONS", "do not set execute permissions for source files\n" . $permhere); } } next if ($realfile =~ /(checkpatch.pl)/); next if ($realfile =~ /\.(md|txt|doc|8|pdf|tex)$/); # Check that the subject does not have a trailing dot if ($in_header_lines && $line =~ /^Subject: \[PATCH\] (.+)\.(\s*)$/) { $subject_trailing_dot++; } # Check the patch for a signoff: if ($line =~ /^\s*signed-off-by:/i) { $signoff++; $in_commit_log = 0; } # Check signature styles if (!$in_header_lines && $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { my $space_before = $1; my $sign_off = $2; my $space_after = $3; my $email = $4; my $ucfirst_sign_off = ucfirst(lc($sign_off)); if ($sign_off !~ /$signature_tags/) { WARN("BAD_SIGN_OFF", "Non-standard signature: $sign_off\n" . $herecurr); } if (defined $space_before && $space_before ne "") { if (WARN("BAD_SIGN_OFF", "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && $fix) { $fixed[$linenr - 1] = "$ucfirst_sign_off $email"; } } if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { if (WARN("BAD_SIGN_OFF", "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && $fix) { $fixed[$linenr - 1] = "$ucfirst_sign_off $email"; } } if (!defined $space_after || $space_after ne " ") { if (WARN("BAD_SIGN_OFF", "Use a single space after $ucfirst_sign_off\n" . $herecurr) && $fix) { $fixed[$linenr - 1] = "$ucfirst_sign_off $email"; } } # Check if email is really Gerrit URL if ($email =~ /^($url_tags)(.*)/) { my $uri = $1; my $url = $2; if ($uri && $url !~ /$gerrit_url/) { ERROR("BAD_URL", "Unrecognized url address: '$email'\n" . $herecurr); } } else { my ($email_name, $email_address, $comment) = parse_email($email); my $suggested_email = format_email(($email_name, $email_address)); if ($suggested_email eq "") { ERROR("BAD_SIGN_OFF", "Unrecognized email address: '$email'\n" . $herecurr); } else { my $dequoted = $suggested_email; $dequoted =~ s/^"//; $dequoted =~ s/" $comment" ne $email && "$suggested_email$comment" ne $email) { WARN("BAD_SIGN_OFF", "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); } } } # Check for duplicate signatures my $sig_nospace = $line; $sig_nospace =~ s/\s//g; $sig_nospace = lc($sig_nospace); if (defined $signatures{$sig_nospace}) { WARN("BAD_SIGN_OFF", "Duplicate signature\n" . $herecurr); } else { $signatures{$sig_nospace} = 1; } } # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("CORRUPTED_PATCH", "patch seems to be corrupt (line wrapped?)\n" . $herecurr) if (!$emitted_corrupt++); } # Check for absolute kernel paths. if ($tree) { while ($line =~ m{(?:^|\s)(/\S*)}g) { my $file = $1; if ($file =~ m{^(.*?)(?::\d+)+:?$} && check_absolute_file($1, $herecurr)) { # } else { check_absolute_file($file, $herecurr); } } } # UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php if (($realfile =~ /^$/ || $line =~ /^\+/) && $rawline !~ m/^$UTF8*$/) { my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); my $blank = copy_spacing($rawline); my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; my $hereptr = "$hereline$ptr\n"; CHK("INVALID_UTF8", "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); } # Check if it's the start of a commit log # (not a header line and we haven't seen the patch filename) if ($in_header_lines && $realfile =~ /^$/ && $rawline !~ /^(commit\b|from\b|[\w-]+:).+$/i) { $in_header_lines = 0; $in_commit_log = 1; } # Check if there is UTF-8 in a commit log when a mail header has explicitly # declined it, i.e defined some charset where it is missing. if ($in_header_lines && $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && $1 !~ /utf-8/i) { $non_utf8_charset = 1; } if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && $rawline =~ /$NON_ASCII_UTF8/) { WARN("UTF8_BEFORE_PATCH", "8-bit UTF-8 used in possible commit log\n" . $herecurr); } # ignore non-hunk lines and lines being removed next if (!$hunk_line || $line =~ /^-/); #trailing whitespace if ($line =~ /^\+.*\015/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (ERROR("DOS_LINE_ENDINGS", "DOS line endings\n" . $herevet) && $fix) { $fixed[$linenr - 1] =~ s/[\s\015]+$//; } } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (ERROR("TRAILING_WHITESPACE", "trailing whitespace\n" . $herevet) && $fix) { $fixed[$linenr - 1] =~ s/\s+$//; } $rpt_cleaners = 1; } if (($realfile =~ /Makefile.*/) && ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { my $flag = $1; my $replacement = { 'EXTRA_AFLAGS' => 'asflags-y', 'EXTRA_CFLAGS' => 'ccflags-y', 'EXTRA_CPPFLAGS' => 'cppflags-y', 'EXTRA_LDFLAGS' => 'ldflags-y', }; WARN("DEPRECATED_VARIABLE", "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); } # check we are in .spec file, then ignore this hunk next if ($realfile eq "glusterfs.spec.in"); # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|pl|py|l|y|sh|in)$/); #line length limit if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && $rawline !~ /^.\s*\*\s*\@$Ident\s/ && !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:|,|\)\s*;)\s*$/ || $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && $length > $max_line_length) { WARN("LONG_LINE", "line over $max_line_length characters\n" . $herecurr); } # check for spaces before a quoted newline if ($rawline =~ /^.*\".*\s\\n/) { if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", "unnecessary whitespace before a quoted newline\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; } } # check for adding lines without a newline. if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { WARN("MISSING_EOF_NEWLINE", "adding a line without newline at end of file\n" . $herecurr); } # check we are in a valid source file C or perl if not then ignore this hunk next if ($realfile !~ /\.(h|c|pl)$/); # check for space before tabs. if ($rawline =~ /^\+/ && $rawline =~ / \t/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (WARN("SPACE_BEFORE_TAB", "please, no space before tabs\n" . $herevet) && $fix) { while ($fixed[$linenr - 1] =~ s/(^\+.*) {8,8}\t/$1\t\t/) {} while ($fixed[$linenr - 1] =~ s/(^\+.*) +\t/$1\t/) {} } } # check for && or || at the start of a line if ($rawline =~ /^\+\s*(&&|\|\|)/) { CHK("LOGICAL_CONTINUATIONS", "Logical continuations should be on the previous line\n" . $hereprev); } # check multi-line statement indentation matches previous line if ($^V && $^V ge 5.10.0 && $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { $prevline =~ /^\+(\t*)(.*)$/; my $oldindent = $1; my $rest = $2; my $pos = pos_last_openparen($rest); if ($pos >= 0) { $line =~ /^(\+| )([ \t]*)/; my $newindent = $2; my $goodtabindent = $oldindent . "\t" x ($pos / 8) . " " x ($pos % 8); my $goodspaceindent = $oldindent . " " x $pos; if ($newindent ne $goodtabindent && $newindent ne $goodspaceindent) { if (CHK("PARENTHESIS_ALIGNMENT", "Alignment should match open parenthesis\n" . $hereprev) && $fix && $line =~ /^\+/) { $fixed[$linenr - 1] =~ s/^\+[ \t]*/\+$goodtabindent/; } } } } if ($line =~ /^\+.*\*[ \t]*\)[ \t]+(?!$Assignment|$Arithmetic)/) { if (CHK("SPACING", "No space is necessary after a cast\n" . $hereprev) && $fix) { $fixed[$linenr - 1] =~ s/^(\+.*\*[ \t]*\))[ \t]+/$1/; } } # check for missing blank lines after declarations if ($sline =~ /^\+\s+\S/ && #Not at char 1 # actual declarations ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # foo bar; where foo is some local typedef or #define $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros $prevline =~ /^\+\s+$declaration_macros/) && # for "else if" which can look like "$Ident $Ident" !($prevline =~ /^\+\s+$c90_Keywords\b/ || # other possible extensions of declaration lines $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || # not starting a section or a macro "\" extended line $prevline =~ /(?:\{\s*|\\)$/) && # looks like a declaration !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # foo bar; where foo is some local typedef or #define $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros $sline =~ /^\+\s+$declaration_macros/ || # start of struct or union or enum $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ || # start or end of block or continuation of declaration $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || # bitfield continuation $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || # other possible extensions of declaration lines $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) && # indentation of previous and current line are the same (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) { WARN("SPACING", "Missing a blank line after declarations\n" . $hereprev); } # check we are in a valid C source file if not then ignore this hunk next if ($realfile !~ /\.(h|c)$/); # check for RCS/CVS revision markers if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { WARN("CVS_KEYWORD", "CVS style keyword markers, these will _not_ be updated\n". $herecurr); } # Check for potential 'bare' types my ($stat, $cond, $line_nr_next, $remain_next, $off_next, $realline_next); #print "LINE<$line>\n"; if ($linenr >= $suppress_statement && $realcnt && $sline =~ /.\s*\S/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0); $stat =~ s/\n./\n /g; $cond =~ s/\n./\n /g; #print "linenr<$linenr> <$stat>\n"; # If this statement has no statement boundaries within # it there is no point in retrying a statement scan # until we hit end of it. my $frag = $stat; $frag =~ s/;+\s*$//; if ($frag !~ /(?:{|;)/) { #print "skip<$line_nr_next>\n"; $suppress_statement = $line_nr_next; } # Find the real next line. $realline_next = $line_nr_next; if (defined $realline_next && (!defined $lines[$realline_next - 1] || substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { $realline_next++; } my $s = $stat; $s =~ s/{.*$//s; # Ignore goto labels. if ($s =~ /$Ident:\*$/s) { # Ignore functions being called } elsif ($s =~ /^.\s*$Ident\s*\(/s) { } elsif ($s =~ /^.\s*else\b/s) { # declarations always start with types } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { my $type = $1; $type =~ s/\s+/ /g; possible($type, "A:" . $s); # definitions in global scope can only start with types } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { possible($1, "B:" . $s); } # any (foo ... *) is a pointer cast, and foo is a type while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { possible($1, "C:" . $s); } # Check for any sort of function declaration. # int foo(something bar, other baz); # void (*store_gdt)(x86_descr_ptr *); if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { my ($name_len) = length($1); my $ctx = $s; substr($ctx, 0, $name_len + 1, ''); $ctx =~ s/\)[^\)]*$//; for my $arg (split(/\s*,\s*/, $ctx)) { if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { possible($1, "D:" . $s); } } } } # # Checks which may be anchored in the context. # # Check for switch () and associated case and default # statements should be at the same indent. if ($line=~/\bswitch\s*\(.*\)/) { my $err = ''; my $sep = ''; my @ctx = ctx_block_outer($linenr, $realcnt); shift(@ctx); for my $ctx (@ctx) { my ($clen, $cindent) = line_stats($ctx); if ($ctx =~ /^\+\s*(case\s+|default:)/ && $indent != $cindent) { $err .= "$sep$ctx\n"; $sep = ''; } else { $sep = "[...]\n"; } } if ($err ne '') { ERROR("SWITCH_CASE_INDENT_LEVEL", "switch and case should be at the same indent\n$hereline$err"); } } # if/while/etc brace do not go on next line, unless defining a do while loop, # or if that brace on the next line is for something else if ($line =~ /(.*)\b((?:if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { my $pre_ctx = "$1$2"; my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); if ($line =~ /^\+\t{6,}/) { WARN("DEEP_INDENTATION", "Too many leading tabs - consider code refactoring\n" . $herecurr); } my $ctx_cnt = $realcnt - $#ctx - 1; my $ctx = join("\n", @ctx); my $ctx_ln = $linenr; my $ctx_skip = $realcnt; while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && defined $lines[$ctx_ln - 1] && $lines[$ctx_ln - 1] =~ /^-/)) { ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); $ctx_ln++; } #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { ERROR("OPEN_BRACE", "that open brace { should be on the previous line\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); } if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && $ctx =~ /\)\s*\;\s*$/ && defined $lines[$ctx_ln - 1]) { my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); if ($nindent > $indent) { WARN("TRAILING_SEMICOLON", "trailing semicolon indicates no statements, indent implies otherwise\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); } } } # Check relative indent for conditionals and blocks. if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0) if (!defined $stat); my ($s, $c) = ($stat, $cond); substr($s, 0, length($c), ''); # Make sure we remove the line prefixes as we have # none on the first line, and are going to readd them # where necessary. $s =~ s/\n./\n/gs; # Find out how long the conditional actually is. my @newlines = ($c =~ /\n/gs); my $cond_lines = 1 + $#newlines; # We want to check the first line inside the block # starting at the end of the conditional, so remove: # 1) any blank line termination # 2) any opening brace { on end of the line # 3) any do (...) { my $continuation = 0; my $check = 0; $s =~ s/^.*\bdo\b//; $s =~ s/^\s*{//; if ($s =~ s/^\s*\\//) { $continuation = 1; } if ($s =~ s/^\s*?\n//) { $check = 1; $cond_lines++; } # Also ignore a loop construct at the end of a # preprocessor statement. if (($prevline =~ /^.\s*#\s*define\s/ || $prevline =~ /\\\s*$/) && $continuation == 0) { $check = 0; } my $cond_ptr = -1; $continuation = 0; while ($cond_ptr != $cond_lines) { $cond_ptr = $cond_lines; # If we see an #else/#elif then the code # is not linear. if ($s =~ /^\s*\#\s*(?:else|elif)/) { $check = 0; } # Ignore: # 1) blank lines, they should be at 0, # 2) preprocessor lines, and # 3) labels. if ($continuation || $s =~ /^\s*?\n/ || $s =~ /^\s*#\s*?/ || $s =~ /^\s*$Ident\s*:/) { $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; if ($s =~ s/^.*?\n//) { $cond_lines++; } } } my (undef, $sindent) = line_stats("+" . $s); my $stat_real = raw_line($linenr, $cond_lines); # Check if either of these lines are modified, else # this is not this patch's fault. if (!defined($stat_real) || $stat !~ /^\+/ && $stat_real !~ /^\+/) { $check = 0; } if (defined($stat_real) && $cond_lines > 1) { $stat_real = "[...]\n$stat_real"; } #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; if ($check && (($sindent % 8) != 0 || ($sindent <= $indent && $s ne ''))) { WARN("SUSPECT_CODE_INDENT", "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); } } # Track the 'values' across context and added lines. my $opline = $line; $opline =~ s/^./ /; my ($curr_values, $curr_vars) = annotate_values($opline . "\n", $prev_values); $curr_values = $prev_values . $curr_values; if ($dbg_values) { my $outline = $opline; $outline =~ s/\t/ /g; print "$linenr > .$outline\n"; print "$linenr > $curr_values\n"; print "$linenr > $curr_vars\n"; } $prev_values = substr($curr_values, -1); #ignore lines not being added next if ($line =~ /^[^\+]/); # TEST: allow direct testing of the type matcher. if ($dbg_type) { if ($line =~ /^.\s*$Declare\s*$/) { ERROR("TEST_TYPE", "TEST: is type\n" . $herecurr); } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { ERROR("TEST_NOT_TYPE", "TEST: is not type ($1 is)\n". $herecurr); } next; } # TEST: allow direct testing of the attribute matcher. if ($dbg_attr) { if ($line =~ /^.\s*$Modifier\s*$/) { ERROR("TEST_ATTR", "TEST: is attr\n" . $herecurr); } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { ERROR("TEST_NOT_ATTR", "TEST: is not attr ($1 is)\n". $herecurr); } next; } # check for initialisation to aggregates open brace on the next line if ($line =~ /^.\s*{/ && $prevline =~ /(?:^|[^=])=\s*$/) { ERROR("OPEN_BRACE", "that open brace { should be on the previous line\n" . $hereprev); } # # Checks which are anchored on the added line. # # check for malformed paths in #include statements (uses RAW line) if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { my $path = $1; if ($path =~ m{//}) { ERROR("MALFORMED_INCLUDE", "malformed #include filename\n" . $herecurr); } if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { ERROR("UAPI_INCLUDE", "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); } } # no C99 // comments if ($line =~ m{//}) { if (ERROR("C99_COMMENTS", "do not use C99 // comments\n" . $herecurr) && $fix) { my $line = $fixed[$linenr - 1]; if ($line =~ /\/\/(.*)$/) { my $comment = trim($1); $fixed[$linenr - 1] =~ s@\/\/(.*)$@/\* $comment \*/@; } } } # Remove C99 comments. $line =~ s@//.*@@; $opline =~ s@//.*@@; # EXPORT_SYMBOL should immediately follow the thing it is exporting, consider # the whole statement. #print "APW <$lines[$realline_next - 1]>\n"; if (defined $realline_next && exists $lines[$realline_next - 1] && !defined $suppress_export{$realline_next} && ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { # Handle definitions which produce identifiers with # a prefix: # XXX(foo); # EXPORT_SYMBOL(something_foo); my $name = $1; if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && $name =~ /^${Ident}_$2/) { #print "FOO C name<$name>\n"; $suppress_export{$realline_next} = 1; } elsif ($stat !~ /(?: \n.}\s*$| ^.DEFINE_$Ident\(\Q$name\E\)| ^.DECLARE_$Ident\(\Q$name\E\)| ^.LIST_HEAD\(\Q$name\E\)| ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() )/x) { #print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; $suppress_export{$realline_next} = 2; } else { $suppress_export{$realline_next} = 1; } } if (!defined $suppress_export{$linenr} && $prevline =~ /^.\s*$/ && ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { #print "FOO B <$lines[$linenr - 1]>\n"; $suppress_export{$linenr} = 2; } if (defined $suppress_export{$linenr} && $suppress_export{$linenr} == 2) { WARN("EXPORT_SYMBOL", "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); } # check for global initialisers. if ($line =~ /^\+(\s*$Type\s*$Ident\s*(?:\s+$Modifier))*\s*=\s*(0|NULL|false)\s*;/) { if (ERROR("GLOBAL_INITIALISERS", "do not initialise globals to 0 or NULL\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/($Type\s*$Ident\s*(?:\s+$Modifier))*\s*=\s*(0|NULL|false)\s*;/$1;/; } } # check for static initialisers. if ($line =~ /^\+.*\bstatic\s.*=\s*(0|NULL|false)\s*;/) { if (ERROR("INITIALISED_STATIC", "do not initialise statics to 0 or NULL\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/(\bstatic\s.*?)\s*=\s*(0|NULL|false)\s*;/$1;/; } } # check for static const char * arrays. if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { WARN("STATIC_CONST_CHAR_ARRAY", "static const char * array should probably be static const char * const\n" . $herecurr); } # check for static char foo[] = "bar" declarations. if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { WARN("STATIC_CONST_CHAR_ARRAY", "static char array declaration should probably be static const char\n" . $herecurr); } # check for non-global char *foo[] = {"bar", ...} declarations. if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { WARN("STATIC_CONST_CHAR_ARRAY", "char * array declaration might be better as static const\n" . $herecurr); } # check for function declarations without arguments like "int foo()" if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { if (ERROR("FUNCTION_WITHOUT_ARGS", "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; } } # * goes on variable not on type # (char*[ const]) while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { #print "AA<$1>\n"; my ($ident, $from, $to) = ($1, $2, $2); # Should start with a space. $to =~ s/^(\S)/ $1/; # Should not end with a space. $to =~ s/\s+$//; # '*'s should not have spaces between. while ($to =~ s/\*\s+\*/\*\*/) { } ## print "1: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to) { if (ERROR("POINTER_LOCATION", "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && $fix) { my $sub_from = $ident; my $sub_to = $ident; $sub_to =~ s/\Q$from\E/$to/; $fixed[$linenr - 1] =~ s@\Q$sub_from\E@$sub_to@; } } } while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { #print "BB<$1>\n"; my ($match, $from, $to, $ident) = ($1, $2, $2, $3); # Should start with a space. $to =~ s/^(\S)/ $1/; # Should not end with a space. $to =~ s/\s+$//; # '*'s should not have spaces between. while ($to =~ s/\*\s+\*/\*\*/) { } # Modifiers should have spaces. $to =~ s/(\b$Modifier$)/$1 /; ## print "2: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to && $ident !~ /^$Modifier$/) { if (ERROR("POINTER_LOCATION", "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && $fix) { my $sub_from = $match; my $sub_to = $match; $sub_to =~ s/\Q$from\E/$to/; $fixed[$linenr - 1] =~ s@\Q$sub_from\E@$sub_to@; } } } # function brace can't be on same line, except for #defines of do while, # or if closed on same line if (($line=~/$Type\s*$Ident\(.*\).*\s\{/) and !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { ERROR("OPEN_BRACE", "open brace '{' following function declarations go on the next line\n" . $herecurr); } # open braces for enum, union and struct go on the same line. if ($line =~ /^.\s*{/ && $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { ERROR("OPEN_BRACE", "open brace '{' following $1 go on the same line\n" . $hereprev); } # missing space after union, struct or enum definition if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { if (WARN("SPACING", "missing space after $1 definition\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; } } # Function pointer declarations # check spacing between type, funcptr, and args # canonical declaration is "type (*funcptr)(args...)" if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { my $declare = $1; my $pre_pointer_space = $2; my $post_pointer_space = $3; my $funcname = $4; my $post_funcname_space = $5; my $pre_args_space = $6; # the $Declare variable will capture all spaces after the type # so check it for a missing trailing missing space but pointer return types # don't need a space so don't warn for those. my $post_declare_space = ""; if ($declare =~ /(\s+)$/) { $post_declare_space = $1; $declare = rtrim($declare); } if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { WARN("SPACING", "missing space after return type\n" . $herecurr); $post_declare_space = " "; } # unnecessary space "type ( *funcptr)(args...)" if (defined $pre_pointer_space && $pre_pointer_space =~ /^\s/) { WARN("SPACING", "Unnecessary space after function pointer open parenthesis\n" . $herecurr); } # unnecessary space "type (* funcptr)(args...)" if (defined $post_pointer_space && $post_pointer_space =~ /^\s/) { WARN("SPACING", "Unnecessary space before function pointer name\n" . $herecurr); } # unnecessary space "type (*funcptr )(args...)" if (defined $post_funcname_space && $post_funcname_space =~ /^\s/) { WARN("SPACING", "Unnecessary space after function pointer name\n" . $herecurr); } # unnecessary space "type (*funcptr) (args...)" if (defined $pre_args_space && $pre_args_space =~ /^\s/) { WARN("SPACING", "Unnecessary space before function pointer arguments\n" . $herecurr); } if (show_type("SPACING") && $fix) { $fixed[$linenr - 1] =~ s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; } } # check for spacing round square brackets; allowed: # 1. with a type on the left -- int [] a; # 2. at the beginning of a line for slice initialisers -- [0...10] = 5, # 3. inside a curly brace -- = { [0...10] = 5 } while ($line =~ /(.*?\s)\[/g) { my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && $prefix !~ /[{,]\s+$/) { if (ERROR("BRACKET_SPACE", "space prohibited before open square bracket '['\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/^(\+.*?)\s+\[/$1\[/; } } } # Check operator spacing. if (!($line=~/\#\s*include/)) { my $fixed_line = ""; my $line_fixed = 0; my $ops = qr{ <<=|>>=|<=|>=|==|!=| \+=|-=|\*=|\/=|%=|\^=|\|=|&=| =>|->|<<|>>|<|>|=|!|~| &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| \?:|\?|: }x; my @elements = split(/($ops|;)/, $opline); ## print("element count: <" . $#elements . ">\n"); ## foreach my $el (@elements) { ## print("el: <$el>\n"); ## } my @fix_elements = (); my $off = 0; foreach my $el (@elements) { push(@fix_elements, substr($rawline, $off, length($el))); $off += length($el); } $off = 0; my $blank = copy_spacing($opline); my $last_after = -1; for (my $n = 0; $n < $#elements; $n += 2) { my $good = $fix_elements[$n] . $fix_elements[$n + 1]; ## print("n: <$n> good: <$good>\n"); $off += length($elements[$n]); # Pick up the preceding and succeeding characters. my $ca = substr($opline, 0, $off); my $cc = ''; if (length($opline) >= ($off + length($elements[$n + 1]))) { $cc = substr($opline, $off + length($elements[$n + 1])); } my $cb = "$ca$;$cc"; my $a = ''; $a = 'V' if ($elements[$n] ne ''); $a = 'W' if ($elements[$n] =~ /\s$/); $a = 'C' if ($elements[$n] =~ /$;$/); $a = 'B' if ($elements[$n] =~ /(\[|\()$/); $a = 'O' if ($elements[$n] eq ''); $a = 'E' if ($ca =~ /^\s*$/); my $op = $elements[$n + 1]; my $c = ''; if (defined $elements[$n + 2]) { $c = 'V' if ($elements[$n + 2] ne ''); $c = 'W' if ($elements[$n + 2] =~ /^\s/); $c = 'C' if ($elements[$n + 2] =~ /^$;/); $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); $c = 'O' if ($elements[$n + 2] eq ''); $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); } else { $c = 'E'; } my $ctx = "${a}x${c}"; my $at = "(ctx:$ctx)"; my $ptr = substr($blank, 0, $off) . "^"; my $hereptr = "$hereline$ptr\n"; # Pull out the value of this operator. my $op_type = substr($curr_values, $off + 1, 1); # Get the full operator variant. my $opv = $op . substr($curr_vars, $off, 1); # Ignore operators passed as parameters. if ($op_type ne 'V' && $ca =~ /\s$/ && $cc =~ /^\s*,/) { # # Ignore comments # } elsif ($op =~ /^$;+$/) { # ; should have either the end of line or a space or \ after it } elsif ($op eq ';') { if ($ctx !~ /.x[WEBC]/ && $cc !~ /^\\/ && $cc !~ /^;/) { if (ERROR("SPACING", "space required after that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; $line_fixed = 1; } } # // is a comment } elsif ($op eq '//') { # : when part of a bitfield } elsif ($opv eq ':B') { # skip the bitfield test for now # No spaces for: # -> } elsif ($op eq '->') { if ($ctx =~ /Wx.|.xW/) { if (ERROR("SPACING", "spaces prohibited around that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # , must have a space on the right. } elsif ($op eq ',') { if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { if (ERROR("SPACING", "space required after that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; $line_fixed = 1; $last_after = $n; } } # '*' as part of a type definition -- reported already. } elsif ($opv eq '*_') { #warn "'*' is part of type\n"; # unary operators should have a space before and # none after. May be left adjacent to another # unary operator, or a cast } elsif ($op eq '!' || $op eq '~' || $opv eq '*U' || $opv eq '-U' || $opv eq '&U' || $opv eq '&&U') { if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { if (ERROR("SPACING", "space required before that '$op' $at\n" . $hereptr)) { if ($n != $last_after + 2) { $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); $line_fixed = 1; } } } if ($op eq '*' && $cc =~/\s*$Modifier\b/) { # A unary '*' may be const } elsif ($ctx =~ /.xW/) { if (ERROR("SPACING", "space prohibited after that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # unary ++ and unary -- are allowed no space on one side. } elsif ($op eq '++' or $op eq '--') { if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { if (ERROR("SPACING", "space required one side of that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; $line_fixed = 1; } } if ($ctx =~ /Wx[BE]/ || ($ctx =~ /Wx./ && $cc =~ /^;/)) { if (ERROR("SPACING", "space prohibited before that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); $line_fixed = 1; } } if ($ctx =~ /ExW/) { if (ERROR("SPACING", "space prohibited after that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # << and >> may either have or not have spaces both sides } elsif ($op eq '<<' or $op eq '>>' or $op eq '&' or $op eq '^' or $op eq '|' or $op eq '+' or $op eq '-' or $op eq '*' or $op eq '/' or $op eq '%') { if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { if (ERROR("SPACING", "need consistent spacing around '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # A colon needs no spaces before when it is # terminating a case value or a label. } elsif ($opv eq ':C' || $opv eq ':L') { if ($ctx =~ /Wx./) { if (ERROR("SPACING", "space prohibited before that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); $line_fixed = 1; } } # All the others need spaces both sides. } elsif ($ctx !~ /[EWC]x[CWE]/) { my $ok = 0; # Ignore email addresses if (($op eq '<' && $cc =~ /^\S+\@\S+>/) || ($op eq '>' && $ca =~ /<\S+\@\S+$/)) { $ok = 1; } # messages are ERROR, but ?: are CHK if ($ok == 0) { my $msg_type = \&ERROR; $msg_type = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); if (&{$msg_type}("SPACING", "spaces required around that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } } $off += length($elements[$n + 1]); ## print("n: <$n> GOOD: <$good>\n"); $fixed_line = $fixed_line . $good; } if (($#elements % 2) == 0) { $fixed_line = $fixed_line . $fix_elements[$#elements]; } if ($fix && $line_fixed && $fixed_line ne $fixed[$linenr - 1]) { $fixed[$linenr - 1] = $fixed_line; } } # check for whitespace before a non-naked semicolon if ($line =~ /^\+.*\S\s+;\s*$/) { if (WARN("SPACING", "space prohibited before semicolon\n" . $herecurr) && $fix) { 1 while $fixed[$linenr - 1] =~ s/^(\+.*\S)\s+;/$1;/; } } # check for multiple assignments if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { CHK("MULTIPLE_ASSIGNMENTS", "multiple assignments should be avoided\n" . $herecurr); } ## # check for multiple declarations, allowing for a function declaration ## # continuation. ## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && ## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { ## ## # Remove any bracketed sections to ensure we do not ## # falsly report the parameters of functions. ## my $ln = $line; ## while ($ln =~ s/\([^\(\)]*\)//g) { ## } ## if ($ln =~ /,/) { ## WARN("MULTIPLE_DECLARATION", ## "declaring multiple variables together should be avoided\n" . $herecurr); ## } ## } #need space before brace following if, while, etc if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || $line =~ /do\{/) { if (ERROR("SPACING", "space required before the open brace '{'\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/^(\+.*(?:do|\)))\{/$1 {/; } } ## # check for blank lines before declarations ## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && ## $prevrawline =~ /^.\s*$/) { ## WARN("SPACING", ## "No blank lines before declarations\n" . $hereprev); ## } ## # closing brace should have a space following it when it has anything # on the line if ($line =~ /}(?!(?:,|;|\)))\S/) { if (ERROR("SPACING", "space required after that close brace '}'\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/}((?!(?:,|;|\)))\S)/} $1/; } } # check spacing on square brackets if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { if (ERROR("SPACING", "space prohibited after that open square bracket '['\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\[\s+/\[/; } } if ($line =~ /\s\]/) { if (ERROR("SPACING", "space prohibited before that close square bracket ']'\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\s+\]/\]/; } } # check spacing on parentheses if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && $line !~ /for\s*\(\s+;/) { if (ERROR("SPACING", "space prohibited after that open parenthesis '('\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\(\s+/\(/; } } if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && $line !~ /for\s*\(.*;\s+\)/ && $line !~ /:\s+\)/) { if (ERROR("SPACING", "space prohibited before that close parenthesis ')'\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\s+\)/\)/; } } #goto labels aren't indented, allow a single space however if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { if (WARN("INDENTED_LABEL", "labels should not be indented\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/^(.)\s+/$1/; } } # return is not a function if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { my $spacing = $1; if ($^V && $^V ge 5.10.0 && $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { my $value = $1; $value = deparenthesize($value); if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { ERROR("RETURN_PARENTHESES", "return is not a function, parentheses are not required\n" . $herecurr); } } elsif ($spacing !~ /\s+/) { ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr); } } # unnecessary return in a void function # at end-of-function, with the previous line a single leading tab, then return; # and the line before that not a goto label target like "out:" if ($sline =~ /^[ \+]}\s*$/ && $prevline =~ /^\+\treturn\s*;\s*$/ && $linenr >= 3 && $lines[$linenr - 3] =~ /^[ +]/ && $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { WARN("RETURN_VOID", "void function return statements are not generally useful\n" . $hereprev); } # if statements using unnecessary parentheses - ie: if ((foo == bar)) if ($^V && $^V ge 5.10.0 && $line =~ /\bif\s*((?:\(\s*){2,})/) { my $openparens = $1; my $count = $openparens =~ tr@\(@\(@; my $msg = ""; if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { my $comp = $4; #Not $1 because of $LvalOrFunc $msg = " - maybe == should be = ?" if ($comp eq "=="); WARN("UNNECESSARY_PARENTHESES", "Unnecessary parentheses$msg\n" . $herecurr); } } # Return of what appears to be an errno should normally be -'ve if ($line =~ /^.\s*return\s*(E[A-Z]*)\s*;/) { my $name = $1; if ($name ne 'EOF' && $name ne 'ERROR') { WARN("USE_NEGATIVE_ERRNO", "return of an errno should typically be -ve (return -$1)\n" . $herecurr); } } # Need a space before open parenthesis after if, while etc if ($line =~ /\b(if|while|for|switch)\(/) { if (ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\b(if|while|for|switch)\(/$1 \(/; } } # Check for illegal assignment in if conditional -- and check for trailing # statements after the conditional. if ($line =~ /do\s*(?!{)/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0) if (!defined $stat); my ($stat_next) = ctx_statement_block($line_nr_next, $remain_next, $off_next); $stat_next =~ s/\n./\n /g; ##print "stat<$stat> stat_next<$stat_next>\n"; if ($stat_next =~ /^\s*while\b/) { # If the statement carries leading newlines, # then count those as offsets. my ($whitespace) = ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); my $offset = statement_rawlines($whitespace) - 1; $suppress_whiletrailers{$line_nr_next + $offset} = 1; } } if (!defined $suppress_whiletrailers{$linenr} && defined($stat) && defined($cond) && $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { my ($s, $c) = ($stat, $cond); if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { ERROR("ASSIGN_IN_IF", "do not use assignment in if condition\n" . $herecurr); } # Find out what is on the end of the line after the # conditional. substr($s, 0, length($c), ''); $s =~ s/\n.*//g; $s =~ s/$;//g; # Remove any comments if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && $c !~ /}\s*while\s*/) { # Find out how long the conditional actually is. my @newlines = ($c =~ /\n/gs); my $cond_lines = 1 + $#newlines; my $stat_real = ''; $stat_real = raw_line($linenr, $cond_lines) . "\n" if ($cond_lines); if (defined($stat_real) && $cond_lines > 1) { $stat_real = "[...]\n$stat_real"; } ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr . $stat_real); } } # Check for bitwise tests written as boolean if ($line =~ / (?: (?:\[|\(|\&\&|\|\|) \s*0[xX][0-9]+\s* (?:\&\&|\|\|) | (?:\&\&|\|\|) \s*0[xX][0-9]+\s* (?:\&\&|\|\||\)|\]) )/x) { WARN("HEXADECIMAL_BOOLEAN_TEST", "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); } # if and else should not have general statements after it if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { my $s = $1; $s =~ s/$;//g; # Remove any comments if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr); } } # if should not continue a brace if ($line =~ /}\s*if\b/) { ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr); } # case and default should not have general statements after them if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && $line !~ /\G(?: (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| \s*return\s+ )/xg) { ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr); } # Check for }else {, these must be at the same # indent level to be relevant to each other. if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ and $previndent == $indent) { ERROR("ELSE_AFTER_BRACE", "else should follow close brace '}'\n" . $hereprev); } if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ and $previndent == $indent) { my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); # Find out what is on the end of the line after the # conditional. substr($s, 0, length($c), ''); $s =~ s/\n.*//g; if ($s =~ /^\s*;/) { ERROR("WHILE_AFTER_BRACE", "while should follow close brace '}'\n" . $hereprev); } } #Specific variable tests while ($line =~ m{($Constant|$Lval)}g) { my $var = $1; #gcc binary extension if ($var =~ /^$Binary$/) { if (WARN("GCC_BINARY_CONSTANT", "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) && $fix) { my $hexval = sprintf("0x%x", oct($var)); $fixed[$linenr - 1] =~ s/\b$var\b/$hexval/; } } #CamelCase if ($var !~ /^$Constant$/ && $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && #Ignore Page variants $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && #Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/) { while ($var =~ m{($Ident)}g) { my $word = $1; next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); if ($check) { seed_camelcase_includes(); if (!$file && !$camelcase_file_seeded) { seed_camelcase_file($realfile); $camelcase_file_seeded = 1; } } if (!defined $camelcase{$word}) { $camelcase{$word} = 1; CHK("CAMELCASE", "Avoid CamelCase: <$word>\n" . $herecurr); } } } } #no spaces allowed after \ in define if ($line =~ /\#\s*define.*\\\s+$/) { if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", "Whitespace after \\ makes next lines useless\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\s+$//; } } #warn if is #included and is available (uses RAW line) if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\}) { my $file = "$1.h"; my $checkfile = "include/linux/$file"; if (-f "$root/$checkfile" && $realfile ne $checkfile && $1 !~ /$allowed_asm_includes/) { if ($realfile =~ m{^arch/}) { CHK("ARCH_INCLUDE_LINUX", "Consider using #include instead of \n" . $herecurr); } else { WARN("INCLUDE_LINUX", "Use #include instead of \n" . $herecurr); } } } # multi-statement macros should be enclosed in a do while loop, grab the # first statement and ensure its the whole macro if its not enclosed # in a known good container if ($realfile !~ m@/vmlinux.lds.h$@ && $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { my $ln = $linenr; my $cnt = $realcnt; my ($off, $dstat, $dcond, $rest); my $ctx = ''; ($dstat, $dcond, $ln, $cnt, $off) = ctx_statement_block($linenr, $realcnt, 0); $ctx = $dstat; #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; $dstat =~ s/^.\s*\#\s*define\s+$Ident(?:\([^\)]*\))?\s*//; $dstat =~ s/$;//g; $dstat =~ s/\\\n.//g; $dstat =~ s/^\s*//s; $dstat =~ s/\s*$//s; # Flatten any parentheses and braces while ($dstat =~ s/\([^\(\)]*\)/1/ || $dstat =~ s/\{[^\{\}]*\}/1/ || $dstat =~ s/\[[^\[\]]*\]/1/) { } # Flatten any obvious string concatentation. while ($dstat =~ s/("X*")\s*$Ident/$1/ || $dstat =~ s/$Ident\s*("X*")/$1/) { } my $exceptions = qr{ $Declare| module_param_named| MODULE_PARM_DESC| DECLARE_PER_CPU| DEFINE_PER_CPU| __typeof__\(| union| struct| \.$Ident\s*=\s*| ^\"|\"$ }x; #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; if ($dstat ne '' && $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz $dstat !~ /^'X'$/ && # character constants $dstat !~ /$exceptions/ && $dstat !~ /^\.$Ident\s*=/ && # .foo = $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) $dstat !~ /^for\s*$Constant$/ && # for (...) $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() $dstat !~ /^do\s*{/ && # do {... $dstat !~ /^\(\{/ && # ({... $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) { $ctx =~ s/\n*$//; my $herectx = $here . "\n"; my $cnt = statement_rawlines($ctx); for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } if ($dstat =~ /;/) { ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); } else { ERROR("COMPLEX_MACRO", "Macros with complex values should be enclosed in parenthesis\n" . "$herectx"); } } # check for line continuations outside of #defines, preprocessor #, and asm } else { if ($prevline !~ /^..*\\$/ && $line !~ /^\+\s*\#.*\\$/ && # preprocessor $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm $line =~ /^\+.*\\$/) { WARN("LINE_CONTINUATIONS", "Avoid unnecessary line continuations\n" . $herecurr); } } # do {} while (0) macro tests: # single-statement macros do not need to be enclosed in do while (0) loop, # macro should not end with a semicolon if ($^V && $^V ge 5.10.0 && $realfile !~ m@/vmlinux.lds.h$@ && $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { my $ln = $linenr; my $cnt = $realcnt; my ($off, $dstat, $dcond, $rest); my $ctx = ''; ($dstat, $dcond, $ln, $cnt, $off) = ctx_statement_block($linenr, $realcnt, 0); $ctx = $dstat; $dstat =~ s/\\\n.//g; if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { my $stmts = $2; my $semis = $3; $ctx =~ s/\n*$//; my $cnt = statement_rawlines($ctx); my $herectx = $here . "\n"; for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } if (($stmts =~ tr/;/;/) == 1 && $stmts !~ /^\s*(if|while|for|switch)\b/) { WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); } if (defined $semis && $semis ne "") { WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); } } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { $ctx =~ s/\n*$//; my $cnt = statement_rawlines($ctx); my $herectx = $here . "\n"; for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } WARN("TRAILING_SEMICOLON", "macros should not use a trailing semicolon\n" . "$herectx"); } } # make sure symbols are always wrapped with VMLINUX_SYMBOL() ... # all assignments may have only one of the following with an assignment: # . # ALIGN(...) # VMLINUX_SYMBOL(...) if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { WARN("MISSING_VMLINUX_SYMBOL", "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); } # check for redundant bracing round if etc if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { my ($level, $endln, @chunks) = ctx_statement_full($linenr, $realcnt, 1); #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; if ($#chunks > 0 && $level == 0) { my @allowed = (); my $allow = 0; my $seen = 0; my $herectx = $here . "\n"; my $ln = $linenr - 1; for my $chunk (@chunks) { my ($cond, $block) = @{$chunk}; # If the condition carries leading newlines, then count those as offsets. my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); my $offset = statement_rawlines($whitespace) - 1; $allowed[$allow] = 0; #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; # We have looked at and allowed this specific line. $suppress_ifbraces{$ln + $offset} = 1; $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; $ln += statement_rawlines($block) - 1; substr($block, 0, length($cond), ''); $seen++ if ($block =~ /^\s*{/); #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n"; if (statement_lines($cond) > 1) { #print "APW: ALLOWED: cond<$cond>\n"; $allowed[$allow] = 1; } if ($block =~/\b(?:if|for|while)\b/) { #print "APW: ALLOWED: block<$block>\n"; $allowed[$allow] = 1; } if (statement_block_size($block) > 1) { #print "APW: ALLOWED: lines block<$block>\n"; $allowed[$allow] = 1; } $allow++; } if ($seen) { my $sum_allowed = 0; foreach (@allowed) { $sum_allowed += $_; } if ($sum_allowed != 0 && $sum_allowed != $allow && $seen != $allow) { CHK("BRACES", "braces {} should be used on all arms of this statement\n" . $herectx); } } } } if (!defined $suppress_ifbraces{$linenr - 1} && $line =~ /\b(if|while|for|else)\b/) { my $allowed = 0; # Check the pre-context. if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { #print "APW: ALLOWED: pre<$1>\n"; $allowed = 1; } my ($level, $endln, @chunks) = ctx_statement_full($linenr, $realcnt, $-[0]); # Check the condition. my ($cond, $block) = @{$chunks[0]}; #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; if (defined $cond) { substr($block, 0, length($cond), ''); } if (statement_lines($cond) > 1) { #print "APW: ALLOWED: cond<$cond>\n"; $allowed = 1; } if ($block =~/\b(?:if|for|while)\b/) { #print "APW: ALLOWED: block<$block>\n"; $allowed = 1; } if (statement_block_size($block) > 1) { #print "APW: ALLOWED: lines block<$block>\n"; $allowed = 1; } # Check the post-context. if (defined $chunks[1]) { my ($cond, $block) = @{$chunks[1]}; if (defined $cond) { substr($block, 0, length($cond), ''); } if ($block =~ /^\s*\{/) { #print "APW: ALLOWED: chunk-1 block<$block>\n"; $allowed = 1; } } } # check for unnecessary blank lines around braces if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { CHK("BRACES", "Blank lines aren't necessary before a close brace '}'\n" . $hereprev); } if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { CHK("BRACES", "Blank lines aren't necessary after an open brace '{'\n" . $hereprev); } # no volatiles please my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { WARN("VOLATILE", "Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); } # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { CHK("REDUNDANT_CODE", "if this code is redundant consider removing it\n" . $herecurr); } # check for needless "if () fn()" uses if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { my $expr = '\s*\(\s*' . quotemeta($1) . '\s*\)\s*;'; if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?)$expr/) { WARN('NEEDLESS_IF', "$1(NULL) is safe this check is probably not required\n" . $hereprev); } } # check for bad placement of section $InitAttribute (e.g.: __initdata) if ($line =~ /(\b$InitAttribute\b)/) { my $attr = $1; if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { my $ptr = $1; my $var = $2; if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && ERROR("MISPLACED_INIT", "$attr should be placed after $var\n" . $herecurr)) || ($ptr !~ /\b(union|struct)\s+$attr\b/ && WARN("MISPLACED_INIT", "$attr should be placed after $var\n" . $herecurr))) && $fix) { $fixed[$linenr - 1] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; } } } # check for $InitAttributeData (ie: __initdata) with const if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { my $attr = $1; $attr =~ /($InitAttributePrefix)(.*)/; my $attr_prefix = $1; my $attr_type = $2; if (ERROR("INIT_ATTRIBUTE", "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/$InitAttributeData/${attr_prefix}initconst/; } } # check for $InitAttributeConst (ie: __initconst) without const if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { my $attr = $1; if (ERROR("INIT_ATTRIBUTE", "Use of $attr requires a separate use of const\n" . $herecurr) && $fix) { my $lead = $fixed[$linenr - 1] =~ /(^\+\s*(?:static\s+))/; $lead = rtrim($1); $lead = "$lead " if ($lead !~ /^\+$/); $lead = "${lead}const "; $fixed[$linenr - 1] =~ s/(^\+\s*(?:static\s+))/$lead/; } } # don't use __constant_ functions outside of include/uapi/ if ($realfile !~ m@^include/uapi/@ && $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { my $constant_func = $1; my $func = $constant_func; $func =~ s/^__constant_//; if (WARN("CONSTANT_CONVERSION", "$constant_func should be $func\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\b$constant_func\b/$func/g; } } # prefer usleep_range over udelay if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { my $delay = $1; # ignore udelay's < 10, however if (! ($delay < 10) ) { CHK("USLEEP_RANGE", "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr); } if ($delay > 2000) { WARN("LONG_UDELAY", "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); } } # warn about unexpectedly long msleep's if ($line =~ /\bmsleep\s*\((\d+)\);/) { if ($1 < 20) { WARN("MSLEEP", "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr); } } # check for comparisons of jiffies if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { WARN("JIFFIES_COMPARISON", "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); } # check for comparisons of get_jiffies_64() if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { WARN("JIFFIES_COMPARISON", "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); } # warn about spacing in #ifdefs if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { if (ERROR("SPACING", "exactly one space required after that #$1\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; } } # check for spinlock_t definitions without a comment. if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { my $which = $1; if (!ctx_has_comment($first_line, $linenr)) { CHK("UNCOMMENTED_DEFINITION", "$1 definition without comment\n" . $herecurr); } } # check for memory barriers without a comment. if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { if (!ctx_has_comment($first_line, $linenr)) { WARN("MEMORY_BARRIER", "memory barrier without comment\n" . $herecurr); } } # check of hardware specific defines if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { CHK("ARCH_DEFINES", "architecture specific defines should be avoided\n" . $herecurr); } # Check that the storage class is at the beginning of a declaration if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { WARN("STORAGE_CLASS", "storage class should be at the beginning of the declaration\n" . $herecurr) } # check the location of the inline attribute, that it is between # storage class and type. if ($line =~ /\b$Type\s+$Inline\b/ || $line =~ /\b$Inline\s+$Storage\b/) { ERROR("INLINE_LOCATION", "inline keyword should sit between storage class and type\n" . $herecurr); } # Check for __inline__ and __inline, prefer inline if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b(__inline__|__inline)\b/) { if (WARN("INLINE", "plain inline is preferred over $1\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\b(__inline__|__inline)\b/inline/; } } # Check for __attribute__ packed, prefer __packed if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { WARN("PREFER_PACKED", "__packed is preferred over __attribute__((packed))\n" . $herecurr); } # Check for __attribute__ aligned, prefer __aligned if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { WARN("PREFER_ALIGNED", "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); } # Check for __attribute__ format(printf, prefer __printf if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { if (WARN("PREFER_PRINTF", "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; } } # Check for __attribute__ format(scanf, prefer __scanf if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { if (WARN("PREFER_SCANF", "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; } } # check for sizeof(&) if ($line =~ /\bsizeof\s*\(\s*\&/) { WARN("SIZEOF_ADDRESS", "sizeof(& should be avoided\n" . $herecurr); } # check for sizeof without parenthesis if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { if (WARN("SIZEOF_PARENTHESIS", "sizeof $1 should be sizeof($1)\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; } } # check for line continuations in quoted strings with odd counts of " if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { WARN("LINE_CONTINUATIONS", "Avoid line continuations in quoted strings\n" . $herecurr); } # check for struct spinlock declarations if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { WARN("USE_SPINLOCK_T", "struct spinlock should be spinlock_t\n" . $herecurr); } # check for seq_printf uses that could be seq_puts if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { my $fmt = get_quoted_string($line, $rawline); if ($fmt ne "" && $fmt !~ /[^\\]\%/) { if (WARN("PREFER_SEQ_PUTS", "Prefer seq_puts to seq_printf\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/\bseq_printf\b/seq_puts/; } } } # Check for misused memsets if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/s) { my $ms_addr = $2; my $ms_val = $7; my $ms_size = $12; if ($ms_size =~ /^(0x|)0$/i) { ERROR("MEMSET", "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n"); } elsif ($ms_size =~ /^(0x|)1$/i) { WARN("MEMSET", "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n"); } } # typecasts on min/max could be min_t/max_t if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { if (defined $2 || defined $7) { my $call = $1; my $cast1 = deparenthesize($2); my $arg1 = $3; my $cast2 = deparenthesize($7); my $arg2 = $8; my $cast; if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) { $cast = "$cast1 or $cast2"; } elsif ($cast1 ne "") { $cast = $cast1; } else { $cast = $cast2; } WARN("MINMAX", "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n"); } } # check usleep_range arguments if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { my $min = $1; my $max = $7; if ($min eq $max) { WARN("USLEEP_RANGE", "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && $min > $max) { WARN("USLEEP_RANGE", "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); } } # check for naked sscanf if ($^V && $^V ge 5.10.0 && defined $stat && $line =~ /\bsscanf\b/ && ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; my $stat_real = raw_line($linenr, 0); for (my $count = $linenr + 1; $count <= $lc; $count++) { $stat_real = $stat_real . "\n" . raw_line($count, 0); } WARN("NAKED_SSCANF", "unchecked sscanf return value\n" . "$here\n$stat_real\n"); } # check for simple sscanf that should be kstrto if ($^V && $^V ge 5.10.0 && defined $stat && $line =~ /\bsscanf\b/) { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; my $stat_real = raw_line($linenr, 0); for (my $count = $linenr + 1; $count <= $lc; $count++) { $stat_real = $stat_real . "\n" . raw_line($count, 0); } if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { my $format = $6; my $count = $format =~ tr@%@%@; if ($count == 1 && $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { WARN("SSCANF_TO_KSTRTO", "Prefer kstrto to single variable sscanf\n" . "$here\n$stat_real\n"); } } } # check for new externs in .h files. if ($realfile =~ /\.h$/ && $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { if (CHK("AVOID_EXTERNS", "extern prototypes should be avoided in .h files\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; } } # check for new externs in .c files. if ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) { my $function_name = $1; my $paren_space = $2; my $s = $stat; if (defined $cond) { substr($s, 0, length($cond), ''); } if ($s =~ /^\s*;/ && $function_name ne 'uninitialized_var') { WARN("AVOID_EXTERNS", "externs should be avoided in .c files\n" . $herecurr); } if ($paren_space =~ /\n/) { WARN("FUNCTION_ARGUMENTS", "arguments for function declarations should follow identifier\n" . $herecurr); } } elsif ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^.\s*extern\s+/) { WARN("AVOID_EXTERNS", "externs should be avoided in .c files\n" . $herecurr); } # check for pointless casting of kmalloc return if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) { WARN("UNNECESSARY_CASTS", "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); } # alloc style # p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) if ($^V && $^V ge 5.10.0 && $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { CHK("ALLOC_SIZEOF_STRUCT", "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); } # check for multiple semicolons if ($line =~ /;\s*;\s*$/) { if (WARN("ONE_SEMICOLON", "Statements terminations use 1 semicolon\n" . $herecurr) && $fix) { $fixed[$linenr - 1] =~ s/(\s*;\s*){2,}$/;/g; } } # check for case / default statements not preceeded by break/fallthrough/switch if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { my $has_break = 0; my $has_statement = 0; my $count = 0; my $prevline = $linenr; while ($prevline > 1 && $count < 3 && !$has_break) { $prevline--; my $rline = $rawlines[$prevline - 1]; my $fline = $lines[$prevline - 1]; last if ($fline =~ /^\@\@/); next if ($fline =~ /^\-/); next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); next if ($fline =~ /^.[\s$;]*$/); $has_statement = 1; $count++; $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/); } if (!$has_break && $has_statement) { WARN("MISSING_BREAK", "Possible switch case/default not preceeded by break or fallthrough comment\n" . $herecurr); } } # check for switch/default statements without a break; if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { my $ctx = ''; my $herectx = $here . "\n"; my $cnt = statement_rawlines($stat); for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } WARN("DEFAULT_NO_BREAK", "switch default: should use break\n" . $herectx); } # check for comparisons against true and false if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { my $lead = $1; my $arg = $2; my $test = $3; my $otype = $4; my $trail = $5; my $op = "!"; ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); my $type = lc($otype); if ($type =~ /^(?:true|false)$/) { if (("$test" eq "==" && "$type" eq "true") || ("$test" eq "!=" && "$type" eq "false")) { $op = ""; } CHK("BOOL_COMPARISON", "Using comparison to $otype is error prone\n" . $herecurr); } } # check for semaphores initialized locked if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { WARN("CONSIDER_COMPLETION", "consider using a completion\n" . $herecurr); } # check for %L{u,d,i} in strings my $string; while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { $string = substr($rawline, $-[1], $+[1] - $-[1]); $string =~ s/%%/__/g; if ($string =~ /(?[0]; my $arg_pos = $entry->[1]; my $skip_args = ""; if ($arg_pos > 1) { $arg_pos--; $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; } my $test = "\\b$func\\s*\\(${skip_args}([\\d]+)\\s*[,\\)]"; if ($line =~ /$test/) { my $val = $1; $val = $6 if ($skip_args ne ""); if ($val !~ /^0$/ && (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || length($val) ne 4)) { ERROR("NON_OCTAL_PERMISSIONS", "Use 4 digit octal (0777) not decimal permissions\n" . $herecurr); } } } } } # If we have no input at all, then there is nothing to report on # so just keep quiet. if ($#rawlines == -1) { exit(0); } # In mailback mode only produce a report in the negative, for # things that appear to be patches. if ($mailback && ($clean == 1 || !$is_patch)) { exit(0); } # This is not a patch, and we are are in 'no-patch' mode so # just keep quiet. if (!$chk_patch && !$is_patch) { exit(0); } if (!$is_patch) { ERROR("NOT_UNIFIED_DIFF", "Does not appear to be a unified-diff format patch\n"); } if ($is_patch && $subject_trailing_dot != 0) { ERROR("SUBJECT_TRAILING_DOT", "The subject of the patch should not end with a dot.\n"); } if ($is_patch && $chk_signoff && $signoff == 0) { ERROR("MISSING_SIGN_OFF", "Missing Signed-off-by: line(s)\n"); } print report_dump(); if ($summary && !($clean == 1 && $quiet == 1)) { print "$filename " if ($summary_file); if ($cnt_error > 0) { print "Patch not according to coding guidelines! please fix.\n"; print "total: $cnt_error errors, $cnt_warn warnings, " . (($check)? "$cnt_chk checks, " : "") . "$cnt_lines lines checked\n"; exit 1; } else { print "total: $cnt_warn warnings, " . (($check)? "$cnt_chk checks, " : "") . "$cnt_lines lines checked\n"; print "Patch found to have warnings, please fix if necessary.\n" if ($cnt_warn > 0); exit 2; } print "\n" if ($quiet == 0); } if ($quiet == 0) { if ($^V lt 5.10.0) { print("NOTE: perl $^V is not modern enough to detect all possible issues.\n"); print("An upgrade to at least perl v5.10.0 is suggested.\n\n"); } # If there were whitespace errors which cleanpatch can fix # then suggest that. if ($rpt_cleaners) { print "NOTE: whitespace errors detected, you may wish to use scripts/cleanpatch or\n"; print " scripts/cleanfile\n\n"; $rpt_cleaners = 0; } } hash_show_words(\%use_type, "Used"); hash_show_words(\%ignore_type, "Ignored"); if ($clean == 0 && $fix && "@rawlines" ne "@fixed") { my $newfile = $filename; $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); my $linecount = 0; my $f; open($f, '>', $newfile) or die "$P: Can't open $newfile for write\n"; foreach my $fixed_line (@fixed) { $linecount++; if ($file) { if ($linecount > 3) { $fixed_line =~ s/^\+//; print $f $fixed_line. "\n"; } } else { print $f $fixed_line . "\n"; } } close($f); if (!$quiet) { print << "EOM"; Wrote EXPERIMENTAL --fix correction(s) to '$newfile' Do _NOT_ trust the results written to this file. Do _NOT_ submit these changes without inspecting them for correctness. This EXPERIMENTAL file is simply a convenience to help rewrite patches. No warranties, expressed or implied... EOM } } if ($clean == 1 && $quiet == 0) { print "$vname has no obvious style problems and is ready for submission.\n" } if ($clean == 0 && $quiet == 0) { print << "EOM"; $vname has style problems, please review. If any of these errors are false positives, please report them to the maintainer, see MAINTAINERS EOM } return $clean; } glusterfs-11.2/build-aux/config.guess.dist000077500000000000000000000007101503123251100206160ustar00rootroot00000000000000#!/bin/sh # # This script is intentionally left empty. Distributions that package GlusterFS # may want to to replace it with an updated copy from the automake project. # cat << EOM It is not expected to execute this script. When you are building from a released tarball (generated with 'make dist'), you are expected to pass --build=... and --host=... to ./configure or replace this config.guess script in the sources with an updated version. EOM exit 0 glusterfs-11.2/build-aux/config.sub.dist000077500000000000000000000007061503123251100202660ustar00rootroot00000000000000#!/bin/sh # # This script is intentionally left empty. Distributions that package GlusterFS # may want to to replace it with an updated copy from the automake project. # cat << EOM It is not expected to execute this script. When you are building from a released tarball (generated with 'make dist'), you are expected to pass --build=... and --host=... to ./configure or replace this config.sub script in the sources with an updated version. EOM exit 0 glusterfs-11.2/build-aux/pkg-version000077500000000000000000000036221503123251100175330ustar00rootroot00000000000000#!/bin/bash # To override version/release from git, # create VERSION file containing text with version/release # eg. v3.4.0-1 # One thing to note, If one does 'git clone --depth N glusterfs.git', # the git describe command doesn't work. Hence you notice below that # we have added timestamp as version (YYYY.MM.DD) and release (HH.mmss) PKG_VERSION=`cat VERSION 2> /dev/null || git describe --tags --match "v[0-9]*" 2>/dev/null` get_version() { # tags and output versions: # - v3.4.0 => 3.4.0 (upstream clean) # - v3.4.0-1 => 3.4.0 (downstream clean) # - v3.4.0-2-g34e62f => 3.4.0 (upstream dirty) # - v3.4.0-1-2-g34e62f => 3.4.0 (downstream dirty) AWK_VERSION=' BEGIN { FS="-" } /^v[0-9]/ { sub(/^v/,"") ; print $1 }' version=$(echo $PKG_VERSION | awk "$AWK_VERSION" | tr -cd '[:alnum:].') if [ "x${version}" == "x" ] ; then version=$(date +%Y.%m.%d | tr -d '\n') fi echo $version | tr -d '\n' } get_release() { # tags and output releases: # - v3.4.0 => 0 (upstream clean) # - v3.4.0-1 => 1 (downstream clean) # - v3.4.0-2-g34e62f1 => 2.git34e62f1 (upstream dirty) # - v3.4.0-1-2-g34e62f1 => 1.2.git34e62f1 (downstream dirty) AWK_RELEASE=' BEGIN { FS="-"; OFS="." } /^v[0-9]/ { if (NF == 1) print 0 else if (NF == 2) print $2 else if (NF == 3) print $2, "git" substr($3, 2) else if (NF == 4) print $2, $3, "git" substr($4, 2) }' release=$(echo $PKG_VERSION | awk "$AWK_RELEASE" | tr -cd '[:alnum:].') if [ "x${release}" == "x" ] ; then release=$(date +%H.%M%S | tr -d '\n') fi echo $release | tr -d '\n' } if test "x$1" = "x--full"; then echo -n "v$(get_version)-$(get_release)" elif test "x$1" = "x--version"; then get_version elif test "x$1" = "x--release"; then get_release else echo "usage: $0 [--full|--version|--release]" exit 1 fi glusterfs-11.2/cli/000077500000000000000000000000001503123251100142135ustar00rootroot00000000000000glusterfs-11.2/cli/Makefile.am000066400000000000000000000000341503123251100162440ustar00rootroot00000000000000SUBDIRS = src CLEANFILES = glusterfs-11.2/cli/src/000077500000000000000000000000001503123251100150025ustar00rootroot00000000000000glusterfs-11.2/cli/src/Makefile.am000066400000000000000000000022501503123251100170350ustar00rootroot00000000000000sbin_PROGRAMS = gluster gluster_SOURCES = cli.c registry.c input.c cli-cmd.c cli-rl.c cli-cmd-global.c \ cli-cmd-volume.c cli-cmd-peer.c cli-rpc-ops.c cli-cmd-parser.c\ cli-cmd-system.c cli-cmd-misc.c cli-xml-output.c cli-quotad-client.c cli-cmd-snapshot.c gluster_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la $(GF_LDADD) \ $(top_builddir)/rpc/xdr/src/libgfxdr.la \ $(top_builddir)/rpc/rpc-lib/src/libgfrpc.la \ $(READLINE_LIBS) $(XML_LIBS) gluster_LDFLAGS = $(GF_LDFLAGS) noinst_HEADERS = cli.h cli-mem-types.h cli-cmd.h cli-quotad-client.h AM_CPPFLAGS = $(GF_CPPFLAGS) \ -I$(top_srcdir)/libglusterfs/src -I$(top_srcdir)/rpc/rpc-lib/src \ -I$(top_srcdir)/rpc/xdr/src -I$(top_builddir)/rpc/xdr/src \ -DDATADIR=\"$(localstatedir)\" \ -DCONFDIR=\"$(sysconfdir)/glusterfs\" \ -DGSYNCD_PREFIX=\"$(GLUSTERFS_LIBEXECDIR)\"\ -DGLFSHEAL_PREFIX=\"$(GLUSTERFS_LIBEXECDIR)\"\ -DSYNCDAEMON_COMPILE=$(SYNCDAEMON_COMPILE) AM_CFLAGS = -Wall $(GF_CFLAGS) $(XML_CFLAGS) $(READLINE_CFLAGS) CLEANFILES = $(top_builddir)/libglusterfs/src/libglusterfs.la: $(MAKE) -C $(top_builddir)/libglusterfs/src/ all install-data-hook: $(mkdir_p) $(DESTDIR)$(localstatedir)/run/gluster glusterfs-11.2/cli/src/cli-cmd-global.c000066400000000000000000000117521503123251100177220ustar00rootroot00000000000000/* Copyright (c) 2015 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include "cli.h" #include "cli-cmd.h" #include "cli-mem-types.h" #include #include int cli_cmd_global_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount); int cli_cmd_get_state_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount); int cli_cmd_ganesha_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount); struct cli_cmd global_cmds[] = { { "global help", cli_cmd_global_help_cbk, "list global commands", }, { "get-state [] [[odir ] " "[file ]] [detail|volumeoptions]", cli_cmd_get_state_cbk, "Get local state representation of mentioned daemon", }, { "nfs-ganesha {enable| disable} ", cli_cmd_ganesha_cbk, "Enable/disable NFS-Ganesha support", }, {NULL, NULL, NULL}}; int cli_cmd_global_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount) { struct cli_cmd *cmd = NULL; struct cli_cmd *global_cmd = NULL; int count = 0; cmd = GF_MALLOC(sizeof(global_cmds), cli_mt_cli_cmd); memcpy(cmd, global_cmds, sizeof(global_cmds)); count = (sizeof(global_cmds) / sizeof(struct cli_cmd)); cli_cmd_sort(cmd, count); cli_out("\ngluster global commands"); cli_out("========================\n"); for (global_cmd = cmd; global_cmd->pattern; global_cmd++) if (_gf_false == global_cmd->disable) cli_out("%s - %s", global_cmd->pattern, global_cmd->desc); cli_out("\n"); GF_FREE(cmd); return 0; } int cli_cmd_global_register(struct cli_state *state) { int ret = 0; struct cli_cmd *cmd = NULL; for (cmd = global_cmds; cmd->pattern; cmd++) { ret = cli_cmd_register(&state->tree, cmd); if (ret) goto out; } out: return ret; } int cli_cmd_ganesha_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int sent = 0; int parse_error = 0; int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; dict_t *options = NULL; cli_local_t *local = NULL; char *op_errstr = NULL; proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GANESHA]; frame = create_frame(THIS, THIS->ctx->pool); if (!frame) goto out; ret = cli_cmd_ganesha_parse(state, words, wordcount, &options, &op_errstr); if (ret) { if (op_errstr) { cli_err("%s", op_errstr); GF_FREE(op_errstr); } else cli_usage_out(word->pattern); parse_error = 1; goto out; } CLI_LOCAL_INIT(local, words, frame, options); if (proc->fn) { ret = proc->fn(frame, THIS, options); } out: if (ret) { cli_cmd_sent_status_get(&sent); if ((sent == 0) && (parse_error == 0)) cli_out("Setting global option failed"); } CLI_STACK_DESTROY(frame); if (options) dict_unref(options); return ret; } int cli_cmd_get_state_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int sent = 0; int parse_error = 0; int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; dict_t *options = NULL; cli_local_t *local = NULL; char *op_errstr = NULL; ret = cli_cmd_get_state_parse(state, words, wordcount, &options, &op_errstr); if (ret) { if (op_errstr) { cli_err("%s", op_errstr); cli_usage_out(word->pattern); GF_FREE(op_errstr); } else cli_usage_out(word->pattern); parse_error = 1; goto out; } frame = create_frame(THIS, THIS->ctx->pool); if (!frame) { ret = -1; goto out; } CLI_LOCAL_INIT(local, words, frame, options); proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GET_STATE]; if (proc->fn) ret = proc->fn(frame, THIS, options); out: if (ret) { cli_cmd_sent_status_get(&sent); if ((sent == 0) && (parse_error == 0)) cli_out("Getting daemon state failed"); } CLI_STACK_DESTROY(frame); if (options) dict_unref(options); return ret; } glusterfs-11.2/cli/src/cli-cmd-misc.c000066400000000000000000000060571503123251100174170ustar00rootroot00000000000000/* Copyright (c) 2010-2012 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include #include #include #include "cli.h" #include "cli-cmd.h" #include "cli-mem-types.h" extern struct cli_cmd volume_cmds[]; extern struct cli_cmd bitrot_cmds[]; extern struct cli_cmd quota_cmds[]; extern struct cli_cmd cli_probe_cmds[]; extern struct cli_cmd cli_log_cmds[]; extern struct cli_cmd cli_system_cmds[]; extern struct cli_cmd cli_bd_cmds[]; extern struct cli_cmd snapshot_cmds[]; extern struct cli_cmd global_cmds[]; struct cli_cmd cli_misc_cmds[]; int cli_cmd_quit_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { exit(0); } static gf_boolean_t cli_is_help_command(const char *pattern) { /* FixFixFix * This is not the best way to determine whether * this is a help command */ if (strstr(pattern, "help")) return _gf_true; return _gf_false; } int cli_cmd_display_help(struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount) { static struct cli_cmd *cmd[] = { cli_misc_cmds, cli_probe_cmds, volume_cmds, bitrot_cmds, quota_cmds, snapshot_cmds, global_cmds, NULL}; struct cli_cmd *cmd_ind = NULL; int i = 0; gf_boolean_t list_all = _gf_false; /* cli_system_cmds commands for internal usage they are not exposed */ /* If "help all" */ if (wordcount == 2) list_all = _gf_true; for (i = 0; cmd[i] != NULL; i++) { for (cmd_ind = cmd[i]; cmd_ind->pattern; cmd_ind++) { if ((_gf_false == cmd_ind->disable) && cli_is_help_command(cmd_ind->pattern)) { if (list_all && (cmd_ind->cbk)) { cmd_ind->cbk(state, in_word, words, wordcount); } else { cli_out(" %-25s- %s", cmd_ind->pattern, cmd_ind->desc); } } } } cli_out("\n"); return 0; } struct cli_cmd cli_help_cmds[] = { {"help [all]", cli_cmd_display_help, "display help for command classes"}, {NULL, NULL, NULL}}; struct cli_cmd cli_misc_cmds[] = {{"quit", cli_cmd_quit_cbk, "quit"}, {"exit", cli_cmd_quit_cbk, "exit"}, {NULL, NULL, NULL}}; int cli_cmd_misc_register(struct cli_state *state) { int ret = 0; struct cli_cmd *cmd = NULL; for (cmd = cli_misc_cmds; cmd->pattern; cmd++) { ret = cli_cmd_register(&state->tree, cmd); if (ret) goto out; } for (cmd = cli_help_cmds; cmd->pattern; cmd++) { ret = cli_cmd_register(&state->tree, cmd); if (ret) goto out; } out: return ret; } glusterfs-11.2/cli/src/cli-cmd-parser.c000066400000000000000000005016671503123251100177670ustar00rootroot00000000000000/* Copyright (c) 2010-2013 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include #include #include #include #include #include "cli.h" #include "cli-cmd.h" #include "cli-mem-types.h" #include #include #define MAX_SNAP_DESCRIPTION_LEN 1024 static struct snap_config_opt_vals_ snap_confopt_vals[] = { {.op_name = "snap-max-hard-limit", .question = "Changing snapshot-max-hard-limit " "will limit the creation of new snapshots " "if they exceed the new limit.\n" "Do you want to continue?"}, {.op_name = "snap-max-soft-limit", .question = "If Auto-delete is enabled, snap-max-soft-limit will" " trigger deletion of oldest snapshot, on the " "creation of new snapshot, when the " "snap-max-soft-limit is reached.\n" "Do you want to change the snap-max-soft-limit?"}, {.op_name = "both", .question = "Changing snapshot-max-hard-limit " "will limit the creation of new snapshots " "if they exceed the new snapshot-max-hard-limit.\n" "If Auto-delete is enabled, snap-max-soft-limit will" " trigger deletion of oldest snapshot, on the " "creation of new snapshot, when the " "snap-max-soft-limit is reached.\n" "Do you want to continue?"}, { .op_name = NULL, }}; enum cli_snap_config_set_types { GF_SNAP_CONFIG_SET_HARD = 0, GF_SNAP_CONFIG_SET_SOFT = 1, GF_SNAP_CONFIG_SET_BOTH = 2, }; typedef enum cli_snap_config_set_types cli_snap_config_set_types; typedef struct _cli_brick { struct list_head list; const char *name; int32_t len; } cli_brick_t; int cli_cmd_validate_volume(char *volname); static const char * id_sel(void *wcon) { return (const char *)wcon; } static char * str_getunamb(const char *tok, char **opwords) { return (char *)cli_getunamb(tok, (void **)opwords, id_sel); } static int validate_brick_name(char *brick) { char *delimiter = NULL; int ret = 0; delimiter = strrchr(brick, ':'); if (!delimiter || delimiter == brick || *(delimiter + 1) != '/') ret = -1; return ret; } int32_t cli_cmd_ta_brick_parse(const char **words, int wordcount, char **ta_brick) { char *host_name = NULL; char *tmp_host = NULL; char *delimiter = NULL; cli_brick_t *brick = NULL; int ret = 0; GF_ASSERT(words); GF_ASSERT(wordcount); if (validate_brick_name((char *)words[wordcount - 1])) { cli_err( "Wrong brick type: %s, use :" "", words[wordcount - 1]); ret = -1; goto out; } else { delimiter = strrchr(words[wordcount - 1], ':'); ret = gf_canonicalize_path(delimiter + 1); if (ret) goto out; } tmp_host = gf_strdup((char *)words[wordcount - 1]); if (!tmp_host) { gf_log("cli", GF_LOG_ERROR, "Out of memory"); ret = -1; goto out; } get_host_name(tmp_host, &host_name); if (!host_name) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Unable to retrieve " "hostname"); goto out; } if (!(strcmp(host_name, "localhost") && strcmp(host_name, "127.0.0.1") && strncmp(host_name, "0.", 2))) { cli_err( "Please provide a valid hostname/ip other " "than localhost, 127.0.0.1 or loopback " "address (0.0.0.0 to 0.255.255.255)."); ret = -1; goto out; } if (!valid_internet_address(host_name, _gf_false, _gf_false)) { cli_err( "internet address '%s' does not conform to " "standards", host_name); } brick = GF_MALLOC(sizeof(cli_brick_t), gf_common_list_node); if (brick == NULL) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Out of memory"); goto out; } brick->name = words[wordcount - 1]; brick->len = strlen(words[wordcount - 1]); gf_asprintf(ta_brick, " %s ", brick->name); if (*ta_brick == NULL) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Out of memory"); goto out; } out: if (tmp_host) { GF_FREE(tmp_host); tmp_host = NULL; } if (brick) { GF_FREE(brick); brick = NULL; } return ret; } int32_t cli_cmd_bricks_parse(const char **words, int wordcount, int brick_index, char **bricks, int *brick_count) { int ret = 0; char *delimiter = NULL; char *host_name = NULL; char *tmp_host = NULL; char *bricks_str = NULL; int len = 0; int brick_list_len = 1; /* For initial space */ struct list_head brick_list = { 0, }; cli_brick_t *brick = NULL; GF_ASSERT(words); GF_ASSERT(wordcount); GF_ASSERT(bricks); GF_ASSERT(brick_index > 0); GF_ASSERT(brick_index < wordcount); INIT_LIST_HEAD(&brick_list); while (brick_index < wordcount) { if (validate_brick_name((char *)words[brick_index])) { cli_err( "Wrong brick type: %s, use :" "", words[brick_index]); ret = -1; goto out; } else { delimiter = strrchr(words[brick_index], ':'); ret = gf_canonicalize_path(delimiter + 1); if (ret) goto out; } tmp_host = gf_strdup((char *)words[brick_index]); if (!tmp_host) { gf_log("cli", GF_LOG_ERROR, "Out of memory"); ret = -1; goto out; } get_host_name(tmp_host, &host_name); if (!host_name) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Unable to allocate " "memory"); GF_FREE(tmp_host); goto out; } if (!(strcmp(host_name, "localhost") && strcmp(host_name, "127.0.0.1") && strncmp(host_name, "0.", 2))) { cli_err( "Please provide a valid hostname/ip other " "than localhost, 127.0.0.1 or loopback " "address (0.0.0.0 to 0.255.255.255)."); ret = -1; GF_FREE(tmp_host); goto out; } if (!valid_internet_address(host_name, _gf_false, _gf_false)) { cli_err( "internet address '%s' does not conform to " "standards", host_name); } GF_FREE(tmp_host); list_for_each_entry(brick, &brick_list, list) { if (strcmp(brick->name, words[brick_index]) == 0) { ret = -1; cli_err("Found duplicate exports %s", words[brick_index]); goto out; } } brick = GF_MALLOC(sizeof(cli_brick_t), gf_common_list_node); if (brick == NULL) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Out of memory"); goto out; } len = strlen(words[brick_index]); brick->name = words[brick_index]; brick->len = len; list_add_tail(&brick->list, &brick_list); brick_list_len += len + 1; /* Brick name + space */ ++(*brick_count); ++brick_index; } /* If brick count is not valid exit here */ if (!*brick_count) { cli_err("No bricks specified"); ret = -1; goto out; } brick_list_len++; /* For terminating null char */ bricks_str = GF_MALLOC(brick_list_len, gf_common_mt_char); if (bricks_str == NULL) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Out of memory"); goto out; } *bricks = bricks_str; *bricks_str = ' '; bricks_str++; while (!list_empty(&brick_list)) { brick = list_first_entry(&brick_list, cli_brick_t, list); list_del_init(&brick->list); memcpy(bricks_str, brick->name, brick->len); bricks_str[brick->len] = ' '; bricks_str += brick->len + 1; GF_FREE(brick); } *bricks_str = 0; out: while (!list_empty(&brick_list)) { brick = list_first_entry(&brick_list, cli_brick_t, list); list_del_init(&brick->list); GF_FREE(brick); } return ret; } int32_t cli_cmd_create_disperse_check(struct cli_state *state, int *disperse, int *redundancy, int *data, int count) { int i = 0; int tmp = 0; gf_answer_t answer = GF_ANSWER_NO; char question[128]; const char *question1 = "There isn't an optimal redundancy value " "for this configuration. Do you want to " "create the volume with redundancy 1 ?"; const char *question2 = "The optimal redundancy for this " "configuration is %d. Do you want to create " "the volume with this value ?"; const char *question3 = "This configuration is not optimal on most " "workloads. Do you want to use it ?"; const char *question4 = "Redundancy for this configuration is %d. " "Do you want to create " "the volume with this value ?"; if (*data > 0) { if (*disperse > 0 && *redundancy > 0) { if (*disperse != (*data + *redundancy)) { cli_err( "Disperse count(%d) should be equal " "to sum of disperse-data count(%d) and " "redundancy count(%d)", *disperse, *data, *redundancy); return -1; } } else if (*redundancy > 0) { *disperse = *data + *redundancy; } else if (*disperse > 0) { *redundancy = *disperse - *data; } else { if ((count - *data) >= *data) { cli_err( "Please provide redundancy count " "along with disperse-data count"); return -1; } else { sprintf(question, question4, count - *data); answer = cli_cmd_get_confirmation(state, question); if (answer == GF_ANSWER_NO) return -1; *redundancy = count - *data; *disperse = count; } } } if (*disperse <= 0) { if (count < 3) { cli_err( "number of bricks must be greater " "than 2"); return -1; } *disperse = count; } if (*redundancy == -1) { tmp = *disperse - 1; for (i = tmp / 2; (i > 0) && ((tmp & -tmp) != tmp); i--, tmp--) ; if (i == 0) { answer = cli_cmd_get_confirmation(state, question1); if (answer == GF_ANSWER_NO) return -1; *redundancy = 1; } else { *redundancy = *disperse - tmp; if (*redundancy > 1) { sprintf(question, question2, *redundancy); answer = cli_cmd_get_confirmation(state, question); if (answer == GF_ANSWER_NO) return -1; } } tmp = 0; } else { tmp = *disperse - *redundancy; } if ((*redundancy < 1) || (*redundancy > (*disperse - 1) / 2)) { cli_err( "redundancy must be greater than or equal to 1 and " "less than %d for a disperse %d volume", (*disperse + 1) / 2, *disperse); return -1; } if ((*disperse - *redundancy) > 16) { cli_err("disperse-data bricks must be less than or equal to 16"); return -1; } if ((tmp & -tmp) != tmp) { answer = cli_cmd_get_confirmation(state, question3); if (answer == GF_ANSWER_NO) return -1; } return 0; } static int32_t cli_validate_disperse_volume(char *word, gf1_cluster_type type, const char **words, int32_t wordcount, int32_t index, int32_t *disperse_count, int32_t *redundancy_count, int32_t *data_count) { int ret = -1; switch (type) { case GF_CLUSTER_TYPE_NONE: case GF_CLUSTER_TYPE_DISPERSE: if (strcmp(word, "disperse") == 0) { if (*disperse_count >= 0) { cli_err("disperse option given twice"); goto out; } if (wordcount < (index + 2)) { goto out; } ret = gf_string2int(words[index + 1], disperse_count); if (ret == -1 && errno == EINVAL) { *disperse_count = 0; ret = 1; } else if (ret == -1) { goto out; } else { if (*disperse_count < 3) { cli_err( "disperse count must " "be greater than 2"); ret = -1; goto out; } ret = 2; } } else if (strcmp(word, "disperse-data") == 0) { if (*data_count >= 0) { cli_err("disperse-data option given twice"); goto out; } if (wordcount < (index + 2)) { goto out; } ret = gf_string2int(words[index + 1], data_count); if (ret == -1 || *data_count < 2 || *data_count > 16) { cli_err( "disperse-data must be greater than 1 " "and less than equal to 16"); ret = -1; goto out; } ret = 2; } else if (strcmp(word, "redundancy") == 0) { if (*redundancy_count >= 0) { cli_err("redundancy option given twice"); goto out; } if (wordcount < (index + 2)) { goto out; } ret = gf_string2int(words[index + 1], redundancy_count); if (ret == -1 || *redundancy_count < 1) { cli_err("redundancy must be greater than 0"); ret = -1; goto out; } ret = 2; } break; case GF_CLUSTER_TYPE_REPLICATE: cli_err( "replicated-dispersed volume is not " "supported"); goto out; default: cli_err("Invalid type given"); break; } out: return ret; } int32_t cli_validate_volname(const char *volname) { int32_t ret = -1; int32_t i = -1; int volname_len; static const char *const invalid_volnames[] = {"volume", "type", "help", "subvolumes", "option", "end-volume", "all", "volume_not_in_ring", "description", "force", "snap-max-hard-limit", "snap-max-soft-limit", "auto-delete", "activate-on-create", NULL}; if (volname[0] == '-') goto out; for (i = 0; invalid_volnames[i]; i++) { if (!strcmp(volname, invalid_volnames[i])) { cli_err("\"%s\" cannot be the name of a volume.", volname); goto out; } } if (strchr(volname, '/')) goto out; volname_len = strlen(volname); if (volname_len > GD_VOLUME_NAME_MAX) { cli_err("Volume name exceeds %d characters.", GD_VOLUME_NAME_MAX); goto out; } for (i = 0; i < volname_len; i++) { if (!isalnum(volname[i]) && (volname[i] != '_') && (volname[i] != '-')) { cli_err( "Volume name should not contain \"%c\"" " character.\nVolume names can only" "contain alphanumeric, '-' and '_' " "characters.", volname[i]); goto out; } } ret = 0; out: return ret; } int32_t cli_cmd_volume_create_parse(struct cli_state *state, const char **words, int wordcount, dict_t **options, char **brick_list) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; gf1_cluster_type type = GF_CLUSTER_TYPE_NONE; int sub_count = 1; int brick_index = 0; char *trans_type = NULL; int32_t index = 0; char *bricks = NULL; char *ta_brick = NULL; int32_t brick_count = 0; static char *opwords[] = { "replica", "transport", "disperse", "redundancy", "disperse-data", "arbiter", "thin-arbiter", NULL}; char *w = NULL; int op_count = 0; int32_t replica_count = 1; int32_t arbiter_count = 0; int32_t thin_arbiter_count = 0; int32_t disperse_count = -1; int32_t redundancy_count = -1; int32_t disperse_data_count = -1; gf_boolean_t is_force = _gf_false; int wc = wordcount; gf_answer_t answer = GF_ANSWER_NO; const char *question = NULL; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; if (wordcount < 3) goto out; volname = (char *)words[2]; GF_ASSERT(volname); /* Validate the volume name here itself */ if (cli_validate_volname(volname) < 0) goto out; if (wordcount < 4) { ret = -1; goto out; } type = GF_CLUSTER_TYPE_NONE; index = 3; while (op_count < 3) { ret = -1; w = str_getunamb(words[index], opwords); if (!w) { break; } else if ((strcmp(w, "replica")) == 0) { switch (type) { case GF_CLUSTER_TYPE_REPLICATE: cli_err("replica option given twice"); goto out; case GF_CLUSTER_TYPE_NONE: type = GF_CLUSTER_TYPE_REPLICATE; break; case GF_CLUSTER_TYPE_DISPERSE: cli_err( "replicated-dispersed volume is not " "supported"); goto out; default: cli_err("Invalid type given"); goto out; } if (wordcount < (index + 2)) { ret = -1; goto out; } replica_count = strtol(words[index + 1], NULL, 0); if (replica_count < 2) { cli_err( "replica count should be greater" " than 1"); ret = -1; goto out; } index += 2; if (words[index]) { if (!strcmp(words[index], "arbiter")) { ret = gf_string2int(words[index + 1], &arbiter_count); if ((ret == -1) || (arbiter_count != 1) || ((replica_count < 2) || (replica_count > 3))) { cli_err( "For arbiter " "configuration, " "replica count must be" " 2 and arbiter count " "must be 1. The 3rd " "brick of the replica " "will be the arbiter"); ret = -1; goto out; } ret = dict_set_int32(dict, "arbiter-count", arbiter_count); if (ret) goto out; index += 2; } else if (!strcmp(words[index], "thin-arbiter")) { ret = gf_string2int(words[index + 1], &thin_arbiter_count); if ((ret == -1) || (thin_arbiter_count != 1) || (replica_count != 2)) { cli_err( "For thin-arbiter " "configuration, " "replica count must be" " 2 and thin-arbiter count " "must be 1. The 3rd " "brick of the replica " "will be the thin-arbiter brick"); ret = -1; goto out; } ret = dict_set_int32(dict, "thin-arbiter-count", thin_arbiter_count); if (ret) goto out; index += 2; } } /* Do this to keep glusterd happy with sending "replica 3 arbiter 1" options to server */ if ((arbiter_count == 1) && (replica_count == 2)) replica_count += arbiter_count; if (replica_count == 2 && thin_arbiter_count == 0) { if (strcmp(words[wordcount - 1], "force")) { question = "Replica 2 volumes are prone" " to split-brain. Use " "Arbiter or Replica 3 to " "avoid this. See: " "http://docs.gluster.org/en/latest/Administrator-Guide/" "Split-brain-and-ways-to-deal-with-it/." "\nDo you still want to " "continue?\n"; answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { gf_log("cli", GF_LOG_ERROR, "Volume create " "cancelled, exiting"); ret = -1; goto out; } } } ret = dict_set_int32(dict, "replica-count", replica_count); if (ret) goto out; } else if ((strcmp(w, "transport")) == 0) { if (trans_type) { cli_err( "'transport' option given more" " than one time"); goto out; } if ((strcasecmp(words[index + 1], "tcp") == 0)) { trans_type = gf_strdup("tcp"); } else if ((strcasecmp(words[index + 1], "rdma") == 0)) { trans_type = gf_strdup("rdma"); } else if ((strcasecmp(words[index + 1], "tcp,rdma") == 0) || (strcasecmp(words[index + 1], "rdma,tcp") == 0)) { trans_type = gf_strdup("tcp,rdma"); } else { gf_log("", GF_LOG_ERROR, "incorrect transport" " protocol specified"); ret = -1; goto out; } index += 2; } else if ((strcmp(w, "disperse") == 0) || (strcmp(w, "redundancy") == 0) || (strcmp(w, "disperse-data") == 0)) { ret = cli_validate_disperse_volume( w, type, words, wordcount, index, &disperse_count, &redundancy_count, &disperse_data_count); if (ret < 0) goto out; index += ret; type = GF_CLUSTER_TYPE_DISPERSE; } else if ((strcmp(w, "arbiter") == 0)) { cli_err( "arbiter option must be preceded by replica " "option."); ret = -1; goto out; } else if ((strcmp(w, "thin-arbiter") == 0)) { cli_err( "thin-arbiter option must be preceded by replica " "option."); ret = -1; goto out; } else { GF_ASSERT(!"opword mismatch"); ret = -1; goto out; } op_count++; } if (!trans_type) trans_type = gf_strdup("tcp"); if (index >= wordcount) { ret = -1; goto out; } brick_index = index; if (strcmp(words[wordcount - 1], "force") == 0) { is_force = _gf_true; wc = wordcount - 1; } // Exclude the thin-arbiter-brick i.e. last brick in the bricks list if (thin_arbiter_count == 1) { ret = cli_cmd_bricks_parse(words, wc - 1, brick_index, &bricks, &brick_count); if (ret) goto out; ret = cli_cmd_ta_brick_parse(words, wc, &ta_brick); } else { ret = cli_cmd_bricks_parse(words, wc, brick_index, &bricks, &brick_count); } if (ret) goto out; if (type == GF_CLUSTER_TYPE_DISPERSE) { ret = cli_cmd_create_disperse_check(state, &disperse_count, &redundancy_count, &disperse_data_count, brick_count); if (!ret) ret = dict_set_int32(dict, "disperse-count", disperse_count); if (!ret) ret = dict_set_int32(dict, "redundancy-count", redundancy_count); if (ret) goto out; sub_count = disperse_count; } if (brick_count % sub_count) { if (type == GF_CLUSTER_TYPE_REPLICATE) cli_err( "number of bricks is not a multiple of " "replica count"); else if (type == GF_CLUSTER_TYPE_DISPERSE) cli_err( "number of bricks is not a multiple of " "disperse count"); else cli_err( "number of bricks given doesn't match " "required count"); ret = -1; goto out; } /* Everything is parsed fine. start setting info in dict */ ret = dict_set_str(dict, "volname", volname); if (ret) goto out; ret = dict_set_int32(dict, "type", type); if (ret) goto out; ret = dict_set_dynstr(dict, "transport", trans_type); if (ret) goto out; trans_type = NULL; ret = dict_set_dynstr(dict, "bricks", bricks); if (ret) goto out; if (thin_arbiter_count == 1) { ret = dict_set_dynstr(dict, "ta-brick", ta_brick); if (ret) goto out; } ret = dict_set_int32(dict, "count", brick_count); if (ret) goto out; ret = dict_set_int32(dict, "force", is_force); if (ret) goto out; *options = dict; *brick_list = bricks; out: if (ret) { GF_FREE(bricks); GF_FREE(ta_brick); gf_log("cli", GF_LOG_ERROR, "Unable to parse create volume CLI"); if (dict) dict_unref(dict); } GF_FREE(trans_type); return ret; } int32_t cli_cmd_volume_reset_parse(const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; if (wordcount < 3) goto out; if (wordcount > 5) goto out; volname = (char *)words[2]; if (!volname) { ret = -1; goto out; } ret = dict_set_str(dict, "volname", volname); if (ret) goto out; if (wordcount == 3) { ret = dict_set_str(dict, "key", "all"); if (ret) goto out; } if (wordcount >= 4) { if (!strcmp("force", (char *)words[3])) { ret = dict_set_int32(dict, "force", 1); if (ret) goto out; ret = dict_set_str(dict, "key", "all"); if (ret) goto out; } else { ret = dict_set_str(dict, "key", (char *)words[3]); if (ret) goto out; } } if (wordcount == 5) { if (strcmp("force", (char *)words[4])) { ret = -1; goto out; } else { ret = dict_set_int32(dict, "force", 1); if (ret) goto out; } } *options = dict; out: if (ret && dict) { dict_unref(dict); } return ret; } int32_t cli_cmd_get_state_parse(struct cli_state *state, const char **words, int wordcount, dict_t **options, char **op_errstr) { dict_t *dict = NULL; int ret = -1; char *odir = NULL; char *filename = NULL; char *daemon_name = NULL; int count = 0; uint32_t cmd = 0; GF_VALIDATE_OR_GOTO("cli", options, out); GF_VALIDATE_OR_GOTO("cli", words, out); dict = dict_new(); if (!dict) goto out; if (wordcount < 1 || wordcount > 7) { *op_errstr = gf_strdup( "Problem parsing arguments." " Check usage."); goto out; } if (wordcount >= 1) { gf_asprintf(&daemon_name, "%s", "glusterd"); for (count = 1; count < wordcount; count++) { if (strcmp(words[count], "odir") == 0 || strcmp(words[count], "file") == 0) { if (strcmp(words[count], "odir") == 0) { if (++count < wordcount) { odir = (char *)words[count]; continue; } else { ret = -1; goto out; } } else if (strcmp(words[count], "file") == 0) { if (++count < wordcount) { filename = (char *)words[count]; continue; } else { ret = -1; goto out; } } } else { if (count > 1) { if (count == wordcount - 1) { if (strcmp(words[count], "detail") == 0) { cmd = GF_CLI_GET_STATE_DETAIL; continue; } else if (strcmp(words[count], "volumeoptions") == 0) { cmd = GF_CLI_GET_STATE_VOLOPTS; continue; } } else { *op_errstr = gf_strdup( "Problem" " parsing arguments. " "Check usage."); ret = -1; goto out; } } if (strcmp(words[count], "glusterd") == 0) { continue; } else { if (count == wordcount - 1) { if (strcmp(words[count], "detail") == 0) { cmd = GF_CLI_GET_STATE_DETAIL; continue; } else if (strcmp(words[count], "volumeoptions") == 0) { cmd = GF_CLI_GET_STATE_VOLOPTS; continue; } } *op_errstr = gf_strdup( "glusterd is " "the only supported daemon."); ret = -1; goto out; } } } ret = dict_set_dynstr(dict, "daemon", daemon_name); if (ret) { *op_errstr = gf_strdup( "Command failed. Please check " " log file for more details."); gf_log(THIS->name, GF_LOG_ERROR, "Setting daemon name to dictionary failed"); goto out; } daemon_name = NULL; if (odir) { ret = dict_set_str(dict, "odir", odir); if (ret) { *op_errstr = gf_strdup( "Command failed. Please" " check log file for" " more details."); gf_log(THIS->name, GF_LOG_ERROR, "Setting output directory to" "dictionary failed"); goto out; } } if (filename) { ret = dict_set_str(dict, "filename", filename); if (ret) { *op_errstr = gf_strdup( "Command failed. Please" " check log file for" " more details."); gf_log(THIS->name, GF_LOG_ERROR, "Setting filename to dictionary failed"); goto out; } } if (cmd) { ret = dict_set_uint32(dict, "getstate-cmd", cmd); if (ret) { *op_errstr = gf_strdup( "Command failed. Please" " check log file for" " more details."); gf_log(THIS->name, GF_LOG_ERROR, "Setting " "get-state command type to dictionary " "failed"); goto out; } } } out: if (dict) *options = dict; if (ret && dict) dict_unref(dict); GF_FREE(daemon_name); return ret; } int32_t cli_cmd_inode_quota_parse(const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) { gf_log("cli", GF_LOG_ERROR, "dict_new failed"); goto out; } if (wordcount != 4) goto out; volname = (char *)words[2]; if (!volname) { ret = -1; goto out; } /* Validate the volume name here itself */ if (cli_validate_volname(volname) < 0) goto out; ret = dict_set_str(dict, "volname", volname); if (ret < 0) goto out; if (strcmp(words[3], "enable") != 0) { cli_out("Invalid quota option : %s", words[3]); ret = -1; goto out; } ret = dict_set_int32(dict, "type", GF_QUOTA_OPTION_TYPE_ENABLE_OBJECTS); if (ret < 0) goto out; *options = dict; out: if (ret < 0) { if (dict) dict_unref(dict); } return ret; } int32_t cli_cmd_quota_parse(const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; int i = -1; char key[20] = { 0, }; int64_t value = 0; gf_quota_type type = GF_QUOTA_OPTION_TYPE_NONE; static char *opwords[] = {"enable", "disable", "limit-usage", "remove", "list", "alert-time", "soft-timeout", "hard-timeout", "default-soft-limit", "limit-objects", "list-objects", "remove-objects", NULL}; char *w = NULL; time_t time = 0; double percent = 0; char *end_ptr = NULL; int64_t limit = 0; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) { gf_log("cli", GF_LOG_ERROR, "dict_new failed"); goto out; } if (wordcount < 4) { if ((wordcount == 3) && !(strcmp(words[2], "help"))) { ret = 1; } goto out; } volname = (char *)words[2]; if (!volname) { ret = -1; goto out; } /* Validate the volume name here itself */ if (cli_validate_volname(volname) < 0) goto out; ret = dict_set_str(dict, "volname", volname); if (ret < 0) goto out; w = str_getunamb(words[3], opwords); if (!w) { cli_out("Invalid quota option : %s", words[3]); ret = -1; goto out; } if (strcmp(w, "enable") == 0) { if (wordcount == 4) { type = GF_QUOTA_OPTION_TYPE_ENABLE; ret = 0; goto set_type; } else { ret = -1; goto out; } } if (strcmp(w, "disable") == 0) { if (wordcount == 4) { type = GF_QUOTA_OPTION_TYPE_DISABLE; ret = 0; goto set_type; } else { ret = -1; goto out; } } if (strcmp(w, "limit-usage") == 0) { type = GF_QUOTA_OPTION_TYPE_LIMIT_USAGE; } else if (strcmp(w, "limit-objects") == 0) { type = GF_QUOTA_OPTION_TYPE_LIMIT_OBJECTS; } if (type == GF_QUOTA_OPTION_TYPE_LIMIT_USAGE || type == GF_QUOTA_OPTION_TYPE_LIMIT_OBJECTS) { if (wordcount < 6 || wordcount > 7) { ret = -1; goto out; } if (words[4][0] != '/') { cli_err("Please enter absolute path"); ret = -1; goto out; } ret = dict_set_str(dict, "path", (char *)words[4]); if (ret) goto out; if (!words[5]) { cli_err("Please enter the limit value to be set"); ret = -1; goto out; } if (type == GF_QUOTA_OPTION_TYPE_LIMIT_USAGE) { ret = gf_string2bytesize_int64(words[5], &value); if (ret != 0 || value <= 0) { if (errno == ERANGE || value <= 0) { ret = -1; cli_err( "Please enter an integer " "value in the range of " "(1 - %" PRId64 ")", INT64_MAX); } else cli_err( "Please enter a correct " "value"); goto out; } } else { errno = 0; limit = strtol(words[5], &end_ptr, 10); if (errno == ERANGE || errno == EINVAL || limit <= 0 || strcmp(end_ptr, "") != 0) { ret = -1; cli_err( "Please enter an integer value in " "the range 1 - %" PRId64, INT64_MAX); goto out; } } ret = dict_set_str(dict, "hard-limit", (char *)words[5]); if (ret < 0) goto out; if (wordcount == 7) { ret = gf_string2percent(words[6], &percent); if (ret != 0 || percent > 100) { ret = -1; cli_err( "Please enter a correct value " "in the range of 0 to 100"); goto out; } ret = dict_set_str(dict, "soft-limit", (char *)words[6]); if (ret < 0) goto out; } goto set_type; } if (strcmp(w, "remove") == 0) { if (wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_REMOVE; if (words[4][0] != '/') { cli_err("Please enter absolute path"); ret = -1; goto out; } ret = dict_set_str(dict, "path", (char *)words[4]); if (ret < 0) goto out; goto set_type; } if (strcmp(w, "remove-objects") == 0) { if (wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_REMOVE_OBJECTS; if (words[4][0] != '/') { cli_err("Please enter absolute path"); ret = -1; goto out; } ret = dict_set_str(dict, "path", (char *)words[4]); if (ret < 0) goto out; goto set_type; } if (strcmp(w, "list") == 0) { type = GF_QUOTA_OPTION_TYPE_LIST; if (words[4] && words[4][0] != '/') { cli_err("Please enter absolute path"); ret = -1; goto out; } i = 4; while (i < wordcount) { snprintf(key, 20, "path%d", i - 4); ret = dict_set_str(dict, key, (char *)words[i++]); if (ret < 0) goto out; } ret = dict_set_int32(dict, "count", i - 4); if (ret < 0) goto out; goto set_type; } if (strcmp(w, "list-objects") == 0) { type = GF_QUOTA_OPTION_TYPE_LIST_OBJECTS; i = 4; while (i < wordcount) { snprintf(key, 20, "path%d", i - 4); ret = dict_set_str(dict, key, (char *)words[i++]); if (ret < 0) { gf_log("cli", GF_LOG_ERROR, "Failed to set " "quota patch in request dictionary"); goto out; } } ret = dict_set_int32(dict, "count", i - 4); if (ret < 0) { gf_log("cli", GF_LOG_ERROR, "Failed to set quota " "limit count in request dictionary"); goto out; } goto set_type; } if (strcmp(w, "alert-time") == 0) { if (wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_ALERT_TIME; ret = gf_string2time(words[4], &time); if (ret) { cli_err( "Invalid argument %s. Please enter a valid " "string", words[4]); goto out; } ret = dict_set_str(dict, "value", (char *)words[4]); if (ret < 0) goto out; goto set_type; } if (strcmp(w, "soft-timeout") == 0) { if (wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_SOFT_TIMEOUT; ret = gf_string2time(words[4], &time); if (ret) { cli_err( "Invalid argument %s. Please enter a valid " "string", words[4]); goto out; } ret = dict_set_str(dict, "value", (char *)words[4]); if (ret < 0) goto out; goto set_type; } if (strcmp(w, "hard-timeout") == 0) { if (wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_HARD_TIMEOUT; ret = gf_string2time(words[4], &time); if (ret) { cli_err( "Invalid argument %s. Please enter a valid " "string", words[4]); goto out; } ret = dict_set_str(dict, "value", (char *)words[4]); if (ret < 0) goto out; goto set_type; } if (strcmp(w, "default-soft-limit") == 0) { if (wordcount != 5) { ret = -1; goto out; } type = GF_QUOTA_OPTION_TYPE_DEFAULT_SOFT_LIMIT; ret = dict_set_str(dict, "value", (char *)words[4]); if (ret < 0) goto out; goto set_type; } else { GF_ASSERT(!"opword mismatch"); } set_type: ret = dict_set_int32(dict, "type", type); if (ret < 0) goto out; *options = dict; out: if (ret < 0) { if (dict) dict_unref(dict); } return ret; } static gf_boolean_t cli_is_key_spl(char *key) { return (strcmp(key, "group") == 0); } static int32_t cli_add_key_group_value(dict_t *dict, const char *name, const char *value, int32_t id, char **op_errstr) { char *key = NULL; char *data = NULL; int32_t ret = -1; ret = gf_asprintf(&key, "%s%d", name, id); if (ret < 0) { goto out; } data = gf_strdup(value); if (data == NULL) { gf_log(THIS->name, GF_LOG_ERROR, "Failed to allocate memory for data"); ret = -1; goto out; } ret = dict_set_dynstr(dict, key, data); if (ret == 0) { data = NULL; } out: GF_FREE(key); GF_FREE(data); if ((ret != 0) && (op_errstr != NULL)) { *op_errstr = gf_strdup("Failed to allocate memory"); } return ret; } static int cli_add_key_group(dict_t *dict, char *key, char *value, char **op_errstr) { int ret = -1; int opt_count = 0; char *saveptr = NULL; char *tok_key = NULL; char *tok_val = NULL; char *tagpath = NULL; char line[PATH_MAX + 256] = { 0, }; FILE *fp = NULL; ret = gf_asprintf(&tagpath, "%s/groups/%s", GLUSTERD_DEFAULT_WORKDIR, value); if (ret == -1) { tagpath = NULL; goto out; } fp = fopen(tagpath, "r"); if (!fp) { ret = -1; if (op_errstr) { gf_asprintf(op_errstr, "Unable to open file '%s'. " "Error: %s", tagpath, strerror(errno)); } goto out; } opt_count = 0; while (fgets(line, sizeof(line), fp) != NULL) { if (strlen(line) >= sizeof(line) - 1) { ret = -1; if (op_errstr != NULL) { *op_errstr = gf_strdup("Line too long"); } goto out; } /* Treat line that start with "#" as comments */ if ('#' == line[0]) continue; opt_count++; tok_key = strtok_r(line, "=", &saveptr); tok_val = strtok_r(NULL, "\r\n", &saveptr); if (!tok_key || !tok_val) { ret = -1; if (op_errstr) { gf_asprintf(op_errstr, "'%s' file format " "not valid.", tagpath); } goto out; } ret = cli_add_key_group_value(dict, "key", tok_key, opt_count, op_errstr); if (ret != 0) { goto out; } ret = cli_add_key_group_value(dict, "value", tok_val, opt_count, op_errstr); if (ret != 0) { goto out; } } if (!opt_count) { ret = -1; if (op_errstr) { gf_asprintf(op_errstr, "'%s' file format not valid.", tagpath); } goto out; } ret = dict_set_int32(dict, "count", opt_count); out: GF_FREE(tagpath); if (fp) fclose(fp); return ret; } /* Strips all whitespace characters in a string and returns length of new string * on success */ static int gf_strip_whitespace(char *str, int len) { int i = 0; int new_len = 0; char *new_str = NULL; GF_ASSERT(str); new_str = GF_MALLOC(len + 1, gf_common_mt_char); if (new_str == NULL) return -1; for (i = 0; i < len; i++) { if (!isspace(str[i])) new_str[new_len++] = str[i]; } new_str[new_len] = '\0'; if (new_len != len) { snprintf(str, new_len + 1, "%s", new_str); } GF_FREE(new_str); return new_len; } int32_t cli_cmd_volume_set_parse(struct cli_state *state, const char **words, int wordcount, dict_t **options, char **op_errstr) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; int count = 0; char *key = NULL; char *value = NULL; int i = 0; char str[50] = { 0, }; const char *question = NULL; gf_answer_t answer = GF_ANSWER_NO; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; if (wordcount < 3) goto out; volname = (char *)words[2]; GF_ASSERT(volname); ret = dict_set_str(dict, "volname", volname); if (ret) goto out; if (!strcmp(volname, "all")) { ret = dict_set_str(dict, "globalname", "All"); if (ret) { gf_log(THIS->name, GF_LOG_ERROR, "dict set on global key failed."); goto out; } ret = dict_set_int32(dict, "hold_global_locks", _gf_true); if (ret) { gf_log(THIS->name, GF_LOG_ERROR, "dict set on global key failed."); goto out; } } if ((!strcmp(volname, "help") || !strcmp(volname, "help-xml")) && wordcount == 3) { ret = dict_set_str(dict, volname, volname); if (ret) goto out; } else if (wordcount < 5) { ret = -1; goto out; } else if (wordcount == 5 && cli_is_key_spl((char *)words[3])) { key = (char *)words[3]; value = (char *)words[4]; if (!key || !value) { ret = -1; goto out; } ret = gf_strip_whitespace(value, strlen(value)); if (ret == -1) goto out; if (strlen(value) == 0) { ret = -1; goto out; } ret = cli_add_key_group(dict, key, value, op_errstr); if (ret == 0) *options = dict; goto out; } for (i = 3; i < wordcount; i += 2) { key = (char *)words[i]; value = (char *)words[i + 1]; if (!key || !value) { ret = -1; goto out; } count++; if (fnmatch("user.*", key, FNM_NOESCAPE) != 0) { ret = gf_strip_whitespace(value, strlen(value)); if (ret == -1) goto out; } if (strlen(value) == 0) { ret = -1; goto out; } if (cli_is_key_spl(key)) { ret = -1; goto out; } sprintf(str, "key%d", count); ret = dict_set_str(dict, str, key); if (ret) goto out; sprintf(str, "value%d", count); ret = dict_set_str(dict, str, value); if (ret) goto out; if ((!strcmp(key, "cluster.enable-shared-storage")) && (!strcmp(value, "disable"))) { question = "Disabling cluster.enable-shared-storage " "will delete the shared storage volume" "(gluster_shared_storage), which is used " "by snapshot scheduler, geo-replication " "and NFS-Ganesha. Do you still want to " "continue?"; answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { gf_log("cli", GF_LOG_ERROR, "Operation " "cancelled, exiting"); *op_errstr = gf_strdup("Aborted by user."); ret = -1; goto out; } } if ((!strcmp(key, "nfs.disable")) && (!strcmp(value, "off"))) { question = "Gluster NFS is being deprecated in favor " "of NFS-Ganesha Enter \"yes\" to continue " "using Gluster NFS"; answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { gf_log("cli", GF_LOG_ERROR, "Operation " "cancelled, exiting"); *op_errstr = gf_strdup("Aborted by user."); ret = -1; goto out; } } } ret = dict_set_int32(dict, "count", wordcount - 3); if (ret) goto out; *options = dict; out: if (ret && dict) dict_unref(dict); return ret; } int32_t cli_cmd_volume_add_brick_parse(struct cli_state *state, const char **words, int wordcount, dict_t **options, int *ret_type) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; int brick_count = 0, brick_index = 0; char *bricks = NULL; static char *opwords_cl[] = {"replica", NULL}; gf1_cluster_type type = GF_CLUSTER_TYPE_NONE; int count = 1; int arbiter_count = 0; char *w = NULL; int index; gf_boolean_t is_force = _gf_false; int wc = wordcount; gf_answer_t answer = GF_ANSWER_NO; const char *question = NULL; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; if (wordcount < 3) goto out; volname = (char *)words[2]; GF_ASSERT(volname); ret = dict_set_str(dict, "volname", volname); if (ret) goto out; if (wordcount < 4) { ret = -1; goto out; } if (wordcount < 6) { /* seems no options are given, go directly to the parse_brick */ brick_index = 3; type = GF_CLUSTER_TYPE_NONE; goto parse_bricks; } w = str_getunamb(words[3], opwords_cl); if (!w) { type = GF_CLUSTER_TYPE_NONE; index = 3; } else if ((strcmp(w, "replica")) == 0) { type = GF_CLUSTER_TYPE_REPLICATE; count = strtol(words[4], NULL, 0); if (!count || (count < 2)) { cli_err("replica count should be greater than 1"); ret = -1; goto out; } ret = dict_set_int32(dict, "replica-count", count); if (ret) goto out; index = 5; if (words[index] && !strcmp(words[index], "arbiter")) { arbiter_count = strtol(words[6], NULL, 0); if (arbiter_count != 1 || count != 3) { cli_err( "For arbiter configuration, replica " "count must be 3 and arbiter count " "must be 1. The 3rd brick of the " "replica will be the arbiter"); ret = -1; goto out; } ret = dict_set_int32(dict, "arbiter-count", arbiter_count); if (ret) goto out; index = 7; } if (count == 2) { if (strcmp(words[wordcount - 1], "force")) { question = "Replica 2 volumes are prone to " "split-brain. Use Arbiter or " "Replica 3 to avoid this. See: " "http://docs.gluster.org/en/latest/Administrator-Guide/" "Split-brain-and-ways-to-deal-with-it/." "\nDo you still want to continue?\n"; answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { gf_log("cli", GF_LOG_ERROR, "Add brick" " cancelled, exiting"); ret = -1; goto out; } } } } else { GF_ASSERT(!"opword mismatch"); ret = -1; goto out; } brick_index = index; parse_bricks: if (strcmp(words[wordcount - 1], "force") == 0) { is_force = _gf_true; wc = wordcount - 1; } ret = cli_cmd_bricks_parse(words, wc, brick_index, &bricks, &brick_count); if (ret) goto out; ret = dict_set_dynstr(dict, "bricks", bricks); if (ret) goto out; ret = dict_set_int32(dict, "count", brick_count); if (ret) goto out; ret = dict_set_int32(dict, "force", is_force); if (ret) goto out; *options = dict; out: if (ret_type) *ret_type = type; if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to parse add-brick CLI"); if (dict) dict_unref(dict); } return ret; } int32_t cli_cmd_volume_remove_brick_parse(struct cli_state *state, const char **words, int wordcount, dict_t **options, int *question, int *brick_count, int32_t *comm) { dict_t *dict = NULL; char *volname = NULL; char *delimiter = NULL; int ret = -1; char key[50]; int brick_index = 0; int32_t tmp_index = 0; int32_t j = 0; char *tmp_brick = NULL; char *tmp_brick1 = NULL; static char *type_opword[] = {"replica", NULL}; static char *opwords[] = {"start", "commit", "stop", "status", "force", NULL}; char *w = NULL; int32_t command = GF_OP_CMD_NONE; long count = 0; gf_answer_t answer = GF_ANSWER_NO; const char *ques = NULL; GF_ASSERT(words); GF_ASSERT(options); if (wordcount < 5) goto out; dict = dict_new(); if (!dict) goto out; volname = (char *)words[2]; GF_ASSERT(volname); ret = dict_set_str(dict, "volname", volname); if (ret) goto out; brick_index = 3; w = str_getunamb(words[3], type_opword); if (w && !strcmp("replica", w)) { if (wordcount < 6) { ret = -1; goto out; } count = strtol(words[4], NULL, 0); if (count < 1) { cli_err( "replica count should be greater than 0 in " "case of remove-brick"); ret = -1; goto out; } if (count == 2) { if (strcmp(words[wordcount - 1], "force")) { ques = "Replica 2 volumes are prone to " "split-brain. Use Arbiter or Replica 3 " "to avoid this. See: " "http://docs.gluster.org/en/latest/Administrator-Guide/" "Split-brain-and-ways-to-deal-with-it/." "\nDo you still want to continue?\n"; answer = cli_cmd_get_confirmation(state, ques); if (GF_ANSWER_NO == answer) { gf_log("cli", GF_LOG_ERROR, "Remove " "brick cancelled, exiting"); ret = -1; goto out; } } } ret = dict_set_int32(dict, "replica-count", count); if (ret) goto out; brick_index = 5; } else if (w) { GF_ASSERT(!"opword mismatch"); } w = str_getunamb(words[wordcount - 1], opwords); if (!w) { ret = -1; goto out; } else { /* handled this option */ wordcount--; if (!strcmp("start", w)) { command = GF_OP_CMD_START; if (question) *question = 1; } else if (!strcmp("commit", w)) { command = GF_OP_CMD_COMMIT; } else if (!strcmp("stop", w)) { command = GF_OP_CMD_STOP; } else if (!strcmp("status", w)) { command = GF_OP_CMD_STATUS; } else if (!strcmp("force", w)) { command = GF_OP_CMD_COMMIT_FORCE; if (question) *question = 1; } else { GF_ASSERT(!"opword mismatch"); ret = -1; goto out; } } ret = dict_set_int32(dict, "command", command); if (ret) gf_log("cli", GF_LOG_INFO, "failed to set 'command' %d", command); tmp_index = brick_index; tmp_brick = GF_MALLOC(2048 * sizeof(*tmp_brick), gf_common_mt_char); if (!tmp_brick) { gf_log("", GF_LOG_ERROR, "cli_cmd_volume_remove_brick_parse: " "Unable to get memory"); ret = -1; goto out; } tmp_brick1 = GF_MALLOC(2048 * sizeof(*tmp_brick1), gf_common_mt_char); if (!tmp_brick1) { gf_log("", GF_LOG_ERROR, "cli_cmd_volume_remove_brick_parse: " "Unable to get memory"); ret = -1; goto out; } while (brick_index < wordcount) { if (validate_brick_name((char *)words[brick_index])) { cli_err( "wrong brick type: %s, use :" "", words[brick_index]); ret = -1; goto out; } else { delimiter = strrchr(words[brick_index], ':'); ret = gf_canonicalize_path(delimiter + 1); if (ret) goto out; } j = tmp_index; strcpy(tmp_brick, words[brick_index]); while (j < brick_index) { strcpy(tmp_brick1, words[j]); if (!(strcmp(tmp_brick, tmp_brick1))) { gf_log("", GF_LOG_ERROR, "Duplicate bricks" " found %s", words[brick_index]); cli_err("Duplicate bricks found %s", words[brick_index]); ret = -1; goto out; } j++; } snprintf(key, 50, "brick%d", ++(*brick_count)); ret = dict_set_str(dict, key, (char *)words[brick_index++]); if (ret) goto out; } if (command != GF_OP_CMD_STATUS && command != GF_OP_CMD_STOP) { ret = dict_set_int32(dict, "count", *brick_count); if (ret) goto out; } *options = dict; out: if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to parse remove-brick CLI"); if (dict) dict_unref(dict); } GF_FREE(tmp_brick); GF_FREE(tmp_brick1); *comm = command; return ret; } int32_t cli_cmd_brick_op_validate_bricks(const char **words, dict_t *dict, int src, int dst) { int ret = -1; char *delimiter = NULL; if (validate_brick_name((char *)words[src])) { cli_err( "wrong brick type: %s, use " ":", words[3]); ret = -1; goto out; } else { delimiter = strrchr((char *)words[src], '/'); ret = gf_canonicalize_path(delimiter); if (ret) goto out; } ret = dict_set_str(dict, "src-brick", (char *)words[src]); if (ret) goto out; if (dst == -1) { ret = 0; goto out; } if (validate_brick_name((char *)words[dst])) { cli_err( "wrong brick type: %s, use " ":", words[dst]); ret = -1; goto out; } else { delimiter = strrchr((char *)words[dst], '/'); ret = gf_canonicalize_path(delimiter); if (ret) goto out; } ret = dict_set_str(dict, "dst-brick", (char *)words[dst]); if (ret) goto out; ret = 0; out: return ret; } int32_t cli_cmd_volume_reset_brick_parse(const char **words, int wordcount, dict_t **options) { int ret = -1; char *volname = NULL; dict_t *dict = NULL; if (wordcount < 5 || wordcount > 7) goto out; dict = dict_new(); if (!dict) goto out; volname = (char *)words[2]; ret = dict_set_str(dict, "volname", volname); if (ret) goto out; if (wordcount == 5) { if (strcmp(words[4], "start")) { cli_err( "Invalid option '%s' for reset-brick. Please " "enter valid reset-brick command", words[4]); ret = -1; goto out; } ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, -1); if (ret) goto out; ret = dict_set_str(dict, "operation", "GF_RESET_OP_START"); if (ret) goto out; } else if (wordcount == 6) { if (strcmp(words[5], "commit")) { cli_err( "Invalid option '%s' for reset-brick. Please " "enter valid reset-brick command", words[5]); ret = -1; goto out; } ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, 4); if (ret) goto out; ret = dict_set_str(dict, "operation", "GF_RESET_OP_COMMIT"); if (ret) goto out; } else if (wordcount == 7) { if (strcmp(words[5], "commit") || strcmp(words[6], "force")) { cli_err( "Invalid option '%s %s' for reset-brick. Please " "enter valid reset-brick command", words[5], words[6]); ret = -1; goto out; } ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, 4); if (ret) goto out; ret = dict_set_str(dict, "operation", "GF_RESET_OP_COMMIT_FORCE"); if (ret) goto out; } *options = dict; out: if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to parse reset-brick CLI"); if (dict) dict_unref(dict); } return ret; } int32_t cli_cmd_volume_replace_brick_parse(const char **words, int wordcount, dict_t **options) { int ret = -1; char *volname = NULL; dict_t *dict = NULL; GF_ASSERT(words); GF_ASSERT(options); if (wordcount != 7) { ret = -1; goto out; } dict = dict_new(); if (!dict) { gf_log("cli", GF_LOG_ERROR, "Failed to allocate dictionary"); goto out; } volname = (char *)words[2]; GF_ASSERT(volname); ret = dict_set_str(dict, "volname", volname); if (ret) goto out; ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, 4); if (ret) goto out; /* commit force option */ if (strcmp("commit", words[5]) || strcmp("force", words[6])) { cli_err( "Invalid option '%s' '%s' for replace-brick. Please " "enter valid replace-brick command", words[5], words[6]); ret = -1; goto out; } ret = dict_set_str(dict, "operation", "GF_REPLACE_OP_COMMIT_FORCE"); if (ret) goto out; *options = dict; out: if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to parse reset-brick CLI"); if (dict) dict_unref(dict); } return ret; } int32_t cli_cmd_log_rotate_parse(const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; char *str = NULL; int ret = -1; char *delimiter = NULL; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; if (strcmp("rotate", words[3]) == 0) volname = (char *)words[2]; GF_ASSERT(volname); ret = dict_set_str(dict, "volname", volname); if (ret) goto out; if (words[4]) { delimiter = strchr(words[4], ':'); if (!delimiter || delimiter == words[4] || *(delimiter + 1) != '/') { cli_err( "wrong brick type: %s, use :" "", words[4]); ret = -1; goto out; } else { ret = gf_canonicalize_path(delimiter + 1); if (ret) goto out; } str = (char *)words[4]; ret = dict_set_str(dict, "brick", str); if (ret) goto out; } *options = dict; out: if (ret && dict) dict_unref(dict); return ret; } static gf_boolean_t gsyncd_url_check(const char *w) { return !!strpbrk(w, ":/"); } static gf_boolean_t valid_secondary_gsyncd_url(const char *w) { if (strstr(w, ":::")) return _gf_false; else if (strstr(w, "::")) return _gf_true; else return _gf_false; } static gf_boolean_t gsyncd_glob_check(const char *w) { return !!strpbrk(w, "*?["); } static int config_parse(const char **words, int wordcount, dict_t *dict, unsigned cmdi, unsigned glob) { int32_t ret = -1; int32_t i = -1; char *append_str = NULL; size_t append_len = 0; char *subop = NULL; char *ret_chkpt = NULL; struct tm checkpoint_time; char chkpt_buf[20] = ""; switch ((wordcount - 1) - cmdi) { case 0: subop = gf_strdup("get-all"); break; case 1: if (words[cmdi + 1][0] == '!') { (words[cmdi + 1])++; if (gf_asprintf(&subop, "del%s", glob ? "-glob" : "") == -1) subop = NULL; } else subop = gf_strdup("get"); ret = dict_set_str(dict, "op_name", ((char *)words[cmdi + 1])); if (ret < 0) goto out; break; default: if (gf_asprintf(&subop, "set%s", glob ? "-glob" : "") == -1) subop = NULL; ret = dict_set_str(dict, "op_name", ((char *)words[cmdi + 1])); if (ret < 0) goto out; /* join the varargs by spaces to get the op_value */ for (i = cmdi + 2; i < wordcount; i++) append_len += (strlen(words[i]) + 1); /* trailing strcat will add two bytes, make space for that */ append_len++; /* strcat is used on this allocation and hence expected to be * initiatlized to 0. So GF_CALLOC is used. */ append_str = GF_CALLOC(1, append_len, cli_mt_append_str); if (!append_str) { ret = -1; goto out; } for (i = cmdi + 2; i < wordcount; i++) { strcat(append_str, words[i]); strcat(append_str, " "); } append_str[append_len - 2] = '\0'; /* "checkpoint now" is special: we resolve that "now" */ if ((strcmp(words[cmdi + 1], "checkpoint") == 0) && (strcmp(append_str, "now") == 0)) { struct timeval tv = { 0, }; ret = gettimeofday(&tv, NULL); if (ret == -1) goto out; GF_FREE(append_str); append_str = GF_MALLOC(300, cli_mt_append_str); if (!append_str) { ret = -1; goto out; } snprintf(append_str, 300, "%" GF_PRI_SECOND, tv.tv_sec); } else if ((strcmp(words[cmdi + 1], "checkpoint") == 0) && (strcmp(append_str, "now") != 0)) { memset(&checkpoint_time, 0, sizeof(struct tm)); ret_chkpt = strptime(append_str, "%Y-%m-%d %H:%M:%S", &checkpoint_time); if (ret_chkpt == NULL || *ret_chkpt != '\0') { ret = -1; cli_err( "Invalid Checkpoint label. Use format " "\"Y-m-d H:M:S\", Example: 2016-10-25 15:30:45"); goto out; } GF_FREE(append_str); append_str = GF_MALLOC(300, cli_mt_append_str); if (!append_str) { ret = -1; goto out; } strftime(chkpt_buf, sizeof(chkpt_buf), "%s", &checkpoint_time); snprintf(append_str, 300, "%s", chkpt_buf); } ret = dict_set_dynstr(dict, "op_value", append_str); if (ret != 0) { goto out; } append_str = NULL; } ret = -1; if (subop) { ret = dict_set_dynstr(dict, "subop", subop); if (!ret) subop = NULL; } out: GF_FREE(append_str); GF_FREE(subop); gf_log("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; } /* ssh_port_parse: Parses and validates when ssh_port is given. * ssh_index refers to index of ssh_port and * type refers to either push-pem or no-verify */ static int32_t parse_ssh_port(const char **words, int wordcount, dict_t *dict, unsigned *cmdi, int ssh_index, char *type) { int ret = 0; char *end_ptr = NULL; int64_t limit = 0; if (!strcmp((char *)words[ssh_index], "ssh-port")) { if (strcmp((char *)words[ssh_index - 1], "create")) { ret = -1; goto out; } (*cmdi)++; limit = strtol(words[ssh_index + 1], &end_ptr, 10); if (errno == ERANGE || errno == EINVAL || limit <= 0 || strcmp(end_ptr, "") != 0) { ret = -1; cli_err("Please enter an integer value for ssh_port "); goto out; } ret = dict_set_int32(dict, "ssh_port", limit); if (ret) goto out; (*cmdi)++; } else if (strcmp((char *)words[ssh_index + 1], "create")) { ret = -1; goto out; } ret = dict_set_int32(dict, type, 1); if (ret) goto out; (*cmdi)++; out: return ret; } static int32_t force_push_pem_no_verify_parse(const char **words, int wordcount, dict_t *dict, unsigned *cmdi) { int32_t ret = 0; if (!strcmp((char *)words[wordcount - 1], "force")) { if ((strcmp((char *)words[wordcount - 2], "start")) && (strcmp((char *)words[wordcount - 2], "stop")) && (strcmp((char *)words[wordcount - 2], "create")) && (strcmp((char *)words[wordcount - 2], "no-verify")) && (strcmp((char *)words[wordcount - 2], "push-pem")) && (strcmp((char *)words[wordcount - 2], "pause")) && (strcmp((char *)words[wordcount - 2], "resume"))) { ret = -1; goto out; } ret = dict_set_int32n(dict, "force", SLEN("force"), 1); if (ret) goto out; (*cmdi)++; if (!strcmp((char *)words[wordcount - 2], "push-pem")) { ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 4, "push_pem"); if (ret) goto out; } else if (!strcmp((char *)words[wordcount - 2], "no-verify")) { ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 4, "no_verify"); if (ret) goto out; } } else if (!strcmp((char *)words[wordcount - 1], "push-pem")) { ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 3, "push_pem"); if (ret) goto out; } else if (!strcmp((char *)words[wordcount - 1], "no-verify")) { ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 3, "no_verify"); if (ret) goto out; } out: gf_log("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; } int32_t cli_cmd_gsync_set_parse(struct cli_state *state, const char **words, int wordcount, dict_t **options, char **errstr) { int32_t ret = -1; dict_t *dict = NULL; gf1_cli_gsync_set type = GF_GSYNC_OPTION_TYPE_NONE; int i = 0; unsigned primary_idx = 0; unsigned secondary_idx = 0; unsigned glob = 0; unsigned cmdi = 0; static char *opwords[] = {"create", "status", "start", "stop", "config", "force", "delete", "ssh-port", "no-verify", "push-pem", "detail", "pause", "resume", NULL}; char *w = NULL; char *save_ptr = NULL; char *secondary_temp = NULL; char *token = NULL; gf_answer_t answer = GF_ANSWER_NO; const char *question = NULL; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; /* new syntax: * * volume geo-replication $m $s create [[ssh-port n] [[no-verify] | * [push-pem]]] [force] volume geo-replication [$m [$s]] status [detail] * volume geo-replication [$m] $s config [[!]$opt [$val]] * volume geo-replication $m $s start|stop [force] * volume geo-replication $m $s delete [reset-sync-time] * volume geo-replication $m $s pause [force] * volume geo-replication $m $s resume [force] */ if (wordcount < 3) goto out; for (i = 2; i <= 3 && i < wordcount - 1; i++) { if (gsyncd_glob_check(words[i])) glob = i; if (gsyncd_url_check(words[i])) { secondary_idx = i; break; } } if (glob && !secondary_idx) /* glob is allowed only for config, thus it implies there is a * secondary argument; but that might have not been recognized on * the first scan as it's url characteristics has been covered * by the glob syntax. * * In this case, the secondary is perforce the last glob-word -- the * upcoming one is neither glob, nor url, so it's definitely not * the secondary. */ secondary_idx = glob; if (secondary_idx) { cmdi = secondary_idx + 1; if (secondary_idx == 3) primary_idx = 2; } else if (i <= 4) { if (strtail("detail", (char *)words[wordcount - 1])) { cmdi = wordcount - 2; if (i == 4) primary_idx = 2; } else { /* no $s, can only be status cmd * (with either a single $m before it or nothing) * -- these conditions imply that i <= 3 after * the iteration and that i is the successor of * the (0 or 1 length) sequence of $m-s. */ cmdi = i; if (i == 3) primary_idx = 2; } } else goto out; /* now check if input really complies syntax * (in a somewhat redundant way, in favor * transparent soundness) */ if (primary_idx && gsyncd_url_check(words[primary_idx])) goto out; if (secondary_idx && !glob && !valid_secondary_gsyncd_url(words[secondary_idx])) { gf_asprintf(errstr, "Invalid secondary url: %s", words[secondary_idx]); goto out; } w = str_getunamb(words[cmdi], opwords); if (!w) goto out; if (strcmp(w, "create") == 0) { type = GF_GSYNC_OPTION_TYPE_CREATE; if (!primary_idx || !secondary_idx) goto out; } else if (strcmp(w, "status") == 0) { type = GF_GSYNC_OPTION_TYPE_STATUS; if (secondary_idx && !primary_idx) goto out; } else if (strcmp(w, "config") == 0) { type = GF_GSYNC_OPTION_TYPE_CONFIG; if (!secondary_idx) goto out; } else if (strcmp(w, "start") == 0) { type = GF_GSYNC_OPTION_TYPE_START; if (!primary_idx || !secondary_idx) goto out; } else if (strcmp(w, "stop") == 0) { type = GF_GSYNC_OPTION_TYPE_STOP; if (!primary_idx || !secondary_idx) goto out; } else if (strcmp(w, "delete") == 0) { type = GF_GSYNC_OPTION_TYPE_DELETE; if (!primary_idx || !secondary_idx) goto out; } else if (strcmp(w, "pause") == 0) { type = GF_GSYNC_OPTION_TYPE_PAUSE; if (!primary_idx || !secondary_idx) goto out; } else if (strcmp(w, "resume") == 0) { type = GF_GSYNC_OPTION_TYPE_RESUME; if (!primary_idx || !secondary_idx) goto out; } else GF_ASSERT(!"opword mismatch"); ret = force_push_pem_no_verify_parse(words, wordcount, dict, &cmdi); if (ret) goto out; if (strtail("detail", (char *)words[wordcount - 1])) { if (!strtail("status", (char *)words[wordcount - 2])) { ret = -1; goto out; } ret = dict_set_uint32(dict, "status-detail", _gf_true); if (ret) goto out; cmdi++; } if (type == GF_GSYNC_OPTION_TYPE_DELETE && !strcmp((char *)words[wordcount - 1], "reset-sync-time")) { if (strcmp((char *)words[wordcount - 2], "delete")) { ret = -1; goto out; } ret = dict_set_uint32(dict, "reset-sync-time", _gf_true); if (ret) goto out; cmdi++; } if (type != GF_GSYNC_OPTION_TYPE_CONFIG && (cmdi < wordcount - 1 || glob)) { ret = -1; goto out; } /* If got so far, input is valid, assemble the message */ ret = 0; if (primary_idx) { ret = dict_set_str(dict, "primary", (char *)words[primary_idx]); if (!ret) ret = dict_set_str(dict, "volname", (char *)words[primary_idx]); } if (!ret && secondary_idx) { /* If geo-rep is created with root user using the syntax * gluster vol geo-rep root@ ... * pass down only else pass as it is. */ secondary_temp = gf_strdup(words[secondary_idx]); if (secondary_temp == NULL) { ret = -1; goto out; } token = strtok_r(secondary_temp, "@", &save_ptr); if (token && !strcmp(token, "root")) { ret = dict_set_str(dict, "secondary", (char *)words[secondary_idx] + 5); } else { ret = dict_set_str(dict, "secondary", (char *)words[secondary_idx]); } } if (!ret) ret = dict_set_int32(dict, "type", type); if (!ret && type == GF_GSYNC_OPTION_TYPE_CONFIG) { if (!strcmp((char *)words[wordcount - 2], "ignore-deletes") && !strcmp((char *)words[wordcount - 1], "true")) { question = "There exists ~15 seconds delay for the option to take" " effect from stime of the corresponding brick. Please" " check the log for the time, the option is effective." " Proceed"; answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { gf_log("cli", GF_LOG_INFO, "Operation " "cancelled, exiting"); *errstr = gf_strdup("Aborted by user."); ret = -1; goto out; } } ret = config_parse(words, wordcount, dict, cmdi, glob); } out: if (secondary_temp) GF_FREE(secondary_temp); if (ret && dict) dict_unref(dict); else *options = dict; return ret; } int32_t cli_cmd_volume_profile_parse(const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; int ret = -1; gf1_cli_stats_op op = GF_CLI_STATS_NONE; gf1_cli_info_op info_op = GF_CLI_INFO_NONE; gf_boolean_t is_peek = _gf_false; static char *opwords[] = {"start", "stop", "info", NULL}; char *w = NULL; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; if (wordcount < 4) goto out; volname = (char *)words[2]; ret = dict_set_str(dict, "volname", volname); if (ret) goto out; w = str_getunamb(words[3], opwords); if (!w) { ret = -1; goto out; } if ((strcmp(w, "start") == 0 || strcmp(w, "stop") == 0) && wordcount > 5) { ret = -1; goto out; } if (strcmp(w, "info") == 0 && wordcount > 7) { ret = -1; goto out; } if (strcmp(w, "start") == 0) { op = GF_CLI_STATS_START; } else if (strcmp(w, "stop") == 0) { op = GF_CLI_STATS_STOP; } else if (strcmp(w, "info") == 0) { op = GF_CLI_STATS_INFO; info_op = GF_CLI_INFO_ALL; if (wordcount > 4) { if (strcmp(words[4], "incremental") == 0) { info_op = GF_CLI_INFO_INCREMENTAL; if (wordcount > 5 && strcmp(words[5], "peek") == 0) { is_peek = _gf_true; } } else if (strcmp(words[4], "cumulative") == 0) { info_op = GF_CLI_INFO_CUMULATIVE; } else if (strcmp(words[4], "clear") == 0) { info_op = GF_CLI_INFO_CLEAR; } else if (strcmp(words[4], "peek") == 0) { is_peek = _gf_true; } } } else GF_ASSERT(!"opword mismatch"); ret = dict_set_int32(dict, "op", (int32_t)op); if (ret) goto out; ret = dict_set_int32(dict, "info-op", (int32_t)info_op); if (ret) goto out; ret = dict_set_int32(dict, "peek", is_peek); if (ret) goto out; if (!strcmp(words[wordcount - 1], "nfs")) { ret = dict_set_int32(dict, "nfs", _gf_true); if (ret) goto out; } *options = dict; out: if (ret && dict) dict_unref(dict); return ret; } static int gf_is_str_int(const char *value) { int flag = 0; char *str = NULL; char *fptr = NULL; GF_VALIDATE_OR_GOTO(THIS->name, value, out); str = gf_strdup(value); if (!str) goto out; fptr = str; while (*str) { if (!isdigit(*str)) { flag = 1; goto out; } str++; } out: GF_FREE(fptr); return flag; } int32_t cli_cmd_volume_top_parse(const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; char *volname = NULL; char *value = NULL; char *key = NULL; int ret = -1; gf1_cli_stats_op op = GF_CLI_STATS_NONE; gf1_cli_top_op top_op = GF_CLI_TOP_NONE; int32_t list_cnt = -1; int index = 0; int perf = 0; int32_t blk_size = 0; int count = 0; gf_boolean_t nfs = _gf_false; char *delimiter = NULL; static char *opwords[] = {"open", "read", "write", "opendir", "readdir", "read-perf", "write-perf", "clear", NULL}; char *w = NULL; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; if (wordcount < 4) goto out; volname = (char *)words[2]; ret = dict_set_str(dict, "volname", volname); if (ret) goto out; op = GF_CLI_STATS_TOP; ret = dict_set_int32(dict, "op", (int32_t)op); if (ret) goto out; w = str_getunamb(words[3], opwords); if (!w) { ret = -1; goto out; } if (strcmp(w, "open") == 0) { top_op = GF_CLI_TOP_OPEN; } else if (strcmp(w, "read") == 0) { top_op = GF_CLI_TOP_READ; } else if (strcmp(w, "write") == 0) { top_op = GF_CLI_TOP_WRITE; } else if (strcmp(w, "opendir") == 0) { top_op = GF_CLI_TOP_OPENDIR; } else if (strcmp(w, "readdir") == 0) { top_op = GF_CLI_TOP_READDIR; } else if (strcmp(w, "read-perf") == 0) { top_op = GF_CLI_TOP_READ_PERF; perf = 1; } else if (strcmp(w, "write-perf") == 0) { top_op = GF_CLI_TOP_WRITE_PERF; perf = 1; } else if (strcmp(w, "clear") == 0) { ret = dict_set_int32(dict, "clear-stats", 1); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not set clear-stats in dict"); goto out; } } else GF_ASSERT(!"opword mismatch"); ret = dict_set_int32(dict, "top-op", (int32_t)top_op); if (ret) goto out; if ((wordcount > 4) && !strcmp(words[4], "nfs")) { nfs = _gf_true; ret = dict_set_int32(dict, "nfs", nfs); if (ret) goto out; index = 5; } else { index = 4; } for (; index < wordcount; index += 2) { key = (char *)words[index]; value = (char *)words[index + 1]; if (!key || !value) { ret = -1; goto out; } if (!strcmp(key, "brick")) { delimiter = strchr(value, ':'); if (!delimiter || delimiter == value || *(delimiter + 1) != '/') { cli_err( "wrong brick type: %s, use :" "", value); ret = -1; goto out; } else { ret = gf_canonicalize_path(delimiter + 1); if (ret) goto out; } ret = dict_set_str(dict, "brick", value); } else if (!strcmp(key, "list-cnt")) { ret = gf_is_str_int(value); if (!ret) list_cnt = atoi(value); if (ret || (list_cnt < 0) || (list_cnt > 100)) { cli_err("list-cnt should be between 0 to 100"); ret = -1; goto out; } } else if (perf && !nfs && !strcmp(key, "bs")) { ret = gf_is_str_int(value); if (!ret) blk_size = atoi(value); if (ret || (blk_size <= 0)) { if (blk_size < 0) cli_err( "block size is an invalid" " number"); else cli_err( "block size should be an " "integer greater than zero"); ret = -1; goto out; } ret = dict_set_uint32(dict, "blk-size", (uint32_t)blk_size); } else if (perf && !nfs && !strcmp(key, "count")) { ret = gf_is_str_int(value); if (!ret) count = atoi(value); if (ret || (count <= 0)) { if (count < 0) cli_err("count is an invalid number"); else cli_err( "count should be an integer " "greater than zero"); ret = -1; goto out; } ret = dict_set_uint32(dict, "blk-cnt", count); } else { ret = -1; goto out; } if (ret) { gf_log("", GF_LOG_WARNING, "Dict set failed for " "key %s", key); goto out; } } if (list_cnt == -1) list_cnt = 100; ret = dict_set_int32(dict, "list-cnt", list_cnt); if (ret) { gf_log("", GF_LOG_WARNING, "Dict set failed for list_cnt"); goto out; } if ((blk_size > 0) ^ (count > 0)) { cli_err("Need to give both 'bs' and 'count'"); ret = -1; goto out; } else if (((uint64_t)blk_size * count) > (10 * GF_UNIT_GB)) { cli_err("'bs * count' value %" PRIu64 " is greater than " "maximum allowed value of 10GB", ((uint64_t)blk_size * count)); ret = -1; goto out; } *options = dict; out: if (ret && dict) dict_unref(dict); return ret; } uint32_t cli_cmd_get_statusop(const char *arg) { int i = 0; uint32_t ret = GF_CLI_STATUS_NONE; char *w = NULL; static char *opwords[] = {"detail", "mem", "clients", "fd", "inode", "callpool", "tasks", "client-list", NULL}; static struct { char *opname; uint32_t opcode; } optable[] = {{"detail", GF_CLI_STATUS_DETAIL}, {"mem", GF_CLI_STATUS_MEM}, {"clients", GF_CLI_STATUS_CLIENTS}, {"fd", GF_CLI_STATUS_FD}, {"inode", GF_CLI_STATUS_INODE}, {"callpool", GF_CLI_STATUS_CALLPOOL}, {"tasks", GF_CLI_STATUS_TASKS}, {"client-list", GF_CLI_STATUS_CLIENT_LIST}, {NULL}}; w = str_getunamb(arg, opwords); if (!w) { gf_log("cli", GF_LOG_DEBUG, "Not a status op %s", arg); goto out; } for (i = 0; optable[i].opname; i++) { if (!strcmp(w, optable[i].opname)) { ret = optable[i].opcode; break; } } out: return ret; } int cli_cmd_volume_status_parse(const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; int ret = -1; uint32_t cmd = 0; GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; switch (wordcount) { case 2: cmd = GF_CLI_STATUS_ALL; ret = 0; break; case 3: if (!strcmp(words[2], "all")) { cmd = GF_CLI_STATUS_ALL; ret = 0; } else { cmd = GF_CLI_STATUS_VOL; ret = dict_set_str(dict, "volname", (char *)words[2]); } break; case 4: cmd = cli_cmd_get_statusop(words[3]); if (!strcmp(words[2], "all")) { if (cmd == GF_CLI_STATUS_NONE) { cli_err("%s is not a valid status option", words[3]); ret = -1; goto out; } cmd |= GF_CLI_STATUS_ALL; ret = 0; } else { ret = dict_set_str(dict, "volname", (char *)words[2]); if (ret) goto out; if (cmd == GF_CLI_STATUS_NONE) { if (!strcmp(words[3], "nfs")) { cmd |= GF_CLI_STATUS_NFS; } else if (!strcmp(words[3], "shd")) { cmd |= GF_CLI_STATUS_SHD; } else if (!strcmp(words[3], "quotad")) { cmd |= GF_CLI_STATUS_QUOTAD; } else if (!strcmp(words[3], "snapd")) { cmd |= GF_CLI_STATUS_SNAPD; } else if (!strcmp(words[3], "bitd")) { cmd |= GF_CLI_STATUS_BITD; } else if (!strcmp(words[3], "scrub")) { cmd |= GF_CLI_STATUS_SCRUB; } else { cmd = GF_CLI_STATUS_BRICK; ret = dict_set_str(dict, "brick", (char *)words[3]); } } else { cmd |= GF_CLI_STATUS_VOL; ret = 0; } } break; case 5: if (!strcmp(words[2], "all")) { cli_err("Cannot specify brick/nfs for \"all\""); ret = -1; goto out; } cmd = cli_cmd_get_statusop(words[4]); if (cmd == GF_CLI_STATUS_NONE) { cli_err("%s is not a valid status option", words[4]); ret = -1; goto out; } ret = dict_set_str(dict, "volname", (char *)words[2]); if (ret) goto out; if (!strcmp(words[3], "nfs")) { if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_TASKS) { cli_err( "Detail/FD/Tasks status not available" " for NFS Servers"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_NFS; } else if (!strcmp(words[3], "shd")) { if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS || cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_TASKS) { cli_err( "Detail/FD/Clients/Tasks status not " "available for Self-heal Daemons"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_SHD; } else if (!strcmp(words[3], "quotad")) { if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS || cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_INODE) { cli_err( "Detail/FD/Clients/Inode status not " "available for Quota Daemon"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_QUOTAD; } else if (!strcmp(words[3], "snapd")) { if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS || cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_INODE) { cli_err( "Detail/FD/Clients/Inode status not " "available for snap daemon"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_SNAPD; } else { if (cmd == GF_CLI_STATUS_TASKS) { cli_err( "Tasks status not available for " "bricks"); ret = -1; goto out; } cmd |= GF_CLI_STATUS_BRICK; ret = dict_set_str(dict, "brick", (char *)words[3]); } break; default: goto out; } if (ret) goto out; ret = dict_set_int32(dict, "cmd", cmd); if (ret) goto out; *options = dict; out: if (ret && dict) dict_unref(dict); return ret; } gf_boolean_t cli_cmd_validate_dumpoption(const char *arg, char **option) { static char *opwords[] = {"all", "nfs", "mem", "iobuf", "callpool", "priv", "fd", "inode", "history", "inodectx", "fdctx", "quotad", NULL}; char *w = NULL; w = str_getunamb(arg, opwords); if (!w) { gf_log("cli", GF_LOG_DEBUG, "Unknown statedump option %s", arg); return _gf_false; } *option = w; return _gf_true; } /* Check if the pid is > 0 */ static gf_boolean_t gf_valid_pid(const char *pid, int length) { gf_boolean_t ret = _gf_true; pid_t value = 0; char *end_ptr = NULL; if (length <= 0) { ret = _gf_false; goto out; } value = strtol(pid, &end_ptr, 10); if (value <= 0) { ret = _gf_false; } out: return ret; } int cli_cmd_volume_statedump_options_parse(const char **words, int wordcount, dict_t **options) { int ret = 0; int i = 0; dict_t *dict = NULL; int option_cnt = 0; char *option = NULL; char *option_str = NULL; char *tmp_str = NULL; char *tmp = NULL; char *ip_addr = NULL; char *pid = NULL; if ((wordcount >= 5) && ((strcmp(words[3], "client")) == 0)) { tmp = gf_strdup(words[4]); if (!tmp) { ret = -1; goto out; } ip_addr = strtok(tmp, ":"); pid = strtok(NULL, ":"); if (valid_internet_address(ip_addr, _gf_true, _gf_false) && pid && gf_valid_pid(pid, strlen(pid))) { ret = gf_asprintf(&option_str, "%s %s %s", words[3], ip_addr, pid); if (ret < 0) { goto out; } option_cnt = 3; } else { ret = -1; goto out; } } else { for (i = 3; i < wordcount; i++, option_cnt++) { if (!cli_cmd_validate_dumpoption(words[i], &option)) { ret = -1; goto out; } tmp_str = option_str; option_str = NULL; ret = gf_asprintf(&option_str, "%s%s ", tmp_str ? tmp_str : "", option); GF_FREE(tmp_str); if (ret < 0) { goto out; } } if (option_str && (strstr(option_str, "nfs")) && strstr(option_str, "quotad")) { ret = -1; goto out; } } dict = dict_new(); if (!dict) { ret = -1; goto out; } /* dynamic string in dict is freed up when dict is freed up, and hence if option_str is NULL pass in an duplicate empty string to the same */ ret = dict_set_dynstr(dict, "options", (option_str ? option_str : gf_strdup(""))); if (ret) goto out; option_str = NULL; ret = dict_set_int32(dict, "option_cnt", option_cnt); if (ret) goto out; *options = dict; out: GF_FREE(tmp); GF_FREE(option_str); if (ret && dict) dict_unref(dict); if (ret) gf_log("cli", GF_LOG_ERROR, "Error parsing dumpoptions"); return ret; } int cli_cmd_volume_clrlks_opts_parse(const char **words, int wordcount, dict_t **options) { int ret = -1; int i = 0; dict_t *dict = NULL; char *kind_opts[4] = {"blocked", "granted", "all", NULL}; char *types[4] = {"inode", "entry", "posix", NULL}; char *free_ptr = NULL; dict = dict_new(); if (!dict) goto out; if (strcmp(words[4], "kind")) goto out; for (i = 0; kind_opts[i]; i++) { if (!strcmp(words[5], kind_opts[i])) { free_ptr = gf_strdup(words[5]); ret = dict_set_dynstr(dict, "kind", free_ptr); if (ret) goto out; free_ptr = NULL; break; } } if (i == 3) goto out; ret = -1; for (i = 0; types[i]; i++) { if (!strcmp(words[6], types[i])) { free_ptr = gf_strdup(words[6]); ret = dict_set_dynstr(dict, "type", free_ptr); if (ret) goto out; free_ptr = NULL; break; } } if (i == 3) goto out; if (wordcount == 8) { free_ptr = gf_strdup(words[7]); ret = dict_set_dynstr(dict, "opts", free_ptr); if (ret) goto out; free_ptr = NULL; } ret = 0; *options = dict; out: if (ret) { GF_FREE(free_ptr); dict_unref(dict); } return ret; } static int extract_hostname_path_from_token(const char *tmp_words, char **hostname, char **path) { int ret = 0; char *delimiter = NULL; char *tmp_host = NULL; char *host_name = NULL; char *words = NULL; int str_len = 0; *hostname = NULL; *path = NULL; str_len = strlen(tmp_words) + 1; words = GF_MALLOC(str_len, gf_common_mt_char); if (!words) { ret = -1; goto out; } snprintf(words, str_len, "%s", tmp_words); if (validate_brick_name(words)) { cli_err( "Wrong brick type: %s, use :" "", words); ret = -1; goto out; } else { delimiter = strrchr(words, ':'); ret = gf_canonicalize_path(delimiter + 1); if (ret) { goto out; } else { str_len = strlen(delimiter + 1) + 1; *path = GF_MALLOC(str_len, gf_common_mt_char); if (!*path) { ret = -1; goto out; } snprintf(*path, str_len, "%s", delimiter + 1); } } tmp_host = gf_strdup(words); if (!tmp_host) { gf_log("cli", GF_LOG_ERROR, "Out of memory"); ret = -1; goto out; } get_host_name(tmp_host, &host_name); if (!host_name) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Unable to allocate " "memory"); goto out; } if (!(strcmp(host_name, "localhost") && strcmp(host_name, "127.0.0.1") && strncmp(host_name, "0.", 2))) { cli_err( "Please provide a valid hostname/ip other " "than localhost, 127.0.0.1 or loopback " "address (0.0.0.0 to 0.255.255.255)."); ret = -1; goto out; } if (!valid_internet_address(host_name, _gf_false, _gf_false)) { cli_err( "internet address '%s' does not conform to " "standards", host_name); ret = -1; goto out; } str_len = strlen(host_name) + 1; *hostname = GF_MALLOC(str_len, gf_common_mt_char); if (!*hostname) { ret = -1; goto out; } snprintf(*hostname, str_len, "%s", host_name); ret = 0; out: GF_FREE(words); GF_FREE(tmp_host); return ret; } static int set_hostname_path_in_dict(const char *token, dict_t *dict, int heal_op) { char *hostname = NULL; char *path = NULL; int ret = 0; ret = extract_hostname_path_from_token(token, &hostname, &path); if (ret) goto out; switch (heal_op) { case GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK: ret = dict_set_dynstr(dict, "heal-source-hostname", hostname); if (ret) goto out; hostname = NULL; ret = dict_set_dynstr(dict, "heal-source-brickpath", path); if (ret) { goto out; } path = NULL; break; case GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA: ret = dict_set_dynstr(dict, "per-replica-cmd-hostname", hostname); if (ret) goto out; hostname = NULL; ret = dict_set_dynstr(dict, "per-replica-cmd-path", path); if (ret) { goto out; } path = NULL; break; default: ret = -1; break; } out: GF_FREE(hostname); GF_FREE(path); return ret; } static int heal_command_type_get(const char *command) { int i = 0; /* subcommands are set as NULL */ char *heal_cmds[GF_SHD_OP_HEAL_DISABLE + 1] = { [GF_SHD_OP_INVALID] = NULL, [GF_SHD_OP_HEAL_INDEX] = NULL, [GF_SHD_OP_HEAL_FULL] = "full", [GF_SHD_OP_INDEX_SUMMARY] = "info", [GF_SHD_OP_SPLIT_BRAIN_FILES] = NULL, [GF_SHD_OP_STATISTICS] = "statistics", [GF_SHD_OP_STATISTICS_HEAL_COUNT] = NULL, [GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA] = NULL, [GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE] = NULL, [GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK] = NULL, [GF_SHD_OP_HEAL_ENABLE] = "enable", [GF_SHD_OP_HEAL_DISABLE] = "disable", }; for (i = 0; i <= GF_SHD_OP_HEAL_DISABLE; i++) { if (heal_cmds[i] && (strcmp(heal_cmds[i], command) == 0)) return i; } return GF_SHD_OP_INVALID; } int cli_cmd_volume_heal_options_parse(const char **words, int wordcount, dict_t **options) { int ret = 0; dict_t *dict = NULL; gf_xl_afr_op_t op = GF_SHD_OP_INVALID; dict = dict_new(); if (!dict) { gf_log(THIS->name, GF_LOG_ERROR, "Failed to create the dict"); ret = -1; goto out; } ret = dict_set_str(dict, "volname", (char *)words[2]); if (ret) { gf_log(THIS->name, GF_LOG_ERROR, "failed to set volname"); goto out; } if (wordcount == 3) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_HEAL_INDEX); goto done; } if (wordcount == 4) { op = heal_command_type_get(words[3]); if (op == GF_SHD_OP_INVALID) { ret = -1; goto out; } ret = dict_set_int32(dict, "heal-op", op); goto done; } if (wordcount == 5) { if (strcmp(words[3], "info") && strcmp(words[3], "statistics") && strcmp(words[3], "granular-entry-heal")) { ret = -1; goto out; } if (!strcmp(words[3], "info")) { if (!strcmp(words[4], "split-brain")) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_SPLIT_BRAIN_FILES); goto done; } if (!strcmp(words[4], "summary")) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_HEAL_SUMMARY); goto done; } } if (!strcmp(words[3], "statistics")) { if (!strcmp(words[4], "heal-count")) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_STATISTICS_HEAL_COUNT); goto done; } } if (!strcmp(words[3], "granular-entry-heal")) { if (!strcmp(words[4], "enable")) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_GRANULAR_ENTRY_HEAL_ENABLE); goto done; } else if (!strcmp(words[4], "disable")) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_GRANULAR_ENTRY_HEAL_DISABLE); goto done; } } ret = -1; goto out; } if (wordcount == 6) { if (strcmp(words[3], "split-brain")) { ret = -1; goto out; } if (!strcmp(words[4], "bigger-file")) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE); if (ret) goto out; ret = dict_set_str(dict, "file", (char *)words[5]); if (ret) goto out; goto done; } if (!strcmp(words[4], "latest-mtime")) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_SBRAIN_HEAL_FROM_LATEST_MTIME); if (ret) goto out; ret = dict_set_str(dict, "file", (char *)words[5]); if (ret) goto out; goto done; } if (!strcmp(words[4], "source-brick")) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK); if (ret) goto out; ret = set_hostname_path_in_dict(words[5], dict, GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK); if (ret) goto out; goto done; } ret = -1; goto out; } if (wordcount == 7) { if (!strcmp(words[3], "statistics") && !strcmp(words[4], "heal-count") && !strcmp(words[5], "replica")) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA); if (ret) goto out; ret = set_hostname_path_in_dict( words[6], dict, GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA); if (ret) goto out; goto done; } if (!strcmp(words[3], "split-brain") && !strcmp(words[4], "source-brick")) { ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK); ret = set_hostname_path_in_dict(words[5], dict, GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK); if (ret) goto out; ret = dict_set_str(dict, "file", (char *)words[6]); if (ret) goto out; goto done; } } ret = -1; goto out; done: *options = dict; out: if (ret && dict) { dict_unref(dict); *options = NULL; } return ret; } int cli_cmd_volume_defrag_parse(const char **words, int wordcount, dict_t **options) { dict_t *dict = NULL; int ret = -1; char *option = NULL; char *volname = NULL; char *command = NULL; gf_defrag_type_t cmd = 0; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; if (!((wordcount == 4) || (wordcount == 5))) goto out; if (wordcount == 4) { if (strcmp(words[3], "start") && strcmp(words[3], "stop") && strcmp(words[3], "status")) goto out; } else { if (strcmp(words[3], "fix-layout") && strcmp(words[3], "start")) goto out; } volname = (char *)words[2]; if (wordcount == 4) { command = (char *)words[3]; } if (wordcount == 5) { if ((strcmp(words[3], "fix-layout") || strcmp(words[4], "start")) && (strcmp(words[3], "start") || strcmp(words[4], "force"))) { ret = -1; goto out; } command = (char *)words[3]; option = (char *)words[4]; } if (strcmp(command, "start") == 0) { cmd = GF_DEFRAG_CMD_START; if (option && strcmp(option, "force") == 0) { cmd = GF_DEFRAG_CMD_START_FORCE; } goto done; } if (strcmp(command, "fix-layout") == 0) { cmd = GF_DEFRAG_CMD_START_LAYOUT_FIX; goto done; } if (strcmp(command, "stop") == 0) { cmd = GF_DEFRAG_CMD_STOP; goto done; } if (strcmp(command, "status") == 0) { cmd = GF_DEFRAG_CMD_STATUS; } done: ret = dict_set_str(dict, "volname", volname); if (ret) { gf_log(THIS->name, GF_LOG_ERROR, "failed to set dict"); goto out; } ret = dict_set_int32(dict, "rebalance-command", (int32_t)cmd); if (ret) { gf_log(THIS->name, GF_LOG_ERROR, "failed to set dict"); goto out; } *options = dict; out: if (ret && dict) dict_unref(dict); return ret; } int32_t cli_snap_create_desc_parse(dict_t *dict, const char **words, size_t wordcount, int32_t desc_opt_loc) { int32_t ret = -1; char *desc = NULL; int32_t desc_len = 0; int len; desc = GF_MALLOC(MAX_SNAP_DESCRIPTION_LEN + 1, gf_common_mt_char); if (!desc) { ret = -1; goto out; } len = strlen(words[desc_opt_loc]); if (len >= MAX_SNAP_DESCRIPTION_LEN) { cli_out( "snapshot create: description truncated: " "Description provided is longer than 1024 characters"); desc_len = MAX_SNAP_DESCRIPTION_LEN; } else { desc_len = len; } snprintf(desc, desc_len + 1, "%s", words[desc_opt_loc]); /* Calculating the size of the description as given by the user */ ret = dict_set_dynstr(dict, "description", desc); if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to save snap " "description"); goto out; } ret = 0; out: if (ret && desc) GF_FREE(desc); return ret; } /* snapshot clone * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_clone_parse(dict_t *dict, const char **words, int wordcount) { uint64_t i = 0; int ret = -1; char *clonename = NULL; unsigned int cmdi = 2; /* cmdi is command index, here cmdi is "2" (gluster snapshot clone)*/ GF_ASSERT(words); GF_ASSERT(dict); if (wordcount == cmdi + 1) { cli_err("Invalid Syntax."); gf_log("cli", GF_LOG_ERROR, "Invalid number of words for snap clone command"); goto out; } if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) { cli_err( "snapshot clone: failed: clonename cannot exceed " "255 characters."); gf_log("cli", GF_LOG_ERROR, "Clone name too long"); goto out; } clonename = (char *)words[cmdi]; for (i = 0; i < strlen(clonename); i++) { /* Following volume name convention */ if (!isalnum(clonename[i]) && (clonename[i] != '_' && (clonename[i] != '-'))) { /* TODO : Is this message enough?? */ cli_err( "Clonename can contain only alphanumeric, " "\"-\" and \"_\" characters"); goto out; } } ret = dict_set_int32(dict, "volcount", 1); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save volcount"); goto out; } ret = dict_set_str(dict, "clonename", (char *)words[cmdi]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save clone " "name(%s)", (char *)words[cmdi]); goto out; } /* Filling snap name in the dictionary */ ret = dict_set_str(dict, "snapname", (char *)words[cmdi + 1]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not " "save snap name(%s)", (char *)words[cmdi + 1]); goto out; } ret = 0; out: return ret; } /* snapshot create [description ] * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_create_parse(dict_t *dict, const char **words, int wordcount) { uint64_t i = 0; int ret = -1; uint64_t volcount = 0; char key[PATH_MAX] = ""; char *snapname = NULL; unsigned int cmdi = 2; int flags = 0; /* cmdi is command index, here cmdi is "2" (gluster snapshot create)*/ GF_ASSERT(words); GF_ASSERT(dict); if (wordcount <= cmdi + 1) { cli_err("Invalid Syntax."); gf_log("cli", GF_LOG_ERROR, "Too less words for snap create command"); goto out; } if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) { cli_err( "snapshot create: failed: snapname cannot exceed " "255 characters."); gf_log("cli", GF_LOG_ERROR, "Snapname too long"); goto out; } snapname = (char *)words[cmdi]; for (i = 0; i < strlen(snapname); i++) { /* Following volume name convention */ if (!isalnum(snapname[i]) && (snapname[i] != '_' && (snapname[i] != '-'))) { /* TODO : Is this message enough?? */ cli_err( "Snapname can contain only alphanumeric, " "\"-\" and \"_\" characters"); goto out; } } ret = dict_set_str(dict, "snapname", (char *)words[cmdi]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save snap " "name(%s)", (char *)words[cmdi]); goto out; } /* Filling volume name in the dictionary */ for (i = cmdi + 1; i < wordcount && (strcmp(words[i], "description")) != 0 && (strcmp(words[i], "force") != 0) && (strcmp(words[i], "no-timestamp") != 0); i++) { volcount++; /* volume index starts from 1 */ ret = snprintf(key, sizeof(key), "volname%" PRIu64, volcount); if (ret < 0) { goto out; } ret = dict_set_str(dict, key, (char *)words[i]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not " "save volume name(%s)", (char *)words[i]); goto out; } if (i >= cmdi + 2) { ret = -1; cli_err( "Creating multiple volume snapshot is not " "supported as of now"); goto out; } /* TODO : remove this above condition check once * multiple volume snapshot is supported */ } if (volcount == 0) { ret = -1; cli_err("Please provide the volume name"); gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_int32(dict, "volcount", volcount); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save volcount"); goto out; } /* Verify how we got out of "for" loop, * if it is by reaching wordcount limit then goto "out", * because we need not parse for "description","force" and * "no-timestamp" after this. */ if (i == wordcount) { goto out; } if (strcmp(words[i], "no-timestamp") == 0) { ret = dict_set_int32n(dict, "no-timestamp", SLEN("no-timestamp"), 1); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save " "time-stamp option"); } if (i == (wordcount - 1)) goto out; i++; } if ((strcmp(words[i], "description")) == 0) { ++i; if (i > (wordcount - 1)) { ret = -1; cli_err("Please provide a description"); gf_log("cli", GF_LOG_ERROR, "Description not provided"); goto out; } ret = cli_snap_create_desc_parse(dict, words, wordcount, i); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save snap " "description"); goto out; } if (i == (wordcount - 1)) goto out; i++; /* point the index to next word. * As description might be follwed by force option which is deprecated. * Before that, check if wordcount limit is reached */ } /*TODO: the below force option should be completely removed after a couple of releases as it is deprecated.*/ if (strcmp(words[i], "force") == 0) { cli_out( "Warning: \'force\' option is deprecated and " "should not be used in the future while " "creating snapshot. Snapshot create command will " "only execute if all the bricks used in creating " "the snapshot are online."); } else { ret = -1; cli_err("Invalid Syntax."); gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } /* Check if the command has anything after the force */ if (++i < wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = 0; out: if (ret == 0) { /*Adding force flag in either of the case i.e force set * or unset*/ ret = dict_set_int32(dict, "flags", flags); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save " "snap force option"); } } return ret; } /* snapshot list [volname] * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_list_parse(dict_t *dict, const char **words, int wordcount) { int ret = -1; GF_ASSERT(words); GF_ASSERT(dict); if (wordcount < 2 || wordcount > 3) { gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } if (wordcount == 2) { ret = 0; goto out; } ret = dict_set_str(dict, "volname", (char *)words[2]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to save volname in dictionary"); goto out; } out: return ret; } /* snapshot info [(snapname | volume )] * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_info_parse(dict_t *dict, const char **words, int wordcount) { int ret = -1; int32_t cmd = GF_SNAP_INFO_TYPE_ALL; unsigned int cmdi = 2; /* cmdi is command index, here cmdi is "2" (gluster snapshot info)*/ GF_ASSERT(words); GF_ASSERT(dict); if (wordcount > 4 || wordcount < cmdi) { gf_log("cli", GF_LOG_ERROR, "Invalid syntax"); goto out; } if (wordcount == cmdi) { ret = 0; goto out; } /* If 3rd word is not "volume", then it must * be snapname. */ if (strcmp(words[cmdi], "volume") != 0) { ret = dict_set_str(dict, "snapname", (char *)words[cmdi]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to save " "snapname %s", words[cmdi]); goto out; } /* Once snap name is parsed, if we encounter any other * word then fail it. Invalid Syntax. * example : snapshot info word */ if ((cmdi + 1) != wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } cmd = GF_SNAP_INFO_TYPE_SNAP; ret = 0; goto out; /* No need to continue the parsing once we * get the snapname */ } /* If 3rd word is "volume", then check if next word * is present. As, "snapshot info volume" is an * invalid command. */ if ((cmdi + 1) == wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str(dict, "volname", (char *)words[wordcount - 1]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save " "volume name %s", words[wordcount - 1]); goto out; } cmd = GF_SNAP_INFO_TYPE_VOL; out: if (ret == 0) { ret = dict_set_int32(dict, "sub-cmd", cmd); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save " "type of snapshot info"); } } return ret; } /* snapshot restore * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_restore_parse(dict_t *dict, const char **words, int wordcount, struct cli_state *state) { int ret = -1; const char *question = NULL; gf_answer_t answer = GF_ANSWER_NO; GF_ASSERT(words); GF_ASSERT(dict); if (wordcount != 3) { gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str(dict, "snapname", (char *)words[2]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to save snap-name %s", words[2]); goto out; } question = "Restore operation will replace the " "original volume with the snapshotted volume. " "Do you still want to continue?"; answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { ret = 1; gf_log("cli", GF_LOG_ERROR, "User cancelled a snapshot " "restore operation for snap %s", (char *)words[2]); goto out; } out: return ret; } /* snapshot activate [force] * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_activate_parse(dict_t *dict, const char **words, int wordcount) { int ret = -1; int flags = 0; GF_ASSERT(words); GF_ASSERT(dict); if ((wordcount < 3) || (wordcount > 4)) { gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str(dict, "snapname", (char *)words[2]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to save snap-name %s", words[2]); goto out; } if (wordcount == 4) { if (!strcmp("force", (char *)words[3])) { flags = GF_CLI_FLAG_OP_FORCE; } else { gf_log("cli", GF_LOG_ERROR, "Invalid option"); ret = -1; goto out; } } ret = dict_set_int32(dict, "flags", flags); if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to save force option"); goto out; } out: return ret; } /* snapshot deactivate * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success * 1 if user cancelled the request */ int cli_snap_deactivate_parse(dict_t *dict, const char **words, int wordcount, struct cli_state *state) { int ret = -1; gf_answer_t answer = GF_ANSWER_NO; const char *question = "Deactivating snap will make its " "data inaccessible. Do you want to " "continue?"; GF_ASSERT(words); GF_ASSERT(dict); if ((wordcount != 3)) { gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str(dict, "snapname", (char *)words[2]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to save snap-name %s", words[2]); goto out; } answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { ret = 1; gf_log("cli", GF_LOG_DEBUG, "User cancelled " "snapshot deactivate operation"); goto out; } out: return ret; } /* snapshot delete (all | snapname | volume ) * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success * 1 if user cancel the operation */ int cli_snap_delete_parse(dict_t *dict, const char **words, int wordcount, struct cli_state *state) { int ret = -1; const char *question = NULL; int32_t cmd = -1; unsigned int cmdi = 2; gf_answer_t answer = GF_ANSWER_NO; GF_ASSERT(words); GF_ASSERT(dict); if (wordcount > 4 || wordcount <= cmdi) { gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } question = "Deleting snap will erase all the information about " "the snap. Do you still want to continue?"; if (strcmp(words[cmdi], "all") == 0) { ret = 0; cmd = GF_SNAP_DELETE_TYPE_ALL; } else if (strcmp(words[cmdi], "volume") == 0) { if (++cmdi == wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str(dict, "volname", (char *)words[cmdi]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save " "volume name %s", words[wordcount - 1]); goto out; } cmd = GF_SNAP_DELETE_TYPE_VOL; } else { ret = dict_set_str(dict, "snapname", (char *)words[cmdi]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to save " "snapname %s", words[2]); goto out; } cmd = GF_SNAP_DELETE_TYPE_SNAP; } if ((cmdi + 1) != wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } if (cmd == GF_SNAP_DELETE_TYPE_SNAP) { answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { ret = 1; gf_log("cli", GF_LOG_DEBUG, "User cancelled " "snapshot delete operation for snap %s", (char *)words[2]); goto out; } } ret = dict_set_int32(dict, "sub-cmd", cmd); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save " "type of snapshot delete"); } out: return ret; } /* snapshot status [(snapname | volume )] * @arg-0, dict : Request Dictionary to be sent to server side. * @arg-1, words : Contains individual words of CLI command. * @arg-2, wordcount: Contains number of words present in the CLI command. * * return value : -1 on failure * 0 on success */ int cli_snap_status_parse(dict_t *dict, const char **words, int wordcount) { int ret = -1; int32_t cmd = GF_SNAP_STATUS_TYPE_ALL; unsigned int cmdi = 2; /* cmdi is command index, here cmdi is "2" (gluster snapshot status)*/ GF_ASSERT(words); GF_ASSERT(dict); if (wordcount > 4 || wordcount < cmdi) { gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } if (wordcount == cmdi) { ret = 0; goto out; } /* if 3rd word is not "volume", then it must be "snapname" */ if (strcmp(words[cmdi], "volume") != 0) { ret = dict_set_str(dict, "snapname", (char *)words[cmdi]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Count not save " "snap name %s", words[cmdi]); goto out; } if ((cmdi + 1) != wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = 0; cmd = GF_SNAP_STATUS_TYPE_SNAP; goto out; } /* If 3rd word is "volume", then check if next word is present. * As, "snapshot info volume" is an invalid command */ if ((cmdi + 1) == wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str(dict, "volname", (char *)words[wordcount - 1]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Count not save " "volume name %s", words[wordcount - 1]); goto out; } cmd = GF_SNAP_STATUS_TYPE_VOL; out: if (ret == 0) { ret = dict_set_int32(dict, "sub-cmd", cmd); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not save cmd " "of snapshot status"); } } return ret; } /* return value: * -1 in case of failure. * 0 in case of success. */ int32_t cli_snap_config_limit_parse(const char **words, dict_t *dict, unsigned int wordcount, unsigned int index, char *key) { int ret = -1; int limit = 0; char *end_ptr = NULL; GF_ASSERT(words); GF_ASSERT(dict); GF_ASSERT(key); if (index >= wordcount) { ret = -1; cli_err("Please provide a value for %s.", key); gf_log("cli", GF_LOG_ERROR, "Value not provided for %s", key); goto out; } limit = strtol(words[index], &end_ptr, 10); if (limit <= 0 || strcmp(end_ptr, "") != 0) { ret = -1; cli_err( "Please enter an integer value " "greater than zero for %s", key); goto out; } ret = dict_set_int32(dict, key, limit); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not set " "%s in dictionary", key); goto out; } ret = dict_set_dynstr_with_alloc(dict, "globalname", "All"); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not set global key"); goto out; } ret = dict_set_int32(dict, "hold_global_locks", _gf_true); if (ret) { gf_log("cli", GF_LOG_ERROR, "Could not set global locks"); goto out; } out: return ret; } /* function cli_snap_config_parse * Config Syntax : gluster snapshot config [volname] * [snap-max-hard-limit ] * [snap-max-soft-limit ] * return value: <0 on failure 1 if user cancels the operation, or limit value is out of range 0 on success NOTE : snap-max-soft-limit can only be set for system. */ int32_t cli_snap_config_parse(const char **words, int wordcount, dict_t *dict, struct cli_state *state) { int ret = -1; gf_answer_t answer = GF_ANSWER_NO; gf_boolean_t vol_presence = _gf_false; struct snap_config_opt_vals_ *conf_vals = NULL; int8_t hard_limit = 0; int8_t soft_limit = 0; int8_t config_type = -1; const char *question = NULL; unsigned int cmdi = 2; /* cmdi is command index, here cmdi is "2" (gluster snapshot config)*/ GF_ASSERT(words); GF_ASSERT(dict); GF_ASSERT(state); if ((wordcount < 2) || (wordcount > 7)) { gf_log("cli", GF_LOG_ERROR, "Invalid wordcount(%d)", wordcount); goto out; } if (wordcount == 2) { config_type = GF_SNAP_CONFIG_DISPLAY; ret = 0; goto set; } /* auto-delete cannot be a volume name */ /* Check whether the 3rd word is volname */ if (strcmp(words[cmdi], "snap-max-hard-limit") != 0 && strcmp(words[cmdi], "snap-max-soft-limit") != 0 && strcmp(words[cmdi], "auto-delete") != 0 && strcmp(words[cmdi], "activate-on-create") != 0) { ret = dict_set_str(dict, "volname", (char *)words[cmdi]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to set volname"); goto out; } cmdi++; vol_presence = _gf_true; if (cmdi == wordcount) { config_type = GF_SNAP_CONFIG_DISPLAY; ret = 0; goto set; } } config_type = GF_SNAP_CONFIG_TYPE_SET; if (strcmp(words[cmdi], "snap-max-hard-limit") == 0) { ret = cli_snap_config_limit_parse(words, dict, wordcount, ++cmdi, "snap-max-hard-limit"); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to parse snap " "config hard limit"); goto out; } hard_limit = 1; if (++cmdi == wordcount) { ret = 0; goto set; } } if (strcmp(words[cmdi], "snap-max-soft-limit") == 0) { if (vol_presence == 1) { ret = -1; cli_err( "Soft limit cannot be set to individual " "volumes."); gf_log("cli", GF_LOG_ERROR, "Soft limit cannot be " "set to volumes"); goto out; } ret = cli_snap_config_limit_parse(words, dict, wordcount, ++cmdi, "snap-max-soft-limit"); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to parse snap " "config soft limit"); goto out; } if (++cmdi != wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } soft_limit = 1; } if (hard_limit || soft_limit) goto set; if (strcmp(words[cmdi], "auto-delete") == 0) { if (vol_presence == 1) { ret = -1; cli_err( "As of now, auto-delete option cannot be set " "to volumes"); gf_log("cli", GF_LOG_ERROR, "auto-delete option " "cannot be set to volumes"); goto out; } if (++cmdi >= wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str(dict, "auto-delete", (char *)words[cmdi]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to set " "value of auto-delete in request " "dictionary"); goto out; } if (++cmdi != wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } } else if (strcmp(words[cmdi], "activate-on-create") == 0) { if (vol_presence == 1) { ret = -1; cli_err( "As of now, activate-on-create option " "cannot be set to volumes"); gf_log("cli", GF_LOG_ERROR, "activate-on-create " "option cannot be set to volumes"); goto out; } if (++cmdi >= wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = dict_set_str(dict, "snap-activate-on-create", (char *)words[cmdi]); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to set value " "of activate-on-create in request dictionary"); goto out; } if (++cmdi != wordcount) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } } else { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = 0; /* Success */ set: ret = dict_set_int32(dict, "config-command", config_type); if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to set " "config-command"); goto out; } if (config_type == GF_SNAP_CONFIG_TYPE_SET && (hard_limit || soft_limit)) { conf_vals = snap_confopt_vals; if (hard_limit && soft_limit) { question = conf_vals[GF_SNAP_CONFIG_SET_BOTH].question; } else if (soft_limit) { question = conf_vals[GF_SNAP_CONFIG_SET_SOFT].question; } else if (hard_limit) { question = conf_vals[GF_SNAP_CONFIG_SET_HARD].question; } answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { ret = 1; gf_log("cli", GF_LOG_DEBUG, "User cancelled " "snapshot config operation"); } } out: return ret; } int validate_op_name(const char *op, const char *opname, char **opwords) { int ret = -1; int i = 0; GF_ASSERT(opname); GF_ASSERT(opwords); for (i = 0; opwords[i] != NULL; i++) { if (strcmp(opwords[i], opname) == 0) { cli_out("\"%s\" cannot be a %s", opname, op); goto out; } } ret = 0; out: return ret; } int32_t cli_cmd_snapshot_parse(const char **words, int wordcount, dict_t **options, struct cli_state *state) { int32_t ret = -1; dict_t *dict = NULL; gf1_cli_snapshot type = GF_SNAP_OPTION_TYPE_NONE; char *w = NULL; static char *opwords[] = {"create", "delete", "restore", "activate", "deactivate", "list", "status", "config", "info", "clone", NULL}; static char *invalid_snapnames[] = {"description", "force", "volume", "all", NULL}; static char *invalid_volnames[] = {"volume", "type", "subvolumes", "option", "end-volume", "all", "volume_not_in_ring", "description", "force", "snap-max-hard-limit", "snap-max-soft-limit", "auto-delete", "activate-on-create", NULL}; GF_ASSERT(words); GF_ASSERT(options); GF_ASSERT(state); dict = dict_new(); if (!dict) goto out; /* Lowest wordcount possible */ if (wordcount < 2) { gf_log("", GF_LOG_ERROR, "Invalid command: Not enough arguments"); goto out; } w = str_getunamb(words[1], opwords); if (!w) { /* Checks if the operation is a valid operation */ gf_log("", GF_LOG_ERROR, "Opword Mismatch"); goto out; } if (!strcmp(w, "create")) { type = GF_SNAP_OPTION_TYPE_CREATE; } else if (!strcmp(w, "list")) { type = GF_SNAP_OPTION_TYPE_LIST; } else if (!strcmp(w, "info")) { type = GF_SNAP_OPTION_TYPE_INFO; } else if (!strcmp(w, "delete")) { type = GF_SNAP_OPTION_TYPE_DELETE; } else if (!strcmp(w, "config")) { type = GF_SNAP_OPTION_TYPE_CONFIG; } else if (!strcmp(w, "restore")) { type = GF_SNAP_OPTION_TYPE_RESTORE; } else if (!strcmp(w, "status")) { type = GF_SNAP_OPTION_TYPE_STATUS; } else if (!strcmp(w, "activate")) { type = GF_SNAP_OPTION_TYPE_ACTIVATE; } else if (!strcmp(w, "deactivate")) { type = GF_SNAP_OPTION_TYPE_DEACTIVATE; } else if (!strcmp(w, "clone")) { type = GF_SNAP_OPTION_TYPE_CLONE; } if (type != GF_SNAP_OPTION_TYPE_CONFIG && type != GF_SNAP_OPTION_TYPE_STATUS) { ret = dict_set_int32(dict, "hold_snap_locks", _gf_true); if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to set hold-snap-locks value " "as _gf_true"); goto out; } } /* Following commands does not require volume locks */ if (type == GF_SNAP_OPTION_TYPE_STATUS || type == GF_SNAP_OPTION_TYPE_ACTIVATE || type == GF_SNAP_OPTION_TYPE_DEACTIVATE) { ret = dict_set_int32(dict, "hold_vol_locks", _gf_false); if (ret) { gf_log("cli", GF_LOG_ERROR, "Setting volume lock " "flag failed"); goto out; } } /* Check which op is intended */ switch (type) { case GF_SNAP_OPTION_TYPE_CREATE: /* Syntax : * gluster snapshot create * [no-timestamp] * [description ] * [force] */ /* In cases where the snapname is not given then * parsing fails & snapname cannot be "description", * "force" and "volume", that check is made here */ if (wordcount == 2) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = validate_op_name("snapname", words[2], invalid_snapnames); if (ret) { goto out; } ret = cli_snap_create_parse(dict, words, wordcount); if (ret) { gf_log("cli", GF_LOG_ERROR, "create command parsing failed."); goto out; } break; case GF_SNAP_OPTION_TYPE_CLONE: /* Syntax : * gluster snapshot clone */ /* In cases where the clonename is not given then * parsing fails & snapname cannot be "description", * "force" and "volume", that check is made here */ if (wordcount == 2) { ret = -1; gf_log("cli", GF_LOG_ERROR, "Invalid Syntax"); goto out; } ret = validate_op_name("clonename", words[2], invalid_volnames); if (ret) { goto out; } ret = cli_snap_clone_parse(dict, words, wordcount); if (ret) { gf_log("cli", GF_LOG_ERROR, "clone command parsing failed."); goto out; } break; case GF_SNAP_OPTION_TYPE_INFO: /* Syntax : * gluster snapshot info [(snapname] | [vol )] */ ret = cli_snap_info_parse(dict, words, wordcount); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to parse " "snapshot info command"); goto out; } break; case GF_SNAP_OPTION_TYPE_LIST: /* Syntax : * gluster snaphsot list [volname] */ ret = cli_snap_list_parse(dict, words, wordcount); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to parse " "snapshot list command"); goto out; } break; case GF_SNAP_OPTION_TYPE_DELETE: /* Syntax : * snapshot delete (all | snapname | volume ) */ ret = cli_snap_delete_parse(dict, words, wordcount, state); if (ret) { /* A positive ret value means user cancelled * the command */ if (ret < 0) { gf_log("cli", GF_LOG_ERROR, "Failed to parse " "snapshot delete command"); } goto out; } break; case GF_SNAP_OPTION_TYPE_CONFIG: /* snapshot config [volname] [snap-max-hard-limit ] * [snap-max-soft-limit ] */ ret = cli_snap_config_parse(words, wordcount, dict, state); if (ret) { if (ret < 0) gf_log("cli", GF_LOG_ERROR, "config command parsing failed."); goto out; } ret = dict_set_int32(dict, "type", GF_SNAP_OPTION_TYPE_CONFIG); if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to set " "config type"); ret = -1; goto out; } break; case GF_SNAP_OPTION_TYPE_STATUS: { /* Syntax : * gluster snapshot status [(snapname | * volume )] */ ret = cli_snap_status_parse(dict, words, wordcount); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to parse " "snapshot status command"); goto out; } break; } case GF_SNAP_OPTION_TYPE_RESTORE: /* Syntax: * snapshot restore */ ret = cli_snap_restore_parse(dict, words, wordcount, state); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to parse " "restore command"); goto out; } break; case GF_SNAP_OPTION_TYPE_ACTIVATE: /* Syntax: * snapshot activate [force] */ ret = cli_snap_activate_parse(dict, words, wordcount); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to parse " "start command"); goto out; } break; case GF_SNAP_OPTION_TYPE_DEACTIVATE: /* Syntax: * snapshot deactivate */ ret = cli_snap_deactivate_parse(dict, words, wordcount, state); if (ret) { /* A positive ret value means user cancelled * the command */ if (ret < 0) { gf_log("cli", GF_LOG_ERROR, "Failed to parse deactivate " "command"); } goto out; } break; default: ret = -1; gf_log("", GF_LOG_ERROR, "Opword Mismatch"); goto out; } ret = dict_set_int32(dict, "type", type); if (ret) { gf_log("", GF_LOG_ERROR, "Failed to set type."); goto out; } /* If you got so far, input is valid */ ret = 0; out: if (ret) { if (dict) dict_unref(dict); } else *options = dict; return ret; } int cli_cmd_validate_volume(char *volname) { int i = 0; int ret = -1; int volname_len; if (volname[0] == '-') return ret; if (!strcmp(volname, "all")) { cli_err("\"all\" cannot be the name of a volume."); return ret; } if (strchr(volname, '/')) { cli_err("Volume name should not contain \"/\" character."); return ret; } volname_len = strlen(volname); if (volname_len > GD_VOLUME_NAME_MAX) { cli_err("Volname can not exceed %d characters.", GD_VOLUME_NAME_MAX); return ret; } for (i = 0; i < volname_len; i++) if (!isalnum(volname[i]) && (volname[i] != '_') && (volname[i] != '-')) { cli_err( "Volume name should not contain \"%c\"" " character.\nVolume names can only" "contain alphanumeric, '-' and '_' " "characters.", volname[i]); return ret; } ret = 0; return ret; } int32_t cli_cmd_bitrot_parse(const char **words, int wordcount, dict_t **options) { int32_t ret = -1; char *w = NULL; char *volname = NULL; static char *opwords[] = {"enable", "disable", "scrub-throttle", "scrub-frequency", "scrub", "signing-time", "signer-threads", NULL}; static char *scrub_throt_values[] = {"lazy", "normal", "aggressive", NULL}; static char *scrub_freq_values[] = { "hourly", "daily", "weekly", "biweekly", "monthly", "minute", NULL}; static char *scrub_values[] = {"pause", "resume", "status", "ondemand", NULL}; dict_t *dict = NULL; gf_bitrot_type type = GF_BITROT_OPTION_TYPE_NONE; int32_t expiry_time = 0; int32_t signer_th_count = 0; GF_ASSERT(words); GF_ASSERT(options); /* Hack to print out bitrot help properly */ if ((wordcount == 3) && !(strcmp(words[2], "help"))) { ret = 1; return ret; } if (wordcount < 4 || wordcount > 5) { gf_log("cli", GF_LOG_ERROR, "Invalid syntax"); goto out; } dict = dict_new(); if (!dict) goto out; volname = (char *)words[2]; if (!volname) { ret = -1; goto out; } ret = cli_cmd_validate_volume(volname); if (ret) { gf_log("cli", GF_LOG_ERROR, "Failed to validate volume name"); goto out; } ret = dict_set_str(dict, "volname", volname); if (ret) { cli_out("Failed to set volume name in dictionary "); goto out; } w = str_getunamb(words[3], opwords); if (!w) { cli_out("Invalid bit rot option : %s", words[3]); ret = -1; goto out; } if (strcmp(w, "enable") == 0) { if (wordcount == 4) { type = GF_BITROT_OPTION_TYPE_ENABLE; ret = 0; goto set_type; } else { ret = -1; goto out; } } if (strcmp(w, "disable") == 0) { if (wordcount == 4) { type = GF_BITROT_OPTION_TYPE_DISABLE; ret = 0; goto set_type; } else { ret = -1; goto out; } } if (!strcmp(w, "scrub-throttle")) { if (!words[4]) { cli_err( "Missing scrub-throttle value for bitrot " "option"); ret = -1; goto out; } else { w = str_getunamb(words[4], scrub_throt_values); if (!w) { cli_err( "Invalid scrub-throttle option for " "bitrot"); ret = -1; goto out; } else { type = GF_BITROT_OPTION_TYPE_SCRUB_THROTTLE; ret = dict_set_str(dict, "scrub-throttle-value", (char *)words[4]); if (ret) { cli_out( "Failed to set scrub-throttle " "value in the dict"); goto out; } goto set_type; } } } if (!strcmp(words[3], "scrub-frequency")) { if (!words[4]) { cli_err("Missing scrub-frequency value"); ret = -1; goto out; } else { w = str_getunamb(words[4], scrub_freq_values); if (!w) { cli_err("Invalid frequency option for bitrot"); ret = -1; goto out; } else { type = GF_BITROT_OPTION_TYPE_SCRUB_FREQ; ret = dict_set_str(dict, "scrub-frequency-value", (char *)words[4]); if (ret) { cli_out( "Failed to set dict for " "bitrot"); goto out; } goto set_type; } } } if (!strcmp(words[3], "scrub")) { if (!words[4]) { cli_err("Missing scrub value for bitrot option"); ret = -1; goto out; } else { w = str_getunamb(words[4], scrub_values); if (!w) { cli_err("Invalid scrub option for bitrot"); ret = -1; goto out; } else { if (strcmp(words[4], "status") == 0) { type = GF_BITROT_CMD_SCRUB_STATUS; } else if (strcmp(words[4], "ondemand") == 0) { type = GF_BITROT_CMD_SCRUB_ONDEMAND; } else { type = GF_BITROT_OPTION_TYPE_SCRUB; } ret = dict_set_str(dict, "scrub-value", (char *)words[4]); if (ret) { cli_out( "Failed to set dict for " "bitrot"); goto out; } goto set_type; } } } if (!strcmp(words[3], "signing-time")) { if (!words[4]) { cli_err( "Missing signing-time value for bitrot " "option"); ret = -1; goto out; } else { type = GF_BITROT_OPTION_TYPE_EXPIRY_TIME; expiry_time = strtol(words[4], NULL, 0); if (expiry_time < 1) { cli_err( "Expiry time value should not be less" " than 1"); ret = -1; goto out; } ret = dict_set_uint32(dict, "expiry-time", (unsigned int)expiry_time); if (ret) { cli_out("Failed to set dict for bitrot"); goto out; } goto set_type; } } else if (!strcmp(words[3], "signer-threads")) { if (!words[4]) { cli_err( "Missing signer-thread value for bitrot " "option"); ret = -1; goto out; } else { type = GF_BITROT_OPTION_TYPE_SIGNER_THREADS; signer_th_count = strtol(words[4], NULL, 0); if (signer_th_count < 1) { cli_err("signer-thread count should not be less than 1"); ret = -1; goto out; } ret = dict_set_uint32(dict, "signer-threads", (unsigned int)signer_th_count); if (ret) { cli_out("Failed to set dict for bitrot"); goto out; } goto set_type; } } else { cli_err( "Invalid option %s for bitrot. Please enter valid " "bitrot option", words[3]); ret = -1; goto out; } set_type: ret = dict_set_int32(dict, "type", type); if (ret < 0) goto out; *options = dict; out: if (ret) { gf_log("cli", GF_LOG_ERROR, "Unable to parse bitrot command"); if (dict) dict_unref(dict); } return ret; } /* Parsing global option for NFS-Ganesha config * gluster nfs-ganesha enable/disable */ int32_t cli_cmd_ganesha_parse(struct cli_state *state, const char **words, int wordcount, dict_t **options, char **op_errstr) { dict_t *dict = NULL; int ret = -1; char *key = NULL; char *value = NULL; char *w = NULL; static char *opwords[] = {"enable", "disable", NULL}; const char *question = NULL; gf_answer_t answer = GF_ANSWER_NO; GF_ASSERT(words); GF_ASSERT(options); dict = dict_new(); if (!dict) goto out; if (wordcount != 2) goto out; key = (char *)words[0]; value = (char *)words[1]; if (!key || !value) { cli_out("Usage : nfs-ganesha "); ret = -1; goto out; } ret = gf_strip_whitespace(value, strlen(value)); if (ret == -1) goto out; if (strcmp(key, "nfs-ganesha")) { gf_asprintf(op_errstr, "Global option: error: ' %s '" "is not a valid global option.", key); ret = -1; goto out; } w = str_getunamb(value, opwords); if (!w) { cli_out( "Invalid global option \n" "Usage : nfs-ganesha "); ret = -1; goto out; } if (strcmp(value, "enable") == 0) { question = "Enabling NFS-Ganesha requires Gluster-NFS to be " "disabled across the trusted pool. Do you " "still want to continue?\n"; } else if (strcmp(value, "disable") == 0) { question = "Disabling NFS-Ganesha will tear down the entire " "ganesha cluster across the trusted pool. Do you " "still want to continue?\n"; } else { ret = -1; goto out; } answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { gf_log("cli", GF_LOG_ERROR, "Global operation " "cancelled, exiting"); ret = -1; goto out; } cli_out("This will take a few minutes to complete. Please wait .."); ret = dict_set_str(dict, "key", key); if (ret) { gf_log(THIS->name, GF_LOG_ERROR, "dict set on key failed"); goto out; } ret = dict_set_str(dict, "value", value); if (ret) { gf_log(THIS->name, GF_LOG_ERROR, "dict set on value failed"); goto out; } ret = dict_set_str(dict, "globalname", "All"); if (ret) { gf_log(THIS->name, GF_LOG_ERROR, "dict set on global" " key failed."); goto out; } ret = dict_set_int32(dict, "hold_global_locks", _gf_true); if (ret) { gf_log(THIS->name, GF_LOG_ERROR, "dict set on global key " "failed."); goto out; } *options = dict; out: if (ret) dict_unref(dict); return ret; } glusterfs-11.2/cli/src/cli-cmd-peer.c000066400000000000000000000173621503123251100174200ustar00rootroot00000000000000/* Copyright (c) 2010-2012 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include #include #include #include "cli.h" #include "cli-cmd.h" #include "cli-mem-types.h" #include int cli_cmd_peer_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount); int cli_cmd_peer_probe_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; dict_t *dict = NULL; int sent = 0; int parse_error = 0; cli_local_t *local = NULL; if (!(wordcount == 3)) { cli_usage_out(word->pattern); parse_error = 1; goto out; } proc = &cli_rpc_prog->proctable[GLUSTER_CLI_PROBE]; dict = dict_new(); if (!dict) goto out; ret = dict_set_str(dict, "hostname", (char *)words[2]); if (ret) goto out; ret = valid_internet_address((char *)words[2], _gf_false, _gf_false); if (ret == 1) { ret = 0; } else { cli_out("%s is an invalid address", words[2]); cli_usage_out(word->pattern); parse_error = 1; ret = -1; goto out; } /* if (words[3]) { ret = dict_set_str (dict, "port", (char *)words[3]); if (ret) goto out; } */ frame = create_frame(THIS, THIS->ctx->pool); if (!frame) { ret = -1; goto out; } CLI_LOCAL_INIT(local, words, frame, dict); if (proc->fn) { ret = proc->fn(frame, THIS, dict); } out: if (ret) { cli_cmd_sent_status_get(&sent); if ((sent == 0) && (parse_error == 0)) cli_out("Peer probe failed"); } CLI_STACK_DESTROY(frame); if (dict) dict_unref(dict); if (ret == 0) { gf_event(EVENT_PEER_ATTACH, "host=%s", (char *)words[2]); } return ret; } int cli_cmd_peer_deprobe_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; dict_t *dict = NULL; int flags = 0; int sent = 0; int parse_error = 0; cli_local_t *local = NULL; gf_answer_t answer = GF_ANSWER_NO; const char *question = NULL; if ((wordcount < 3) || (wordcount > 4)) { cli_usage_out(word->pattern); parse_error = 1; goto out; } question = "All clients mounted through the peer which is getting detached need " "to be remounted using one of the other active peers in the trusted " "storage pool to ensure client gets notification on any changes done " "on the gluster configuration and if the same has been done do you " "want to proceed?"; proc = &cli_rpc_prog->proctable[GLUSTER_CLI_DEPROBE]; dict = dict_new(); ret = dict_set_str(dict, "hostname", (char *)words[2]); if (ret) goto out; /* if (words[3]) { ret = dict_set_str (dict, "port", (char *)words[3]); if (ret) goto out; } */ if (wordcount == 4) { if (!strcmp("force", words[3])) flags |= GF_CLI_FLAG_OP_FORCE; else { ret = -1; cli_usage_out(word->pattern); parse_error = 1; goto out; } } ret = dict_set_int32(dict, "flags", flags); if (ret) goto out; answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { ret = 0; goto out; } frame = create_frame(THIS, THIS->ctx->pool); if (!frame) { ret = -1; goto out; } CLI_LOCAL_INIT(local, words, frame, dict); if (proc->fn) { ret = proc->fn(frame, THIS, dict); } out: if (ret) { cli_cmd_sent_status_get(&sent); if ((sent == 0) && (parse_error == 0)) cli_out("Peer detach failed"); } CLI_STACK_DESTROY(frame); if (dict) dict_unref(dict); if (ret == 0) { gf_event(EVENT_PEER_DETACH, "host=%s", (char *)words[2]); } return ret; } int cli_cmd_peer_status_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; int sent = 0; int parse_error = 0; if (wordcount != 2) { cli_usage_out(word->pattern); parse_error = 1; goto out; } proc = &cli_rpc_prog->proctable[GLUSTER_CLI_LIST_FRIENDS]; frame = create_frame(THIS, THIS->ctx->pool); if (!frame) goto out; if (proc->fn) { ret = proc->fn(frame, THIS, (void *)GF_CLI_LIST_PEERS); } out: if (ret) { cli_cmd_sent_status_get(&sent); if ((sent == 0) && (parse_error == 0)) cli_out("Peer status failed"); } CLI_STACK_DESTROY(frame); return ret; } int cli_cmd_pool_list_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; int sent = 0; int parse_error = 0; if (wordcount != 2) { cli_usage_out(word->pattern); parse_error = 1; goto out; } proc = &cli_rpc_prog->proctable[GLUSTER_CLI_LIST_FRIENDS]; frame = create_frame(THIS, THIS->ctx->pool); if (!frame) goto out; if (proc->fn) { ret = proc->fn(frame, THIS, (void *)GF_CLI_LIST_POOL_NODES); } out: if (ret) { cli_cmd_sent_status_get(&sent); if ((sent == 0) && (parse_error == 0)) cli_err("pool list: command execution failed"); } CLI_STACK_DESTROY(frame); return ret; } struct cli_cmd cli_probe_cmds[] = { {"peer probe { | }", cli_cmd_peer_probe_cbk, "probe peer specified by "}, {"peer detach { | } [force]", cli_cmd_peer_deprobe_cbk, "detach peer specified by "}, {"peer status", cli_cmd_peer_status_cbk, "list status of peers"}, {"peer help", cli_cmd_peer_help_cbk, "display help for peer commands"}, {"pool list", cli_cmd_pool_list_cbk, "list all the nodes in the pool (including localhost)"}, {NULL, NULL, NULL}}; int cli_cmd_peer_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount) { struct cli_cmd *cmd = NULL; struct cli_cmd *probe_cmd = NULL; int count = 0; cli_out("\ngluster peer commands"); cli_out("======================\n"); cmd = GF_MALLOC(sizeof(cli_probe_cmds), cli_mt_cli_cmd); memcpy(cmd, cli_probe_cmds, sizeof(cli_probe_cmds)); count = (sizeof(cli_probe_cmds) / sizeof(struct cli_cmd)); cli_cmd_sort(cmd, count); for (probe_cmd = cmd; probe_cmd->pattern; probe_cmd++) cli_out("%s - %s", probe_cmd->pattern, probe_cmd->desc); GF_FREE(cmd); cli_out("\n"); return 0; } int cli_cmd_probe_register(struct cli_state *state) { int ret = 0; struct cli_cmd *cmd = NULL; for (cmd = cli_probe_cmds; cmd->pattern; cmd++) { ret = cli_cmd_register(&state->tree, cmd); if (ret) goto out; } out: return ret; } glusterfs-11.2/cli/src/cli-cmd-snapshot.c000066400000000000000000000100531503123251100203120ustar00rootroot00000000000000/* Copyright (c) 2013-2014 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include #include #include #include "cli.h" #include "cli-cmd.h" #include "cli-mem-types.h" int cli_cmd_snapshot_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount); int cli_cmd_snapshot_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = 0; int parse_err = 0; dict_t *options = NULL; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; cli_local_t *local = NULL; proc = &cli_rpc_prog->proctable[GLUSTER_CLI_SNAP]; /* Parses the command entered by the user */ ret = cli_cmd_snapshot_parse(words, wordcount, &options, state); if (ret) { if (ret < 0) { cli_usage_out(word->pattern); parse_err = 1; } else { /* User might have cancelled the snapshot operation */ ret = 0; } goto out; } frame = create_frame(THIS, THIS->ctx->pool); if (frame == NULL) { ret = -1; goto out; } CLI_LOCAL_INIT(local, words, frame, options); if (proc->fn) ret = proc->fn(frame, THIS, options); out: if (ret && parse_err == 0) cli_out("Snapshot command failed"); CLI_STACK_DESTROY(frame); if (options) dict_unref(options); return ret; } struct cli_cmd snapshot_cmds[] = { {"snapshot help", cli_cmd_snapshot_help_cbk, "display help for snapshot commands"}, {"snapshot create [no-timestamp] " "[description ]", cli_cmd_snapshot_cbk, "Snapshot Create."}, {"snapshot clone ", cli_cmd_snapshot_cbk, "Snapshot Clone."}, {"snapshot restore ", cli_cmd_snapshot_cbk, "Snapshot Restore."}, {"snapshot status [(snapname | volume )]", cli_cmd_snapshot_cbk, "Snapshot Status."}, {"snapshot info [(snapname | volume )]", cli_cmd_snapshot_cbk, "Snapshot Info."}, {"snapshot list [volname]", cli_cmd_snapshot_cbk, "Snapshot List."}, {"snapshot config [volname] ([snap-max-hard-limit ] " "[snap-max-soft-limit ]) " "| ([auto-delete ])" "| ([activate-on-create ])", cli_cmd_snapshot_cbk, "Snapshot Config."}, {"snapshot delete (all | snapname | volume )", cli_cmd_snapshot_cbk, "Snapshot Delete."}, {"snapshot activate [force]", cli_cmd_snapshot_cbk, "Activate snapshot volume."}, {"snapshot deactivate ", cli_cmd_snapshot_cbk, "Deactivate snapshot volume."}, {NULL, NULL, NULL}}; int cli_cmd_snapshot_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount) { struct cli_cmd *cmd = NULL; struct cli_cmd *snap_cmd = NULL; int count = 0; cmd = GF_MALLOC(sizeof(snapshot_cmds), cli_mt_cli_cmd); memcpy(cmd, snapshot_cmds, sizeof(snapshot_cmds)); count = (sizeof(snapshot_cmds) / sizeof(struct cli_cmd)); cli_cmd_sort(cmd, count); cli_out("\ngluster snapshot commands"); cli_out("=========================\n"); for (snap_cmd = cmd; snap_cmd->pattern; snap_cmd++) if (_gf_false == snap_cmd->disable) cli_out("%s - %s", snap_cmd->pattern, snap_cmd->desc); cli_out("\n"); GF_FREE(cmd); return 0; } int cli_cmd_snapshot_register(struct cli_state *state) { int ret = 0; struct cli_cmd *cmd = NULL; for (cmd = snapshot_cmds; cmd->pattern; cmd++) { ret = cli_cmd_register(&state->tree, cmd); if (ret) goto out; } out: return ret; } glusterfs-11.2/cli/src/cli-cmd-system.c000066400000000000000000000366271503123251100200160ustar00rootroot00000000000000/* Copyright (c) 2010-2012 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include #include #include #include "cli.h" #include "cli-cmd.h" #include "cli-mem-types.h" int cli_cmd_system_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount); int cli_cmd_copy_file_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount); int cli_cmd_sys_exec_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount); int cli_cmd_getspec_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; dict_t *dict = NULL; if (wordcount != 3) { cli_usage_out(word->pattern); goto out; } dict = dict_new(); if (!dict) goto out; ret = dict_set_str(dict, "volid", (char *)words[2]); if (ret) goto out; proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GETSPEC]; if (proc->fn) { frame = create_frame(THIS, THIS->ctx->pool); if (!frame) { ret = -1; goto out; } ret = proc->fn(frame, THIS, dict); } out: if (!proc && ret) { if (wordcount > 1) cli_out("Fetching spec for volume %s failed", (char *)words[2]); } CLI_STACK_DESTROY(frame); if (dict) dict_unref(dict); return ret; } int cli_cmd_pmap_b2p_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; dict_t *dict = NULL; if (wordcount != 4) { cli_usage_out(word->pattern); goto out; } dict = dict_new(); if (!dict) goto out; ret = dict_set_str(dict, "brick", (char *)words[3]); if (ret) goto out; proc = &cli_rpc_prog->proctable[GLUSTER_CLI_PMAP_PORTBYBRICK]; if (proc->fn) { frame = create_frame(THIS, THIS->ctx->pool); if (!frame) { ret = -1; goto out; } ret = proc->fn(frame, THIS, dict); } out: if (!proc && ret) { if (wordcount > 1) cli_out("Fetching spec for volume %s failed", (char *)words[3]); } CLI_STACK_DESTROY(frame); if (dict) dict_unref(dict); return ret; } int cli_cmd_fsm_log_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; char *name = ""; if ((wordcount != 4) && (wordcount != 3)) { cli_usage_out(word->pattern); goto out; } if (wordcount == 4) name = (char *)words[3]; proc = &cli_rpc_prog->proctable[GLUSTER_CLI_FSM_LOG]; if (proc && proc->fn) { frame = create_frame(THIS, THIS->ctx->pool); if (!frame) goto out; ret = proc->fn(frame, THIS, (void *)name); } out: return ret; } int cli_cmd_getwd_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; if (wordcount != 2) { cli_usage_out(word->pattern); goto out; } proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GETWD]; if (proc && proc->fn) { frame = create_frame(THIS, THIS->ctx->pool); if (!frame) goto out; ret = proc->fn(frame, THIS, NULL); } out: return ret; } static dict_t * make_seq_dict(int argc, char **argv) { char index[] = "4294967296"; // 1<<32 int i = 0; int len; int ret = 0; dict_t *dict = dict_new(); if (!dict) return NULL; for (i = 0; i < argc; i++) { len = snprintf(index, sizeof(index), "%d", i); ret = dict_set_strn(dict, index, len, argv[i]); if (ret == -1) break; } if (ret) { dict_unref(dict); dict = NULL; } return dict; } int cli_cmd_mount_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; int ret = -1; dict_t *dict = NULL; void *dataa[] = {NULL, NULL}; if (wordcount < 4) { cli_usage_out(word->pattern); goto out; } dict = make_seq_dict(wordcount - 3, (char **)words + 3); if (!dict) goto out; dataa[0] = (void *)words[2]; dataa[1] = dict; proc = &cli_rpc_prog->proctable[GLUSTER_CLI_MOUNT]; if (proc && proc->fn) { frame = create_frame(THIS, THIS->ctx->pool); if (!frame) goto out; ret = proc->fn(frame, THIS, dataa); } out: if (dict) dict_unref(dict); if (!proc && ret) cli_out("Mount command failed"); return ret; } int cli_cmd_umount_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; int ret = -1; dict_t *dict = NULL; if (!(wordcount == 3 || (wordcount == 4 && strcmp(words[3], "lazy") == 0))) { cli_usage_out(word->pattern); goto out; } dict = dict_new(); if (!dict) goto out; ret = dict_set_str(dict, "path", (char *)words[2]); if (ret != 0) goto out; ret = dict_set_int32(dict, "lazy", wordcount == 4); if (ret != 0) goto out; proc = &cli_rpc_prog->proctable[GLUSTER_CLI_UMOUNT]; if (proc && proc->fn) { frame = create_frame(THIS, THIS->ctx->pool); if (!frame) { gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame"); ret = -1; goto out; } ret = proc->fn(frame, THIS, dict); } out: if (dict) dict_unref(dict); if (!proc && ret) cli_out("Umount command failed"); return ret; } int cli_cmd_uuid_get_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = -1; int sent = 0; int parse_error = 0; dict_t *dict = NULL; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; cli_local_t *local = NULL; xlator_t *this = NULL; this = THIS; if (wordcount != 3) { cli_usage_out(word->pattern); parse_error = 1; goto out; } proc = &cli_rpc_prog->proctable[GLUSTER_CLI_UUID_GET]; frame = create_frame(this, this->ctx->pool); if (!frame) goto out; dict = dict_new(); if (!dict) goto out; CLI_LOCAL_INIT(local, words, frame, dict); if (proc->fn) ret = proc->fn(frame, this, dict); out: if (ret) { cli_cmd_sent_status_get(&sent); if ((sent == 0) && (parse_error == 0)) cli_out("uuid get failed"); } CLI_STACK_DESTROY(frame); if (dict) dict_unref(dict); return ret; } int cli_cmd_uuid_reset_cbk(struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) { int ret = -1; rpc_clnt_procedure_t *proc = NULL; call_frame_t *frame = NULL; int sent = 0; int parse_error = 0; gf_answer_t answer = GF_ANSWER_NO; char *question = NULL; cli_local_t *local = NULL; dict_t *dict = NULL; xlator_t *this = NULL; question = "Resetting uuid changes the uuid of local glusterd. " "Do you want to continue?"; if (wordcount != 3) { cli_usage_out(word->pattern); parse_error = 1; goto out; } proc = &cli_rpc_prog->proctable[GLUSTER_CLI_UUID_RESET]; this = THIS; frame = create_frame(this, this->ctx->pool); if (!frame) goto out; dict = dict_new(); if (!dict) { ret = -1; goto out; } CLI_LOCAL_INIT(local, words, frame, dict); answer = cli_cmd_get_confirmation(state, question); if (GF_ANSWER_NO == answer) { ret = 0; goto out; } // send NULL as argument since no dictionary is sent to glusterd if (proc->fn) { ret = proc->fn(frame, this, dict); } out: if (ret) { cli_cmd_sent_status_get(&sent); if ((sent == 0) && (parse_error == 0)) cli_out("uuid reset failed"); } CLI_STACK_DESTROY(frame); if (dict) dict_unref(dict); return ret; } static struct cli_cmd cli_system_cmds[] = { {"system:: getspec ", cli_cmd_getspec_cbk, "fetch the volume file for the volume "}, {"system:: portmap brick2port ", cli_cmd_pmap_b2p_cbk, "query which port listens on"}, {"system:: fsm log []", cli_cmd_fsm_log_cbk, "display fsm transitions"}, {"system:: getwd", cli_cmd_getwd_cbk, "query glusterd work directory"}, {"system:: mount