diff --git a/COPYING-3_0.txt b/COPYING-3_0.txt
new file mode 100644
index 0000000..729f5d7
--- /dev/null
+++ b/COPYING-3_0.txt
@@ -0,0 +1,675 @@
+ GNU 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.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU 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. But first, please read
+.
+
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..069269d
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,640 @@
+
+Version 5.02 2014-06-23
+ * corect README spell mistake
+ * bug fixed: can't deal sync truncate file exception
+ * remove tracker_global.c extern keyword to tracker_global.h
+
+Version 5.01 2014-02-02
+ * trunk binlog be compressed when trunk init
+ * bug fixed: sync trunk binlog file to other storage servers immediately when
+ the trunk server init done
+ * move ioevent_loop.[hc] and fast_task_queue.[hc] from tracker/ to common/
+ * hash table support locks
+ * hash talbe support new functions: hash_inc and hash_inc_ex
+
+Version 5.00 2013-12-23
+ * discard libevent, use epoll in Linux, kqueue in FreeBSD, port in SunOS directly
+ * do_notify_leader_changed force close connection when target is myself
+ * modify the INSTALL file and tracker/Makefile.in
+
+Version 4.08 2013-11-30
+ * bug fixed: FDFS_DOWNLOAD_SERVER_ROUND_ROBIN change to FDFS_STORE_SERVER_ROUND_ROBIN
+ * dio_init use memset to init buffer
+ * disable linger setting (setsockopt with option SO_LINGER)
+ * change log level from error to warning when file not exist on storage server
+
+Version 4.07 2013-06-02
+ * make.sh add -lpthread by ldconfig check
+ * support multi accept threads
+ * tracker and storage server close client connection when recv invalid package
+ * client/storage_client.c: file_exist with silence flag
+ * tracker and storage process support start, stop and restart command
+ * tracker/tracker_proto.c fdfs_recv_header: logDebug change to logError
+
+Version 4.06 2013-01-24
+ * fdfs_upload_file tool enhancement
+ * fdfs_download_file tool support offset and download size
+ * trunk file upload support sub paths rotating correctly
+ * add function: fdfs_http_get_file_extension
+ * sync truncate file operation anyway
+
+Version 4.05 2012-12-30
+ * client/fdfs_upload_file.c can specify storage ip port and store path index
+ * add connection pool
+ * client load storage ids config
+ * common/ini_file_reader.c does NOT call chdir
+ * keep the mtime of file same
+ * use g_current_time instead of call time function
+ * remove embed HTTP support
+
+Version 4.04 2012-12-02
+ * bug fixed: get storage server id when storage daemon init
+ * storage id in filename use global variable
+ * dynamic alloc memory 8 bytes alignment
+ * fast_task_queue support memory pool chain
+
+Version 4.03 2012-11-18
+ * trunk_mgr/trunk_mem.c: log error and add more debug info
+ * file id generated by storage server can include storage server ID
+
+Version 4.02 2012-10-30
+ * validate file_ext_name and prefix_name when upload file
+ * storage.conf add parameter: file_sync_skip_invalid_record
+ * add offset debug info when sync file fail
+ * bug fixed: log to binlog also if the file exists when sync file
+ * tracker and storage error log support rotate
+ * support rotate log by file size
+ * rotate log when receive HUP signal
+ * fdfs_monitor support set trunk server
+ * bug fixed: tracker_mem.c correct double mutex lock
+
+Version 4.01 2012-10-21
+ * trunk_mgr/trunk_mem.c: trunk init flag check more strictly
+ * file signature for checking file duplicate support MD5
+ * slave file support both symbol link and direct file
+ * tracker server log trunk server change logs
+
+Version 4.00 2012-10-06
+ * identify storage server by ID instead of IP address
+ * tracker.conf: storage reserved space can use ratio such as 10%
+ * storage server support access log
+ * appender file and trunk file also use rand number in file id
+ * bug fixed: test_upload.c: char file_id[64] change to: char file_id[128]
+ * set pipe reading fd with attribute O_NOATIME
+ * bug fixed: correct php extension call_user_function TSRMLS_DC with TSRMLS_CC
+
+Version 3.11 2012-08-04
+ * setsockopt set linger.l_linger to micro-seconds in FreeBSD and seconds
+ in others
+ * trunk binlog reader skip incorrect records
+ * bug fixed: single disk recovery support symbol link and trunk file
+ * storage generate filename enhancement
+ * ETIME change to ETIMEDOUT for FreeBSD
+ * tracker_mem.c: load storage server ignore empty ip address
+
+Version 3.10 2012-07-22
+ * check and init trunk file more gracefully
+ * remove unused-but-set-variable
+ * bug fixed: return correct group name when g_check_file_duplicate is true
+ * bug fixed: php extension call_user_function replace TSRMLS_CC with TSRMLS_DC
+ * large the interval of tracker re-select trunk server
+ * trunk free block check duplicate using avl tree
+ * trunk file sync overwrite the dest file anyway
+ * common/avl_tree.c: free data when delete
+ * tracker.conf add parameter: trunk_init_reload_from_binlog, when this flag
+ is set to true, load all free trunk blocks from the trunk binlog
+ * trunk status control only by trunk_mem.c and memcmp struct FDFSTrunkFullInfo
+ avoid memory alignment problem
+ * auto remove the too old temp file
+
+Version 3.09 2012-07-08
+ * make.sh avoid override config files of /etc/fdfs/
+ * common/logger.c: function log_init can be called more than once
+ * php extension logInfo change to logDebug
+ * c client logInfo change to logDebug
+ * storage_dio.c log info more properly
+ * delete the trunk space which be occupied
+ * tracker.conf add parameter: trunk_init_check_occupying, when this flag
+ is set to true, do not add the trunk nodes which be occupied
+ * another method to get local ip addresses
+
+Version 3.08 2012-05-27
+ * FAST_MAX_LOCAL_IP_ADDRS change from 4 to 16
+ * appender file support modify
+ * appender file support truncate
+
+Version 3.07 2012-05-13
+ * tracker/tracker_mem.c: check storage ip address is not empty
+ * remove direct IO support
+ * trunk binlog sync optimization
+ * php extension compile passed in PHP 5.4.0
+ * get local ip addresses enhancement
+ * trunk server select the storage server whose binglog file size is max
+ * sync trunk binlog file correctly when trunk server changed
+
+Version 3.06 2012-01-22
+ * add common/avl_tree.h and common/avl_tree.c
+ * organize trunk free blocks using AVL tree
+ * find the trunk server for each group when current tracker be a leader
+ * common/sched_thread.c can add schedule entry dynamicly
+ * support creating trunk file advancely
+
+Version 3.05 2011-12-20
+ * remove compile warnings
+ * storage server's store_path_count can be more than that of group
+ * bug fixed: common/fast_mblock.c malloc bytes are not enough
+ * make.sh support OS: HP-UX
+
+Version 3.04 2011-11-25
+ * bug fixed: duplicate files only save one entry ok with trunk file mode
+ * bug fixed: sync correctly with more binlog files
+ * fdfs_file_info query file info from storage server
+ * bug fixed: php extension compile error using gcc 4.6.1 as:
+ variable 'store_path_index' set but not used
+ * bug fixed: delete the metadata of trunked file correctly
+ * bug fixed: append file ok when check duplicate is on
+ * storage/trunk_mgr/trunk_shared.[hc]: trunk_file_stat_func do not
+ use function pointer
+ * bug fixed: storage/trunk_mgr/trunk_shared.c base64_decode_auto
+ overflow 1 byte
+ * bug fixed: delete slave file correctly
+ * bug fixed: remove debug info
+ * md5 function name changed to avoid conflict
+
+Version 3.03 2011-10-16
+ * ignore existed link when sync link file
+ * http token checking support persistent token
+ * add functions: storage_file_exist and storage_file_exist1
+ * php minfo add fastdfs version info
+ * make.sh changed
+ * client move libevent dependency
+
+Version 3.02 2011-09-18
+ * bug fixed: tracker_mem_check_add_tracker_servers add tracker server
+ correctly
+ * php client compile ok with php 5.2.17
+ * re-select trunk server ok
+
+Version 3.01 2011-07-31
+ * bug fixed: tracker_get_connection_ex and tracker_get_connection_r_ex
+ connect two times with multi tracker servers
+ * bug fixed: tracker_mem_check_add_tracker_servers condition not correct
+ * all logError add source filename and line
+ * php extension support upload file callback
+ * php extension support download file callback
+
+Version 3.00 2011-06-19
+ * mass small files optimization
+ * add fixed block memory pool: common/fast_mblock.c
+ * bug fixed: tracker_mem.c do NOT clear g_groups fields
+ * bug fixed: slave file and appender file download ok
+ * bug fixed: tracker / storage run by group / user, set file owner
+ * tracker server support leader
+ * client support static library
+ * client_func.h add functions fdfs_tracker_group_equals and
+ fdfs_get_file_ext_name
+ * bug fixed: test/dfs_func_pc.c compile ok
+ * storage server check free space enough when upload a file
+
+Version 2.09 2011-02-19
+ * bug fixed: write_to_binlog_index then increase g_binlog_index (feedback
+ by koolcoy)
+ * disk read / write supports direct mode (avoid caching by the file system)
+
+Version 2.08 2011-01-30
+ * bug fixed: fdfs_trackerd.c set g_tracker_thread_count to 0
+ * add cmd TRACKER_PROTO_CMD_SERVER_LIST_ONE_GROUP to support list one group
+ * support disk recovery automatically
+ * support total_upload_bytes, success_upload_bytes, total_download_bytes and
+ success_download_bytes etc. 18 stat fields
+ * tracker data file storage_groups.dat changes to storage_groups_new.dat, and
+ storage_servers.dat changes to storage_servers_new.dat
+ * support file append, add tests: fdfs_appender_test and fdfs_appender_test1
+ * storage_dio.c: dio_deal_task split to several functions
+ * tracker http check thread exit normally
+ * function fdfs_get_file_info_ex changed, add function fdfs_get_file_info_ex1
+ * fix some type cast error when compile with c++
+ * client add tools: fdfs_upload_appender and fdfs_append_file
+
+Version 2.07 2011-01-09
+ * slave file's prefix name can be empty
+ * FDFS_MAX_GROUPS change from 64 to 512
+ * file size field in the file id changed: high 32 bits is random integer
+ when the file size < 2GB and the highest bit set to 1
+ * tracker_service.c: in function list_group_storages, use strcpy
+ intead of memcpy
+ * php extension add function fastdfs_tracker_delete_storage
+ * client add tool: fdfs_file_info to get file info, including file size,
+ create timestamp, source storage ip address and crc32 signature
+ * fdfs_upload_file.c: omit more error info when the local file not exist
+
+Version 2.06 2010-12-26
+ * sync file op: do not sync the file which exists on dest storage server
+ and the file size are same
+ * bug fixed: sync copy file will clear the existed file on dest storage
+ server (truncate the file size to 0), this bug caused by V2.04
+ * bug fixed: make temp file discard system function mkstemp,
+ use file sequence No. with pthread_mutex_lock
+ * bug fixed: function fastdfs_tracker_list_groups, when parameter group_name
+ is null or empty string, return all groups info
+ * bug fixed: upload a file extends 2GB will fail
+ * bug fixed: tracker to tracker sync system data files, in function:
+ tracker_mem_get_tracker_server, pTrackerStatus not be set properly
+
+Version 2.05 2010-12-05
+ * client/fdfs_monitor.c: add sync delay time
+ * tracker/fast_task_queue.c: pTask->data = pTask->arg + arg_size;
+ change to: pTask->data = (char *)pTask->arg + arg_size;
+ * bug fixed: storage_sync.c line 237 cause core dump in Ubuntu 10.04
+ * upload file test use mmap, support more test_upload processes
+ * client add three tools: fdfs_upload_file, fdfs_download_file and
+ fdfs_delete_file
+
+Version 2.04 2010-11-19
+ * storage.conf: tracker server ip can NOT be 127.0.0.1
+ * do not catch signal SIGABRT
+ * strerror change to STRERROR macro
+ * sync copy file use temp filename first, rename to the correct filename
+ when sync done
+ * file id use 4 bytes CRC32 signature instead of random number
+ * add file: client/fdfs_crc32.c
+ * one of file hash code signature function change from APHash_ex
+ to simple_hash_ex
+ * bug fixed: when fdfs_storaged quit, maybe write to binlog file fail,
+ the error info is "Bad file descriptor"
+
+Version 2.03 2010-11-08
+ * bug fixed: core dump when http.need_find_content_type=false and
+ http.anti_steal.check_token=true
+ * storage server add join_time field (create timestamp of this storage)
+ * tracker server fetch system files from other tracker server when
+ first storage server join in (tracker to tracker sync system files)
+ * tracker server changes the old ip address to the new address when the
+ storage server ip address changed
+ * tracker to tracker sync system data files in some case, multi tracker
+ server supported well
+
+Version 2.02 2010-10-28
+ * get parameters function from tracker server changed,
+ add paramter: storage_sync_file_max_delay
+ * local ip functions move to common/local_ip_func.c
+ * when query all storage servers to store, do not increase the current
+ write server index
+ * struct FDFSHTTPParams add field: need_find_content_type
+ * symbol link client library to /usr/lib64 in 64 bits OS
+ * storage_client.c: deal file extension name correctly
+
+Version 2.01 2010-10-17
+ * client/fdfs_monitor.c can specify tracker server
+ * micro STORAGE_STORE_PATH_PREFIX_CHAR change to
+ FDFS_STORAGE_STORE_PATH_PREFIX_CHAR
+ * php extension can set log filename
+ * php extension add function: fastdfs_client_version
+ * bug fixed: client/tracker_client.c tracker_get_connection_ex NULL pointer
+ * set max core dump file size to at least 256MB when DEBUG_FLAG is on,
+ make sure to generate core file when core dump with DEBUG_FLAG on
+ * upload file can get available storage server list of the group,
+ add command TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL and
+ TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL
+ * bug fixed: storage core dump in some case
+
+Version 2.00 2010-08-22
+ * tracker network io use libevent instead of traditional io model
+ * storage network io use libevent instead of traditional io model
+ * storage disk read/write use separate threads
+ * tracker_mem.c malloc single group and storage struct, remove referer
+ * make install copy config files
+ * tracker.conf add two parameters: storage_sync_file_max_delay and
+ storage_sync_file_max_time
+ * client tracker_get_connection increase server_index correctly
+ * storage sync to storage server adds active test
+ * test programs compile ok
+
+Version 1.29 2010-06-30
+ * add files: tracker_dump.h and tracker_dump.c, tracker dump global vars
+ * add files: storage_dump.h and storage_dump.c, storage dump global vars
+ * sockopt.c: tcprecvfile and tcpdiscard add parameter total_recv_bytes
+ * storage server add fields: storage_port and storage_http_port
+ * auto rename synced remark files when the port of all storage servers
+ in a group changed to another port
+ * connect server support timeout, adding connect_timeout parameter in
+ config file
+ * log_init set log to cache to false (no cache)
+
+Version 1.28 2010-05-30
+ * tracker_servive.c: set current_write_group anyway when current group
+ out of space
+ * logger support context (multi instance)
+ * get storage servers by filename: if the file created one day ago (the create
+ timestamp of the file < current_time - 86400), any active storage server matches
+ * add files: common/pthread_func.h and common/pthread_func.c
+ * common/sched_thread.h, remove statement: extern bool g_continue_flag;
+ * client add libfastcommon
+ * global variables: g_base_path, g_network_timeout, g_version change to
+ g_fdfs_base_path, g_fdfs_network_timeout, g_fdfs_version
+ * common/fdfs_base64.h/c change name to common/base64.h/c
+ * make.sh use TARGET_PREFIX instead of TARGET_PATH
+ * protocol add ACTIVE_TEST, tracker and storage both support
+ * php client, bug fixed: fastdfs_connect_server, the sock must init to -1
+ * bug fixed: storage status not correct with multi tracker servers
+ * sync storage mark file and stat file to disk properly
+
+Version 1.27 2010-04-10
+ * storage.conf: add if_alias_prefix parameter to get the ip address of the
+ local host
+ * storage http support domain name
+ * php extension add some parameters in fastdfs_client.ini
+ * make.sh compile use debug mode
+ * type off_t change to int64_t
+ * redirect stdout and stderr to log file
+ * php extension list_groups add fields: version and http_domain
+
+Version 1.26 2010-02-28
+ * remove compile warning of logError
+ * ini reader support section
+ * bug fixed: tracker/tracker_mem.c sync storage server status
+ * use storage server http server port anyway
+ * bug fixed: ini reader can support relative config filename
+ * function enhancement: tracker server can check storage HTTP server alive
+
+Version 1.25 2010-02-04
+ * storage_sync.c if source file not exist when sync a file, change from
+ logWarning to logDebug
+ * filename buff size change from 64 to 128
+ * bug fixed: c client and php client, log not inited cause core dump when
+ call log functions
+ * can print stack trace when process core dumped in Linux server
+ * bug fixed: tracker/tracker_mem.c load storage servers fail with many groups
+ and storage servers
+ * common/sockopt.c remove debug info
+ * storage stat add fields: version
+ * auto adjust when storage server ip address changed
+ * bug fixed: when add a new storage server, other storage servers' status keep
+ the same, not changed
+ * add macros, compile passed in cygwin, thanks Seapeak
+ * write to system data file using lock
+ * common/ini_file_reader.c: use one context parameter, not two parameters
+ * storage status sync modified (the code of tracker and storage both changed)
+ * when recv kill signal, worker thread quit more quickly, daemon process
+ fdfs_trackerd and fdfs_storage quit very quickly when recv kill signal
+ * remove compile warning info of logError
+ * tracker server start more quickly with many groups and storage servers
+ * bug fixed: correct off_t printf format
+
+Version 1.24 2010-01-06
+ * call php_fdfs_close with TSRMLS_CC as php_fdfs_close(i_obj TSRMLS_CC)
+ * storage server to storage server report ip address as tracker client
+ * bug fixed: sendfile exceeds 2GB file in Linux
+ * bug fixed: delete storage server
+ * storage stat add fields: up_time and src_ip_addr
+ * big static or struct memeber char array buffer change to malloc in order to
+ decrease stack size
+ * FDFS_WRITE_BUFF_SIZE change from 512KB to 256KB
+ * bug fixed: client/storage_client.c, meta data miss when upload file
+ * decrease thread_stack_size default value in config files: tracker.conf
+ and storage.conf
+
+Version 1.23 2009-11-29
+ * remove unuseless variable "sleep_secs" in tracker_report_thread_entrance
+ * storage can bind an address when connect to other servers (as a client)
+ * common/md5.h fix UINT4 typedef wrong type in 64 bit OS
+ * client/fdfs_test.c: print the source ip address decoded from the remote
+ filename
+ * client add function fdfs_get_file_info
+ * php extension add functions: fastdfs_http_gen_token and fastdfs_get_file_info
+ * server process will exit when the http service starts fail
+ * support file group, a master file with many slave files whose file id can be
+ combined from master file id and prefix
+ * php client support uploading slave file
+ * ip address in filename change from host byte order to network byte order
+ * storage sync performance enhancement, using read buffer of 64KB to avoid
+ reading binlog file repeatly
+ * storage add prototol cmd: STORAGE_PROTO_CMD_QUERY_FILE_INFO
+ * FDFS_FILE_EXT_NAME_MAX_LEN changed from 5 to 6
+ * get file info support slave file
+ * storage server for uploading file support priority
+
+Version 1.22 2009-10-12
+ * bug fixed: common/shared_func.c allow_hosts array maybe overflow in some case
+ * tracker/tracker_mem.c: protocol TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL,
+ return at least a storage server when active storage
+ server count of the group > 0
+ * bug fixed: when client connection disconnected, always log debug or error info
+ * make.sh: default not install FastDFS services in Linux server
+ * common/sockopt.c: setsockopt level SOL_TCP only supported in Linux
+ * common/http_func.c: do not use function strsep because strsep is not portable
+ * client upload file support callback function
+ * client support multi tracker groups (multi FastDFS clusters)
+ * bug fixed: thread_stack_size not correct when the param thread_stack_size
+ not set in the config file
+ * supply php extension (directory name: php_client)
+ * c client reconnect server (tracker or storage) when network IO error
+ * c client: make tracker server index counter thread safely
+
+Version 1.21 2009-09-19
+ * bug fixed: when source storage server synced file to new storage server done,
+ it's status changed to ONLINE (should keep as ACTIVE, report by zhouzezhong)
+ * add thread_stack_size in config file, default value is 1MB (report by chhxo)
+ * tracker and storage server use setsockopt to keep alive
+ (report by zhouzezhong)
+ * bug fixed: storage server with multi-path, upload file fail when the free
+ space of each path <= reserved space (the total free space > reserved space,
+ report by zhouzezhong)
+ * storage_sync.c: when connect fail, do not change the dest storage server '
+ status to offline
+ * tracker_service.c and storage_service.c change log level from WARNING to DEBUG
+ when client connection disconnected (report by Jney402)
+ * bug fixed: tracker_client.c correct store_path_index return by tracker server
+ (report by happy_fastdfs)
+ * bug fixed: tracker_service.c when store_lookup set to 2 (load balance), use
+ another pthread lock to avoid long time lock waiting
+ (report by happy_fastdfs)
+ * add service shell scripts in directory: init.d
+ (services will auto installed on Linux, report by hugwww)
+
+Version 1.20 2009-09-05
+ * base64 use context, functions changed
+ * common/ini_file_reader.c: fix memory leak
+ * tracker server support HTTP protocol, one thread mode
+ * storage server support HTTP protocol, one thread mode
+ * fix bug: storage server rebuild, auto sync data correctly
+ * fix bug: sync data fail (correct storage server status)
+ * when storage server idle time exceeds check_active_interval seconds,
+ set it's status to offline
+ * tracker counter thread safely
+
+Version 1.19 2009-07-23
+ * use poll instead of select in sockopt.c
+ * hash.c use chain impl by self
+ * use FastDHT 1.09 client code
+ * ini reader support HTTP protocol, conf file can be an url
+ * correct test dir compile error
+ * use non-block socket to increase network IO performance
+ * add cmd TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL: query all storage servers
+ from which the file can be dowloaded
+ * while (1) ... break; changed to do ... while (0);
+
+Version 1.18 2009-05-24
+ * restart.sh only kill the programs match the program name and all parameters
+ * correct get local ip addresses
+ * common files do not use global vars like g_network_timeout and g_base_path
+ * download file support offset and download bytes
+ * hash function change type from unsigned int to signed int
+ * file size in file name support 64 bits, old bytes is 4, new bytes is 8
+
+Version 1.17 2009-03-19
+ * add test programs at sub directory test/
+ * common/shared_func.c: rindex change to strrchr, add #include
+ * support SunOS (Solaris), compile passed on SunOS 5.10
+ * support AIX, compile passed on AIX 5.3
+ * sys call statfs change to statvfs
+ * use scheduling thread to sync binlog buff / cache to disk, add parameter
+ "sync_binlog_buff_interval" to conf file storage.conf
+ * use FastDHT v1.07 client code
+
+Version 1.16 2009-02-14
+ * client can specify group name when upload file
+ * tracker_service.c: cmd dispatch changed to "switch ... case"
+ not "if ... else if"
+ * storage_service.c: call fdfs_quit before tracker_disconnect_server
+
+Version 1.15 2009-01-28
+ * use FastDHT v1.04 client code
+ * use FastDHT client thread safely
+
+Version 1.14 2009-01-18
+ * storage/storage_sync.c:
+ old: if (reader.sync_row_count % 1000 == 0)
+ new: if (reader.scan_row_count % 2000 == 0)
+ * little adjustment for common files can be used by FastDHT
+ * sched_thread.h /.c add global variable g_schedule_flag to quit normally
+ * shared_func.h / .c add function get_time_item_from_conf
+ * sched_thread.h /.c support time_base of task
+ * hash.h / .c add function CRC32, add hash function to support stream hash
+ * add FastDHT client files in storage/fdht_client/
+ * create symbol link when the file content is duplicate,
+ add item "check_file_duplicate" to conf file storage.conf
+ * use FastDHT v1.02 client code
+ * auto delete invalid entry in FastDHT when the source file does not exist
+
+Version 1.13 2008-11-29
+ * re-calculate group 's free space when one of it's storage servers'
+ free space increase
+ * add parameters: sync_interval, sync_start_time and sync_end_time to
+ storage.conf
+ * performance enhancement: log to buffer, flush to disk every interval seconds
+ * standard fds closed by daemon_init: 0(stdin), 1(stdout) and 2(stderr)
+ * fix bug: pthread_kill sometimes cause core dump when program terminated
+ * fix bug: sync.c open next binlog cause loop call
+
+Version 1.12 2008-11-12
+ * storage server support multi path (mount point)
+ * upload file support file ext name, add source storage ip address to filename
+ * add delete command to delete the invalid storage server
+ * add client functions which combine group name and filename to file id,
+ add anothor client test program: fdfs_test1.c to use file id
+ * client download file support callback function
+ * add protocol cmd TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE,
+ and client API add tracker_query_storage_update
+ * add protocol cmd TRACKER_PROTO_CMD_STORAGE_SYNC_REPORT to report last
+ synced timestamp as dest server
+ * fix sync old data files to new server bug
+ * fcntl change to pthread_mutex_lock
+
+Version 1.11 2008-10-04
+ * kill report and sync threads when recv terminate signal
+ * add item "store_server" in tracker.conf, by default use the first
+ storage server to store uploaded files
+ * ini_file_reader.c changed: a conf file can include other conf files
+ * some adjustment:
+ some macro name changed
+ add common_define.h
+ remove fdfs_define.c
+ fdfs_os_bits.h change to _os_bits.h
+
+Version 1.10 2008-09-20
+ * performance optimizing: use thread pool, create all work threads at startup
+ * trim function op in shared_func.c
+ * add Makefile template Makefile.in, delete Makefile and Makefile.freebsd
+ change make.sh to support all unix systems (passed in Linux and FreeBSD)
+
+Version 1.9 2008-09-14
+ * security enhancement: support allow hosts which can connect to the server
+ * server can be run by the specified group and user, set by the config file
+ * change make.sh and add file common/fdfs_os_bits.h,
+ remove the warning info of printf format for int64_t param in 64 bits system
+ * storage_client.c changed: auto connect to storage server when not connected
+ * change some macro name and function name in tracker/tracker_proto.h
+
+Version 1.8 2008-09-07
+ * communication protocol changed to support large file exceed 2GB:
+ # all integer field is 8 bytes big-endian
+ # group name fixed length: FDFS_GROUP_NAME_MAX_LEN bytes
+ * storage stat numbers (such as total_upload_count, success_upload_count)
+ use int64_t (8 bytes integer)
+ * ini_file_reader.c add function iniGetInt64Value
+ * sockopt.c add function tcpsetnonblockopt
+ * shared_func.c add function set_nonblock
+
+Version 1.7 2008-08-31
+ * performance optimizing:
+ # change fopen to syscall open
+ # increase the efficiency of socket functions tcpsenddata and tcprecvdata
+ * change the return value of socket funtions such as tcpsenddata,
+ tcprecvdata and connectserverbyip
+ old return value: result=1 for success, result != 1 fail
+ new return value: result=0 for success, result != 0 fail, return the error code
+ * log function enhancement:
+ # support log level
+ # parameter "log_level" added to server config file
+ # keep the log file opened to increase performance
+ * fix log format and parameter mismatched bug (check by printf)
+ * log CRIT message to log file when program exit unexpectedly
+ * Makefile add compile flag -D_FILE_OFFSET_BITS=64 to support large files
+ * change the type of file_size and file_offset to off_t
+ * change signal to sigaction
+ * fix client Makefile to compile library correctly
+ * restart.sh modified: use external command "expr" to replace shell command "let"
+
+Version 1.6 2008-08-24
+ * add restart daemon shell script: restart.sh
+ * use setrlimit to increase max open files if necessary
+ * security enhancement: the format of data filename must be: HH/HH/filename,
+ eg. B9/F4/SLI2NAAMRPR9r8.d
+ * fix bug: errno is not correct where the downloaded file does not exist,
+ communication is broken when the download file is a directory
+
+Version 1.5 2008-08-17
+ * add client function storage_download_file_to_file
+ * use pthread_attr_setstacksize to increase thread stack size to 1 MB
+ * use sendfile syscall to send file in Linux and FreeBSD
+ * fix bug: add O_TRUNC flag when open file to write
+ * remove warning info compiled by gcc 4.2
+ * fcntl set lock.l_len to 0
+
+Version 1.4 2008-08-10
+ * storage server recv file method change
+ old method: recv the whole file content/buff before write to file
+ new method: write to file once recv a certain bytes file buff, eg. 128KB buff size
+ * storage client and storage server send file method change
+ old method: get the whole file content/buff, then send to storage server
+ new method: send file to storage server more times. get a certain bytes file buff, then send to storage server
+ * upload file package remove the one pad byte field
+ * remove storage status FDFS_STORAGE_STATUS_DEACTIVE and add FDFS_STORAGE_STATUS_DELETED
+
+Version 1.3 2008-08-03
+ * fix bug: when meta data is empty, get meta data return error
+ * support java client
+ # memset response header to 0
+ # add group_name to upload file response package
+
+Version 1.2 2008-07-27
+ * add client function storage_set_metadata to support setting metadata(overwrite or merge)
+
+Version 1.1 2008-07-20
+ * implement storage disk report
+ * storing load balance between storage groups(volumes) when set store_lookup to 2
+
+Version 1.0 2008-07-12
+ * first version
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..578da81
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,188 @@
+Copy right 2009 Happy Fish / YuQing
+
+FastDFS may be copied only under the terms of the GNU General
+Public License V3, which may be found in the FastDFS source kit.
+Please visit the FastDFS Home Page for more detail.
+English language: http://english.csource.org/
+Chinese language: http://www.csource.org/
+
+#step 1. download FastDFS source package and unpack it,
+tar xzf FastDFS_v5.x.tar.gz
+#for example:
+tar xzf FastDFS_v5.01.tar.gz
+
+#step 2. enter the FastDFS dir
+cd FastDFS
+
+#step 3. execute:
+./make.sh
+
+#step 4. make install
+./make.sh install
+
+#step 5. edit/modify the config file of tracker and storage
+
+#step 6. run server programs
+#start the tracker server:
+/usr/local/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart
+#in Linux, you can start fdfs_trackerd as a service:
+/sbin/service fdfs_trackerd start
+
+#start the storage server:
+/usr/local/bin/fdfs_storaged /etc/fdfs/storage.conf restart
+#in Linux, you can start fdfs_storaged as a service:
+/sbin/service fdfs_storaged start
+
+#step 7. run test program
+#run the client test program:
+/usr/local/bin/fdfs_test
+/usr/local/bin/fdfs_test1
+#for example, upload a file:
+/usr/local/bin/fdfs_test conf/client.conf upload /usr/include/stdlib.h
+
+#step 8. run monitor program
+#run the monitor program:
+/usr/local/bin/fdfs_monitor
+
+
+tracker server config file sample please see conf/tracker.conf
+
+storage server config file sample please see conf/storage.conf
+
+client config file sample please see conf/client.conf
+
+
+Item detail
+1. server common items
+---------------------------------------------------
+| item name | type | default | Must |
+---------------------------------------------------
+| base_path | string | | Y |
+---------------------------------------------------
+| disabled | boolean| false | N |
+---------------------------------------------------
+| bind_addr | string | | N |
+---------------------------------------------------
+| network_timeout | int | 30(s) | N |
+---------------------------------------------------
+| max_connections | int | 256 | N |
+---------------------------------------------------
+| log_level | string | info | N |
+---------------------------------------------------
+| run_by_group | string | | N |
+---------------------------------------------------
+| run_by_user | string | | N |
+---------------------------------------------------
+| allow_hosts | string | * | N |
+---------------------------------------------------
+| sync_log_buff_interval| int | 10(s) | N |
+---------------------------------------------------
+| thread_stack_size | string | 1M | N |
+---------------------------------------------------
+memo:
+ * base_path is the base path of sub dirs:
+ data and logs. base_path must exist and it's sub dirs will
+ be automatically created if not exist.
+ $base_path/data: store data files
+ $base_path/logs: store log files
+ * log_level is the standard log level as syslog, case insensitive
+ # emerg: for emergency
+ # alert
+ # crit: for critical
+ # error
+ # warn: for warning
+ # notice
+ # info
+ # debug
+ * allow_hosts can ocur more than once, host can be hostname or ip address,
+ "*" means match all ip addresses, can use range like this: 10.0.1.[1-15,20]
+ or host[01-08,20-25].domain.com, for example:
+ allow_hosts=10.0.1.[1-15,20]
+ allow_hosts=host[01-08,20-25].domain.com
+
+2. tracker server items
+---------------------------------------------------
+| item name | type | default | Must |
+---------------------------------------------------
+| port | int | 22000 | N |
+---------------------------------------------------
+| store_lookup | int | 0 | N |
+---------------------------------------------------
+| store_group | string | | N |
+---------------------------------------------------
+| store_server | int | 0 | N |
+---------------------------------------------------
+| store_path | int | 0 | N |
+---------------------------------------------------
+| download_server | int | 0 | N |
+---------------------------------------------------
+| reserved_storage_space| string | 1GB | N |
+---------------------------------------------------
+
+memo:
+ * the value of store_lookup is:
+ 0: round robin (default)
+ 1: specify group
+ 2: load balance (supported since V1.1)
+ * store_group is the name of group to store files.
+ when store_lookup set to 1(specify group),
+ store_group must be set to a specified group name.
+ * reserved_storage_space is the reserved storage space for system
+ or other applications. if the free(available) space of any stoarge
+ server in a group <= reserved_storage_space, no file can be uploaded
+ to this group (since V1.1)
+ bytes unit can be one of follows:
+ # G or g for gigabyte(GB)
+ # M or m for megabyte(MB)
+ # K or k for kilobyte(KB)
+ # no unit for byte(B)
+
+3. storage server items
+-------------------------------------------------
+| item name | type | default | Must |
+-------------------------------------------------
+| group_name | string | | Y |
+-------------------------------------------------
+| tracker_server | string | | Y |
+-------------------------------------------------
+| port | int | 23000 | N |
+-------------------------------------------------
+| heart_beat_interval | int | 30(s) | N |
+-------------------------------------------------
+| stat_report_interval| int | 300(s) | N |
+-------------------------------------------------
+| sync_wait_msec | int | 100(ms) | N |
+-------------------------------------------------
+| sync_interval | int | 0(ms) | N |
+-------------------------------------------------
+| sync_start_time | string | 00:00 | N |
+-------------------------------------------------
+| sync_end_time | string | 23:59 | N |
+-------------------------------------------------
+| store_path_count | int | 1 | N |
+-------------------------------------------------
+| store_path0 | string |base_path| N |
+-------------------------------------------------
+| store_path# | string | | N |
+-------------------------------------------------
+|subdir_count_per_path| int | 256 | N |
+-------------------------------------------------
+|check_file_duplicate | boolean| 0 | N |
+-------------------------------------------------
+| key_namespace | string | | N |
+-------------------------------------------------
+| keep_alive | boolean| 0 | N |
+-------------------------------------------------
+| sync_binlog_buff_interval| int | 60s | N |
+-------------------------------------------------
+
+memo:
+ * tracker_server can ocur more than once, and tracker_server format is
+ "host:port", host can be hostname or ip address.
+ * store_path#, # for digital, based 0
+ * check_file_duplicate: when set to true, must work with FastDHT server,
+ more detail please see INSTALL of FastDHT. FastDHT download page:
+ http://code.google.com/p/fastdht/downloads/list
+ * key_namespace: FastDHT key namespace, can't be empty when
+ check_file_duplicate is true. the key namespace should short as possible
+
diff --git a/README.md b/README.md
index 8b6e2da..5aa4cee 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,44 @@
-fastdfs
-=======
+Copyright (C) 2008 Happy Fish / YuQing
-FastDFS is an open source high performance distributed file system (DFS). It's major functions include: file storing, file syncing and file accessing, and design for high capacity and load balance.
+FastDFS may be copied only under the terms of the GNU General
+Public License V3, which may be found in the FastDFS source kit.
+Please visit the FastDFS Home Page for more detail.
+English language: http://english.csource.org/
+Chinese language: http://www.csource.org/
+
+
+FastDFS is an open source high performance distributed file system. It's major
+functions include: file storing, file syncing and file accessing (file uploading
+and file downloading), and it can resolve the high capacity and load balancing
+problem. FastDFS should meet the requirement of the website whose service based
+on files such as photo sharing site and video sharing site.
+
+FastDFS has two roles: tracker and storage. The tracker takes charge of
+scheduling and load balancing for file access. The storage store files and it's
+function is file management including: file storing, file syncing, providing file
+access interface. It also manage the meta data which are attributes representing
+as key value pair of the file. For example: width=1024, the key is "width" and
+the value is "1024".
+
+The tracker and storage contain one or more servers. The servers in the tracker
+or storage cluster can be added to or removed from the cluster by any time without
+affecting the online services. The servers in the tracker cluster are peer to peer.
+
+The storarge servers organizing by the file volume/group to obtain high capacity.
+The storage system contains one or more volumes whose files are independent among
+these volumes. The capacity of the whole storage system equals to the sum of all
+volumes' capacity. A file volume contains one or more storage servers whose files
+are same among these servers. The servers in a file volume backup each other,
+and all these servers are load balancing. When adding a storage server to a
+volume, files already existing in this volume are replicated to this new server
+automatically, and when this replication done, system will switch this server
+online to providing storage services.
+
+When the whole storage capacity is insufficiency, you can add one or more
+volumes to expand the storage capacity. To do this, you need to add one or
+more storage servers.
+
+The identification of a file is composed of two parts: the volume name and
+the file name.
+
+Client test code use client library please refer to the directory: client/test.
diff --git a/client/Makefile.in b/client/Makefile.in
new file mode 100644
index 0000000..a27abb8
--- /dev/null
+++ b/client/Makefile.in
@@ -0,0 +1,115 @@
+.SUFFIXES: .c .o .lo
+
+COMPILE = $(CC) $(CFLAGS)
+ENABLE_STATIC_LIB = $(ENABLE_STATIC_LIB)
+ENABLE_SHARED_LIB = $(ENABLE_SHARED_LIB)
+INC_PATH = -I../common -I../tracker -I/usr/local/include
+LIB_PATH = $(LIBS)
+TARGET_PATH = $(TARGET_PREFIX)/bin
+TARGET_LIB = $(TARGET_PREFIX)/lib
+TARGET_INC = $(TARGET_PREFIX)/include
+CONFIG_PATH = $(TARGET_CONF_PATH)
+
+FAST_STATIC_OBJS = ../common/hash.o ../common/chain.o \
+ ../common/shared_func.o ../common/ini_file_reader.o \
+ ../common/logger.o ../common/sockopt.o \
+ ../common/base64.o ../common/sched_thread.o \
+ ../common/http_func.o ../common/md5.o \
+ ../common/pthread_func.o ../common/local_ip_func.o \
+ ../common/avl_tree.o ../common/connection_pool.o
+
+FDFS_STATIC_OBJS = ../common/fdfs_global.o ../common/fdfs_http_shared.o \
+ ../common/mime_file_parser.o ../tracker/tracker_proto.o \
+ ../tracker/fdfs_shared_func.o \
+ ../storage/trunk_mgr/trunk_shared.o \
+ tracker_client.o client_func.o \
+ client_global.o storage_client.o
+
+STATIC_OBJS = $(FAST_STATIC_OBJS) $(FDFS_STATIC_OBJS)
+
+FAST_SHARED_OBJS = ../common/hash.lo ../common/chain.lo \
+ ../common/shared_func.lo ../common/ini_file_reader.lo \
+ ../common/logger.lo ../common/sockopt.lo \
+ ../common/base64.lo ../common/sched_thread.lo \
+ ../common/http_func.lo ../common/md5.lo \
+ ../common/pthread_func.lo ../common/local_ip_func.lo \
+ ../common/avl_tree.lo ../common/connection_pool.lo
+
+FDFS_SHARED_OBJS = ../common/fdfs_global.lo ../common/fdfs_http_shared.lo \
+ ../common/mime_file_parser.lo ../tracker/tracker_proto.lo \
+ ../tracker/fdfs_shared_func.lo \
+ ../storage/trunk_mgr/trunk_shared.lo \
+ tracker_client.lo client_func.lo \
+ client_global.lo storage_client.lo
+
+FAST_HEADER_FILES = ../common/common_define.h ../common/hash.h \
+ ../common/chain.h ../common/logger.h \
+ ../common/base64.h ../common/shared_func.h \
+ ../common/pthread_func.h ../common/ini_file_reader.h \
+ ../common/sockopt.h ../common/sched_thread.h \
+ ../common/http_func.h ../common/md5.h ../common/_os_bits.h \
+ ../common/local_ip_func.h ../common/avl_tree.h \
+ ../common/connection_pool.h
+
+FDFS_HEADER_FILES = ../common/fdfs_define.h ../common/fdfs_global.h \
+ ../common/mime_file_parser.h ../common/fdfs_http_shared.h \
+ ../tracker/tracker_types.h ../tracker/tracker_proto.h \
+ ../tracker/fdfs_shared_func.h \
+ ../storage/trunk_mgr/trunk_shared.h \
+ tracker_client.h storage_client.h storage_client1.h \
+ client_func.h client_global.h fdfs_client.h
+
+ALL_OBJS = $(STATIC_OBJS) $(FAST_SHARED_OBJS) $(FDFS_SHARED_OBJS)
+
+ALL_PRGS = fdfs_monitor fdfs_test fdfs_test1 fdfs_crc32 fdfs_upload_file \
+ fdfs_download_file fdfs_delete_file fdfs_file_info \
+ fdfs_appender_test fdfs_appender_test1 fdfs_append_file \
+ fdfs_upload_appender
+
+STATIC_LIBS = libfastcommon.a libfdfsclient.a
+
+SHARED_LIBS = libfastcommon.so.1 libfdfsclient.so.1
+
+ALL_LIBS = $(STATIC_LIBS) $(SHARED_LIBS)
+
+all: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)
+libfastcommon.so.1:
+ $(COMPILE) -o $@ $< -shared $(FAST_SHARED_OBJS) $(LIB_PATH)
+ ln -fs libfastcommon.so.1 libfastcommon.so
+libfdfsclient.so.1:
+ $(COMPILE) -o $@ $< -shared $(FDFS_SHARED_OBJS) $(LIB_PATH) -L. -lfastcommon
+ ln -fs libfdfsclient.so.1 libfdfsclient.so
+libfastcommon.a:
+ ar cru $@ $< $(FAST_STATIC_OBJS)
+libfdfsclient.a:
+ ar cru $@ $< $(FDFS_STATIC_OBJS)
+.o:
+ $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)
+.c:
+ $(COMPILE) -o $@ $< $(STATIC_OBJS) $(LIB_PATH) $(INC_PATH)
+.c.o:
+ $(COMPILE) -c -o $@ $< $(INC_PATH)
+.c.lo:
+ $(COMPILE) -c -fPIC -o $@ $< $(INC_PATH)
+install:
+ mkdir -p $(TARGET_PATH)
+ mkdir -p $(CONFIG_PATH)
+ mkdir -p $(TARGET_LIB)
+ cp -f $(ALL_PRGS) $(TARGET_PATH)
+ if [ $(ENABLE_STATIC_LIB) -eq 1 ]; then cp -f $(STATIC_LIBS) $(TARGET_LIB); fi
+ if [ $(ENABLE_SHARED_LIB) -eq 1 ]; then cp -f $(SHARED_LIBS) $(TARGET_LIB); fi
+ if [ $(ENABLE_SHARED_LIB) -eq 1 ]; then ln -fs $(TARGET_LIB)/libfastcommon.so.1 $(TARGET_LIB)/libfastcommon.so; fi
+ if [ $(ENABLE_SHARED_LIB) -eq 1 ]; then ln -fs $(TARGET_LIB)/libfdfsclient.so.1 $(TARGET_LIB)/libfdfsclient.so; fi
+
+ if [ ! -f $(CONFIG_PATH)/client.conf ]; then cp -f ../conf/client.conf ../conf/http.conf $(CONFIG_PATH); fi
+
+ mkdir -p $(TARGET_INC)
+ mkdir -p $(TARGET_INC)/fastcommon
+ mkdir -p $(TARGET_INC)/fastdfs
+ cp -f $(FAST_HEADER_FILES) $(TARGET_INC)/fastcommon
+ cp -f $(FDFS_HEADER_FILES) $(TARGET_INC)/fastdfs
+
+ if [ $(ENABLE_SHARED_LIB) -eq 1 -a $(TARGET_LIB) = "/usr/local/lib" ]; then sh ./fdfs_link_library.sh; fi
+clean:
+ rm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) libfastcommon.so libfdfsclient.so
+
diff --git a/client/client_func.c b/client/client_func.c
new file mode 100644
index 0000000..c1d7c49
--- /dev/null
+++ b/client/client_func.c
@@ -0,0 +1,537 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+//client_func.c
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_define.h"
+#include "logger.h"
+#include "fdfs_global.h"
+#include "base64.h"
+#include "sockopt.h"
+#include "shared_func.h"
+#include "ini_file_reader.h"
+#include "connection_pool.h"
+#include "tracker_types.h"
+#include "tracker_proto.h"
+#include "client_global.h"
+#include "client_func.h"
+
+static int storage_cmp_by_ip_and_port(const void *p1, const void *p2)
+{
+ int res;
+
+ res = strcmp(((ConnectionInfo *)p1)->ip_addr, \
+ ((ConnectionInfo *)p2)->ip_addr);
+ if (res != 0)
+ {
+ return res;
+ }
+
+ return ((ConnectionInfo *)p1)->port - \
+ ((ConnectionInfo *)p2)->port;
+}
+
+static void insert_into_sorted_servers(TrackerServerGroup *pTrackerGroup, \
+ ConnectionInfo *pInsertedServer)
+{
+ ConnectionInfo *pDestServer;
+ for (pDestServer=pTrackerGroup->servers+pTrackerGroup->server_count; \
+ pDestServer>pTrackerGroup->servers; pDestServer--)
+ {
+ if (storage_cmp_by_ip_and_port(pInsertedServer, \
+ pDestServer-1) > 0)
+ {
+ memcpy(pDestServer, pInsertedServer, \
+ sizeof(ConnectionInfo));
+ return;
+ }
+
+ memcpy(pDestServer, pDestServer-1, sizeof(ConnectionInfo));
+ }
+
+ memcpy(pDestServer, pInsertedServer, sizeof(ConnectionInfo));
+}
+
+static int copy_tracker_servers(TrackerServerGroup *pTrackerGroup, \
+ const char *filename, char **ppTrackerServers)
+{
+ char **ppSrc;
+ char **ppEnd;
+ ConnectionInfo destServer;
+ char *pSeperator;
+ char szHost[128];
+ int nHostLen;
+
+ memset(&destServer, 0, sizeof(ConnectionInfo));
+ destServer.sock = -1;
+
+ ppEnd = ppTrackerServers + pTrackerGroup->server_count;
+
+ pTrackerGroup->server_count = 0;
+ for (ppSrc=ppTrackerServers; ppSrc= sizeof(szHost))
+ {
+ nHostLen = sizeof(szHost) - 1;
+ }
+ memcpy(szHost, *ppSrc, nHostLen);
+ szHost[nHostLen] = '\0';
+
+ if (getIpaddrByName(szHost, destServer.ip_addr, \
+ sizeof(destServer.ip_addr)) == INADDR_NONE)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "conf file \"%s\", " \
+ "host \"%s\" is invalid", \
+ __LINE__, filename, szHost);
+ return EINVAL;
+ }
+ destServer.port = atoi(pSeperator+1);
+ if (destServer.port <= 0)
+ {
+ destServer.port = FDFS_TRACKER_SERVER_DEF_PORT;
+ }
+
+ if (bsearch(&destServer, pTrackerGroup->servers, \
+ pTrackerGroup->server_count, \
+ sizeof(ConnectionInfo), \
+ storage_cmp_by_ip_and_port) == NULL)
+ {
+ insert_into_sorted_servers(pTrackerGroup, &destServer);
+ pTrackerGroup->server_count++;
+ }
+ }
+
+ /*
+ {
+ ConnectionInfo *pServer;
+ for (pServer=pTrackerGroup->servers; pServerservers+ \
+ pTrackerGroup->server_count; pServer++)
+ {
+ //printf("server=%s:%d\n", \
+ pServer->ip_addr, pServer->port);
+ }
+ }
+ */
+
+ return 0;
+}
+
+int fdfs_load_tracker_group_ex(TrackerServerGroup *pTrackerGroup, \
+ const char *conf_filename, IniContext *pIniContext)
+{
+ int result;
+ char *ppTrackerServers[FDFS_MAX_TRACKERS];
+
+ if ((pTrackerGroup->server_count=iniGetValues(NULL, "tracker_server", \
+ pIniContext, ppTrackerServers, FDFS_MAX_TRACKERS)) <= 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "conf file \"%s\", " \
+ "get item \"tracker_server\" fail", \
+ __LINE__, conf_filename);
+ return ENOENT;
+ }
+
+ pTrackerGroup->servers = (ConnectionInfo *)malloc( \
+ sizeof(ConnectionInfo) * pTrackerGroup->server_count);
+ if (pTrackerGroup->servers == NULL)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "malloc %d bytes fail", __LINE__, \
+ (int)sizeof(ConnectionInfo) * \
+ pTrackerGroup->server_count);
+ pTrackerGroup->server_count = 0;
+ return errno != 0 ? errno : ENOMEM;
+ }
+
+ memset(pTrackerGroup->servers, 0, \
+ sizeof(ConnectionInfo) * pTrackerGroup->server_count);
+ if ((result=copy_tracker_servers(pTrackerGroup, conf_filename, \
+ ppTrackerServers)) != 0)
+ {
+ pTrackerGroup->server_count = 0;
+ free(pTrackerGroup->servers);
+ pTrackerGroup->servers = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+int fdfs_load_tracker_group(TrackerServerGroup *pTrackerGroup, \
+ const char *conf_filename)
+{
+ IniContext iniContext;
+ int result;
+
+ if ((result=iniLoadFromFile(conf_filename, &iniContext)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "load conf file \"%s\" fail, ret code: %d", \
+ __LINE__, conf_filename, result);
+ return result;
+ }
+
+ result = fdfs_load_tracker_group_ex(pTrackerGroup, conf_filename, \
+ &iniContext);
+ iniFreeContext(&iniContext);
+
+ return result;
+}
+
+static int fdfs_get_params_from_tracker(bool *use_storage_id)
+{
+ IniContext iniContext;
+ int result;
+ bool continue_flag;
+
+ continue_flag = false;
+ if ((result=fdfs_get_ini_context_from_tracker(&g_tracker_group, \
+ &iniContext, &continue_flag, false, NULL)) != 0)
+ {
+ return result;
+ }
+
+ *use_storage_id = iniGetBoolValue(NULL, "use_storage_id", \
+ &iniContext, false);
+ iniFreeContext(&iniContext);
+
+ if (*use_storage_id)
+ {
+ result = fdfs_get_storage_ids_from_tracker_group( \
+ &g_tracker_group);
+ }
+
+ return result;
+}
+
+static int fdfs_client_do_init_ex(TrackerServerGroup *pTrackerGroup, \
+ const char *conf_filename, IniContext *iniContext)
+{
+ char *pBasePath;
+ int result;
+ bool use_storage_id;
+ bool load_fdfs_parameters_from_tracker;
+
+ pBasePath = iniGetStrValue(NULL, "base_path", iniContext);
+ if (pBasePath == NULL)
+ {
+ strcpy(g_fdfs_base_path, "/tmp");
+ }
+ else
+ {
+ snprintf(g_fdfs_base_path, sizeof(g_fdfs_base_path),
+ "%s", pBasePath);
+ chopPath(g_fdfs_base_path);
+ if (!fileExists(g_fdfs_base_path))
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "\"%s\" can't be accessed, error info: %s", \
+ __LINE__, g_fdfs_base_path, STRERROR(errno));
+ return errno != 0 ? errno : ENOENT;
+ }
+ if (!isDir(g_fdfs_base_path))
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "\"%s\" is not a directory!", \
+ __LINE__, g_fdfs_base_path);
+ return ENOTDIR;
+ }
+ }
+
+ g_fdfs_connect_timeout = iniGetIntValue(NULL, "connect_timeout", \
+ iniContext, DEFAULT_CONNECT_TIMEOUT);
+ if (g_fdfs_connect_timeout <= 0)
+ {
+ g_fdfs_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
+ }
+
+ g_fdfs_network_timeout = iniGetIntValue(NULL, "network_timeout", \
+ iniContext, DEFAULT_NETWORK_TIMEOUT);
+ if (g_fdfs_network_timeout <= 0)
+ {
+ g_fdfs_network_timeout = DEFAULT_NETWORK_TIMEOUT;
+ }
+
+ if ((result=fdfs_load_tracker_group_ex(pTrackerGroup, \
+ conf_filename, iniContext)) != 0)
+ {
+ return result;
+ }
+
+ g_anti_steal_token = iniGetBoolValue(NULL, \
+ "http.anti_steal.check_token", \
+ iniContext, false);
+ if (g_anti_steal_token)
+ {
+ char *anti_steal_secret_key;
+
+ anti_steal_secret_key = iniGetStrValue(NULL, \
+ "http.anti_steal.secret_key", \
+ iniContext);
+ if (anti_steal_secret_key == NULL || \
+ *anti_steal_secret_key == '\0')
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "param \"http.anti_steal.secret_key\""\
+ " not exist or is empty", __LINE__);
+ return EINVAL;
+ }
+
+ buffer_strcpy(&g_anti_steal_secret_key, anti_steal_secret_key);
+ }
+
+ g_tracker_server_http_port = iniGetIntValue(NULL, \
+ "http.tracker_server_port", \
+ iniContext, 80);
+ if (g_tracker_server_http_port <= 0)
+ {
+ g_tracker_server_http_port = 80;
+ }
+
+ if ((result=fdfs_connection_pool_init(conf_filename, iniContext)) != 0)
+ {
+ return result;
+ }
+
+ load_fdfs_parameters_from_tracker = iniGetBoolValue(NULL, \
+ "load_fdfs_parameters_from_tracker", \
+ iniContext, false);
+ if (load_fdfs_parameters_from_tracker)
+ {
+ fdfs_get_params_from_tracker(&use_storage_id);
+ }
+ else
+ {
+ use_storage_id = iniGetBoolValue(NULL, "use_storage_id", \
+ iniContext, false);
+ if (use_storage_id)
+ {
+ result = fdfs_load_storage_ids_from_file( \
+ conf_filename, iniContext);
+ }
+ }
+
+#ifdef DEBUG_FLAG
+ logDebug("base_path=%s, " \
+ "connect_timeout=%d, "\
+ "network_timeout=%d, "\
+ "tracker_server_count=%d, " \
+ "anti_steal_token=%d, " \
+ "anti_steal_secret_key length=%d, " \
+ "use_connection_pool=%d, " \
+ "g_connection_pool_max_idle_time=%ds, " \
+ "use_storage_id=%d, storage server id count: %d\n", \
+ g_fdfs_base_path, g_fdfs_connect_timeout, \
+ g_fdfs_network_timeout, pTrackerGroup->server_count, \
+ g_anti_steal_token, g_anti_steal_secret_key.length, \
+ g_use_connection_pool, g_connection_pool_max_idle_time, \
+ use_storage_id, g_storage_id_count);
+#endif
+
+ return 0;
+}
+
+int fdfs_client_init_from_buffer_ex(TrackerServerGroup *pTrackerGroup, \
+ const char *buffer)
+{
+ IniContext iniContext;
+ char *new_buff;
+ int result;
+
+ new_buff = strdup(buffer);
+ if (new_buff == NULL)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "strdup %d bytes fail", __LINE__, (int)strlen(buffer));
+ return ENOMEM;
+ }
+
+ result = iniLoadFromBuffer(new_buff, &iniContext);
+ free(new_buff);
+ if (result != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "load parameters from buffer fail, ret code: %d", \
+ __LINE__, result);
+ return result;
+ }
+
+ result = fdfs_client_do_init_ex(pTrackerGroup, "buffer", &iniContext);
+ iniFreeContext(&iniContext);
+ return result;
+}
+
+int fdfs_client_init_ex(TrackerServerGroup *pTrackerGroup, \
+ const char *conf_filename)
+{
+ IniContext iniContext;
+ int result;
+
+ if ((result=iniLoadFromFile(conf_filename, &iniContext)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "load conf file \"%s\" fail, ret code: %d", \
+ __LINE__, conf_filename, result);
+ return result;
+ }
+
+ result = fdfs_client_do_init_ex(pTrackerGroup, conf_filename, \
+ &iniContext);
+ iniFreeContext(&iniContext);
+ return result;
+}
+
+int fdfs_copy_tracker_group(TrackerServerGroup *pDestTrackerGroup, \
+ TrackerServerGroup *pSrcTrackerGroup)
+{
+ int bytes;
+ ConnectionInfo *pDestServer;
+ ConnectionInfo *pDestServerEnd;
+
+ bytes = sizeof(ConnectionInfo) * pSrcTrackerGroup->server_count;
+ pDestTrackerGroup->servers = (ConnectionInfo *)malloc(bytes);
+ if (pDestTrackerGroup->servers == NULL)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "malloc %d bytes fail", __LINE__, bytes);
+ return errno != 0 ? errno : ENOMEM;
+ }
+
+ pDestTrackerGroup->server_index = 0;
+ pDestTrackerGroup->server_count = pSrcTrackerGroup->server_count;
+ memcpy(pDestTrackerGroup->servers, pSrcTrackerGroup->servers, bytes);
+
+ pDestServerEnd = pDestTrackerGroup->servers + \
+ pDestTrackerGroup->server_count;
+ for (pDestServer=pDestTrackerGroup->servers; \
+ pDestServersock = -1;
+ }
+
+ return 0;
+}
+
+bool fdfs_tracker_group_equals(TrackerServerGroup *pGroup1, \
+ TrackerServerGroup *pGroup2)
+{
+ ConnectionInfo *pServer1;
+ ConnectionInfo *pServer2;
+ ConnectionInfo *pEnd1;
+
+ if (pGroup1->server_count != pGroup1->server_count)
+ {
+ return false;
+ }
+
+ pEnd1 = pGroup1->servers + pGroup1->server_count;
+ pServer1 = pGroup1->servers;
+ pServer2 = pGroup2->servers;
+ while (pServer1 < pEnd1)
+ {
+ if (!(strcmp(pServer1->ip_addr, pServer2->ip_addr) == 0 &&
+ pServer1->port == pServer2->port))
+ {
+ return false;
+ }
+
+ pServer1++;
+ pServer2++;
+ }
+
+ return true;
+}
+
+void fdfs_client_destroy_ex(TrackerServerGroup *pTrackerGroup)
+{
+ if (pTrackerGroup->servers != NULL)
+ {
+ free(pTrackerGroup->servers);
+ pTrackerGroup->servers = NULL;
+
+ pTrackerGroup->server_count = 0;
+ pTrackerGroup->server_index = 0;
+ }
+}
+
+const char *fdfs_get_file_ext_name_ex(const char *filename,
+ const bool twoExtName)
+{
+ const char *fileExtName;
+ const char *p;
+ const char *pStart;
+ int extNameLen;
+
+ fileExtName = strrchr(filename, '.');
+ if (fileExtName == NULL)
+ {
+ return NULL;
+ }
+
+ extNameLen = strlen(fileExtName + 1);
+ if (extNameLen > FDFS_FILE_EXT_NAME_MAX_LEN)
+ {
+ return NULL;
+ }
+
+ if (strchr(fileExtName + 1, '/') != NULL) //invalid extension name
+ {
+ return NULL;
+ }
+
+ if (!twoExtName)
+ {
+ return fileExtName + 1;
+ }
+
+ pStart = fileExtName - (FDFS_FILE_EXT_NAME_MAX_LEN - extNameLen) - 1;
+ if (pStart < filename)
+ {
+ pStart = filename;
+ }
+
+ p = fileExtName - 1; //before .
+ while ((p > pStart) && (*p != '.'))
+ {
+ p--;
+ }
+
+ if (p > pStart) //found (extension name have a dot)
+ {
+ if (strchr(p + 1, '/') == NULL) //valid extension name
+ {
+ return p + 1; //skip .
+ }
+ }
+
+ return fileExtName + 1; //skip .
+}
+
diff --git a/client/client_func.h b/client/client_func.h
new file mode 100644
index 0000000..fe83ca7
--- /dev/null
+++ b/client/client_func.h
@@ -0,0 +1,145 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+//client_func.h
+
+#include "tracker_types.h"
+#include "client_global.h"
+#include "ini_file_reader.h"
+
+#ifndef _CLIENT_FUNC_H_
+#define _CLIENT_FUNC_H_
+
+typedef struct {
+ time_t create_timestamp;
+ int crc32;
+ int source_id; //source storage id
+ int64_t file_size;
+ char source_ip_addr[IP_ADDRESS_SIZE]; //source storage ip address
+} FDFSFileInfo;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define fdfs_client_init(filename) \
+ fdfs_client_init_ex((&g_tracker_group), filename)
+
+#define fdfs_client_init_from_buffer(buffer) \
+ fdfs_client_init_from_buffer_ex((&g_tracker_group), buffer)
+
+#define fdfs_client_destroy() \
+ fdfs_client_destroy_ex((&g_tracker_group))
+
+/**
+* client initial from config file
+* params:
+* pTrackerGroup: tracker group
+* conf_filename: client config filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int fdfs_client_init_ex(TrackerServerGroup *pTrackerGroup, \
+ const char *conf_filename);
+
+
+/**
+* client initial from buffer
+* params:
+* pTrackerGroup: tracker group
+* conf_filename: client config filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int fdfs_client_init_from_buffer_ex(TrackerServerGroup *pTrackerGroup, \
+ const char *buffer);
+
+/**
+* load tracker server group
+* params:
+* pTrackerGroup: tracker group
+* conf_filename: tracker server group config filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int fdfs_load_tracker_group(TrackerServerGroup *pTrackerGroup, \
+ const char *conf_filename);
+
+/**
+* load tracker server group
+* params:
+* pTrackerGroup: tracker group
+* conf_filename: config filename
+* items: ini file items
+* nItemCount: ini file item count
+* return: 0 success, !=0 fail, return the error code
+**/
+int fdfs_load_tracker_group_ex(TrackerServerGroup *pTrackerGroup, \
+ const char *conf_filename, IniContext *pIniContext);
+
+/**
+* copy tracker server group
+* params:
+* pDestTrackerGroup: the dest tracker group
+* pSrcTrackerGroup: the source tracker group
+* return: 0 success, !=0 fail, return the error code
+**/
+int fdfs_copy_tracker_group(TrackerServerGroup *pDestTrackerGroup, \
+ TrackerServerGroup *pSrcTrackerGroup);
+
+/**
+* client destroy function
+* params:
+* pTrackerGroup: tracker group
+* return: none
+**/
+void fdfs_client_destroy_ex(TrackerServerGroup *pTrackerGroup);
+
+/**
+* tracker group equals
+* params:
+* pGroup1: tracker group 1
+* pGroup2: tracker group 2
+* return: true for equals, otherwise false
+**/
+bool fdfs_tracker_group_equals(TrackerServerGroup *pGroup1, \
+ TrackerServerGroup *pGroup2);
+
+/**
+* get file ext name from filename, extension name do not include dot
+* params:
+* filename: the filename
+* return: file ext name, NULL for no ext name
+**/
+#define fdfs_get_file_ext_name1(filename) \
+ fdfs_get_file_ext_name_ex(filename, false)
+
+/**
+* get file ext name from filename, extension name maybe include dot
+* params:
+* filename: the filename
+* return: file ext name, NULL for no ext name
+**/
+#define fdfs_get_file_ext_name2(filename) \
+ fdfs_get_file_ext_name_ex(filename, true)
+
+#define fdfs_get_file_ext_name(filename) \
+ fdfs_get_file_ext_name_ex(filename, true)
+
+/**
+* get file ext name from filename
+* params:
+* filename: the filename
+* twoExtName: two extension name as the extension name
+* return: file ext name, NULL for no ext name
+**/
+const char *fdfs_get_file_ext_name_ex(const char *filename,
+ const bool twoExtName);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/client/client_global.c b/client/client_global.c
new file mode 100644
index 0000000..19964b4
--- /dev/null
+++ b/client/client_global.c
@@ -0,0 +1,18 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include "client_global.h"
+
+int g_tracker_server_http_port = 80;
+TrackerServerGroup g_tracker_group = {0, 0, -1, NULL};
+
+bool g_anti_steal_token = false;
+BufferInfo g_anti_steal_secret_key = {0};
+
diff --git a/client/client_global.h b/client/client_global.h
new file mode 100644
index 0000000..e0e1743
--- /dev/null
+++ b/client/client_global.h
@@ -0,0 +1,35 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+//client_global.h
+
+#ifndef _CLIENT_GLOBAL_H
+#define _CLIENT_GLOBAL_H
+
+#include "common_define.h"
+#include "tracker_types.h"
+#include "fdfs_shared_func.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int g_tracker_server_http_port;
+extern TrackerServerGroup g_tracker_group;
+
+extern bool g_anti_steal_token;
+extern BufferInfo g_anti_steal_secret_key;
+
+#define fdfs_get_tracker_leader_index(leaderIp, leaderPort) \
+ fdfs_get_tracker_leader_index_ex(&g_tracker_group, \
+ leaderIp, leaderPort)
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/client/fdfs_append_file.c b/client/fdfs_append_file.c
new file mode 100644
index 0000000..fd539fa
--- /dev/null
+++ b/client/fdfs_append_file.c
@@ -0,0 +1,66 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_client.h"
+#include "logger.h"
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ char *local_filename;
+ ConnectionInfo *pTrackerServer;
+ int result;
+ char appender_file_id[128];
+
+ if (argc < 4)
+ {
+ printf("Usage: %s " \
+ "\n", argv[0]);
+ return 1;
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_ERR;
+
+ conf_filename = argv[1];
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+
+ pTrackerServer = tracker_get_connection();
+ if (pTrackerServer == NULL)
+ {
+ fdfs_client_destroy();
+ return errno != 0 ? errno : ECONNREFUSED;
+ }
+
+ snprintf(appender_file_id, sizeof(appender_file_id), "%s", argv[2]);
+ local_filename = argv[3];
+ if ((result=storage_append_by_filename1(pTrackerServer, \
+ NULL, local_filename, appender_file_id)) != 0)
+ {
+ printf("append file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ return result;
+ }
+
+ tracker_disconnect_server_ex(pTrackerServer, true);
+ fdfs_client_destroy();
+
+ return result;
+}
+
diff --git a/client/fdfs_appender_test.c b/client/fdfs_appender_test.c
new file mode 100644
index 0000000..f9d2876
--- /dev/null
+++ b/client/fdfs_appender_test.c
@@ -0,0 +1,439 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_client.h"
+#include "fdfs_global.h"
+#include "base64.h"
+#include "sockopt.h"
+#include "logger.h"
+#include "fdfs_http_shared.h"
+
+int writeToFileCallback(void *arg, const int64_t file_size, const char *data, \
+ const int current_size)
+{
+ if (arg == NULL)
+ {
+ return EINVAL;
+ }
+
+ if (fwrite(data, current_size, 1, (FILE *)arg) != 1)
+ {
+ return errno != 0 ? errno : EIO;
+ }
+
+ return 0;
+}
+
+int uploadFileCallback(void *arg, const int64_t file_size, int sock)
+{
+ int64_t total_send_bytes;
+ char *filename;
+
+ if (arg == NULL)
+ {
+ return EINVAL;
+ }
+
+ filename = (char *)arg;
+ return tcpsendfile(sock, filename, file_size, \
+ g_fdfs_network_timeout, &total_send_bytes);
+}
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ char *local_filename;
+ ConnectionInfo *pTrackerServer;
+ ConnectionInfo *pStorageServer;
+ int result;
+ ConnectionInfo storageServer;
+ char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ char remote_filename[256];
+ char appender_filename[256];
+ FDFSMetaData meta_list[32];
+ int meta_count;
+ char token[32 + 1];
+ char file_id[128];
+ char file_url[256];
+ char szDatetime[20];
+ char szPortPart[16];
+ int url_len;
+ time_t ts;
+ int64_t file_offset;
+ int64_t file_size = 0;
+ int store_path_index;
+ FDFSFileInfo file_info;
+ int upload_type;
+ const char *file_ext_name;
+ struct stat stat_buf;
+
+ printf("This is FastDFS client test program v%d.%02d\n" \
+"\nCopyright (C) 2008, Happy Fish / YuQing\n" \
+"\nFastDFS may be copied only under the terms of the GNU General\n" \
+"Public License V3, which may be found in the FastDFS source kit.\n" \
+"Please visit the FastDFS Home Page http://www.csource.org/ \n" \
+"for more detail.\n\n" \
+, g_fdfs_version.major, g_fdfs_version.minor);
+
+ if (argc < 3)
+ {
+ printf("Usage: %s " \
+ "[FILE | BUFF | CALLBACK]\n", argv[0]);
+ return 1;
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_DEBUG;
+
+ conf_filename = argv[1];
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+
+ pTrackerServer = tracker_get_connection();
+ if (pTrackerServer == NULL)
+ {
+ fdfs_client_destroy();
+ return errno != 0 ? errno : ECONNREFUSED;
+ }
+
+ local_filename = argv[2];
+ if (argc == 3)
+ {
+ upload_type = FDFS_UPLOAD_BY_FILE;
+ }
+ else
+ {
+ if (strcmp(argv[3], "BUFF") == 0)
+ {
+ upload_type = FDFS_UPLOAD_BY_BUFF;
+ }
+ else if (strcmp(argv[3], "CALLBACK") == 0)
+ {
+ upload_type = FDFS_UPLOAD_BY_CALLBACK;
+ }
+ else
+ {
+ upload_type = FDFS_UPLOAD_BY_FILE;
+ }
+ }
+
+ *group_name = '\0';
+ store_path_index = 0;
+ if ((result=tracker_query_storage_store(pTrackerServer, \
+ &storageServer, group_name, &store_path_index)) != 0)
+ {
+ fdfs_client_destroy();
+ printf("tracker_query_storage fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ return result;
+ }
+
+ printf("group_name=%s, ip_addr=%s, port=%d\n", \
+ group_name, storageServer.ip_addr, \
+ storageServer.port);
+
+ if ((pStorageServer=tracker_connect_server(&storageServer, \
+ &result)) == NULL)
+ {
+ fdfs_client_destroy();
+ return result;
+ }
+
+ memset(&meta_list, 0, sizeof(meta_list));
+ meta_count = 0;
+ strcpy(meta_list[meta_count].name, "ext_name");
+ strcpy(meta_list[meta_count].value, "jpg");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "width");
+ strcpy(meta_list[meta_count].value, "160");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "height");
+ strcpy(meta_list[meta_count].value, "80");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "file_size");
+ strcpy(meta_list[meta_count].value, "115120");
+ meta_count++;
+
+ file_ext_name = fdfs_get_file_ext_name(local_filename);
+
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_upload_appender_by_filename ( \
+ pTrackerServer, pStorageServer, \
+ store_path_index, local_filename, \
+ file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename);
+ }
+ else
+ {
+ result = errno != 0 ? errno : ENOENT;
+ }
+
+ printf("storage_upload_appender_by_filename\n");
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ char *file_content;
+ if ((result=getFileContent(local_filename, \
+ &file_content, &file_size)) == 0)
+ {
+ result = storage_upload_appender_by_filebuff( \
+ pTrackerServer, pStorageServer, \
+ store_path_index, file_content, \
+ file_size, file_ext_name, \
+ meta_list, meta_count, \
+ group_name, remote_filename);
+ free(file_content);
+ }
+
+ printf("storage_upload_appender_by_filebuff\n");
+ }
+ else
+ {
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_upload_appender_by_callback( \
+ pTrackerServer, pStorageServer, \
+ store_path_index, uploadFileCallback, \
+ local_filename, file_size, \
+ file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename);
+ }
+ else
+ {
+ result = errno != 0 ? errno : ENOENT;
+ }
+
+ printf("storage_upload_appender_by_callback\n");
+ }
+
+ if (result != 0)
+ {
+ printf("upload file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+
+ if (g_tracker_server_http_port == 80)
+ {
+ *szPortPart = '\0';
+ }
+ else
+ {
+ sprintf(szPortPart, ":%d", g_tracker_server_http_port);
+ }
+
+ sprintf(file_id, "%s/%s", group_name, remote_filename);
+ url_len = sprintf(file_url, "http://%s%s/%s", \
+ pTrackerServer->ip_addr, szPortPart, file_id);
+ if (g_anti_steal_token)
+ {
+ ts = time(NULL);
+ fdfs_http_gen_token(&g_anti_steal_secret_key, file_id, \
+ ts, token);
+ sprintf(file_url + url_len, "?token=%s&ts=%d", token, (int)ts);
+ }
+
+ printf("group_name=%s, remote_filename=%s\n", \
+ group_name, remote_filename);
+
+ fdfs_get_file_info(group_name, remote_filename, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ printf("file crc32=%u\n", file_info.crc32);
+ printf("file url: %s\n", file_url);
+
+ //sleep(90);
+ strcpy(appender_filename, remote_filename);
+ if (storage_truncate_file(pTrackerServer, pStorageServer, \
+ group_name, appender_filename, file_size / 2) != 0)
+ {
+ printf("truncate file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+
+ fdfs_get_file_info(group_name, appender_filename, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ printf("file crc32=%u\n", file_info.crc32);
+ printf("file url: %s\n", file_url);
+ if (file_info.file_size != file_size / 2)
+ {
+ fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
+ " != "INT64_PRINTF_FORMAT"!!!", file_info.file_size, file_size / 2);
+ }
+
+ //sleep(100);
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ result = storage_append_by_filename(pTrackerServer, \
+ pStorageServer, local_filename,
+ group_name, appender_filename);
+
+ printf("storage_append_by_filename\n");
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ char *file_content;
+ if ((result=getFileContent(local_filename, \
+ &file_content, &file_size)) == 0)
+ {
+ result = storage_append_by_filebuff(pTrackerServer, \
+ pStorageServer, file_content, \
+ file_size, group_name, appender_filename);
+ free(file_content);
+ }
+
+ printf("storage_append_by_filebuff\n");
+ }
+ else
+ {
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_append_by_callback(pTrackerServer, \
+ pStorageServer, uploadFileCallback, \
+ local_filename, file_size, \
+ group_name, appender_filename);
+ }
+ else
+ {
+ result = errno != 0 ? errno : ENOENT;
+ }
+
+ printf("storage_append_by_callback\n");
+ }
+
+ if (result != 0)
+ {
+ printf("append file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+ printf("append file successfully.\n");
+ fdfs_get_file_info(group_name, remote_filename, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ if (file_info.file_size != file_size + file_size / 2)
+ {
+ fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
+ " != "INT64_PRINTF_FORMAT"!!!", file_info.file_size, \
+ file_size + file_size / 2);
+ }
+
+ file_offset = file_info.file_size;
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ result = storage_modify_by_filename(pTrackerServer, \
+ pStorageServer, local_filename, \
+ file_offset, group_name, \
+ appender_filename);
+
+ printf("storage_modify_by_filename\n");
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ char *file_content;
+ if ((result=getFileContent(local_filename, \
+ &file_content, &file_size)) == 0)
+ {
+ result = storage_modify_by_filebuff(pTrackerServer, \
+ pStorageServer, file_content, \
+ file_offset, file_size, group_name, \
+ appender_filename);
+ free(file_content);
+ }
+
+ printf("storage_modify_by_filebuff\n");
+ }
+ else
+ {
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_modify_by_callback(pTrackerServer, \
+ pStorageServer, uploadFileCallback, \
+ local_filename, file_offset, \
+ file_size, group_name, appender_filename);
+ }
+ else
+ {
+ result = errno != 0 ? errno : ENOENT;
+ }
+
+ printf("storage_modify_by_callback\n");
+ }
+
+ if (result != 0)
+ {
+ printf("modify file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+ printf("modify file successfully.\n");
+ fdfs_get_file_info(group_name, remote_filename, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ if (file_info.file_size != 2 * file_size + file_size / 2)
+ {
+ fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
+ " != "INT64_PRINTF_FORMAT"!!!", file_info.file_size, \
+ 2 * file_size + file_size /2);
+ }
+
+ tracker_disconnect_server_ex(pStorageServer, true);
+ tracker_disconnect_server_ex(pTrackerServer, true);
+
+ fdfs_client_destroy();
+
+ return result;
+}
+
diff --git a/client/fdfs_appender_test1.c b/client/fdfs_appender_test1.c
new file mode 100644
index 0000000..e535e38
--- /dev/null
+++ b/client/fdfs_appender_test1.c
@@ -0,0 +1,435 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_client.h"
+#include "fdfs_global.h"
+#include "base64.h"
+#include "sockopt.h"
+#include "logger.h"
+#include "fdfs_http_shared.h"
+
+int writeToFileCallback(void *arg, const int64_t file_size, const char *data, \
+ const int current_size)
+{
+ if (arg == NULL)
+ {
+ return EINVAL;
+ }
+
+ if (fwrite(data, current_size, 1, (FILE *)arg) != 1)
+ {
+ return errno != 0 ? errno : EIO;
+ }
+
+ return 0;
+}
+
+int uploadFileCallback(void *arg, const int64_t file_size, int sock)
+{
+ int64_t total_send_bytes;
+ char *filename;
+
+ if (arg == NULL)
+ {
+ return EINVAL;
+ }
+
+ filename = (char *)arg;
+ return tcpsendfile(sock, filename, file_size, \
+ g_fdfs_network_timeout, &total_send_bytes);
+}
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ char *local_filename;
+ ConnectionInfo *pTrackerServer;
+ ConnectionInfo *pStorageServer;
+ int result;
+ ConnectionInfo storageServer;
+ char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ char file_id[256];
+ char appender_file_id[256];
+ FDFSMetaData meta_list[32];
+ int meta_count;
+ char token[32 + 1];
+ char file_url[256];
+ char szDatetime[20];
+ char szPortPart[16];
+ int url_len;
+ time_t ts;
+ int64_t file_offset;
+ int64_t file_size = 0;
+ int store_path_index;
+ FDFSFileInfo file_info;
+ int upload_type;
+ const char *file_ext_name;
+ struct stat stat_buf;
+
+ printf("This is FastDFS client test program v%d.%02d\n" \
+"\nCopyright (C) 2008, Happy Fish / YuQing\n" \
+"\nFastDFS may be copied only under the terms of the GNU General\n" \
+"Public License V3, which may be found in the FastDFS source kit.\n" \
+"Please visit the FastDFS Home Page http://www.csource.org/ \n" \
+"for more detail.\n\n" \
+, g_fdfs_version.major, g_fdfs_version.minor);
+
+ if (argc < 3)
+ {
+ printf("Usage: %s " \
+ "[FILE | BUFF | CALLBACK]\n", argv[0]);
+ return 1;
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_DEBUG;
+
+ conf_filename = argv[1];
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+
+ pTrackerServer = tracker_get_connection();
+ if (pTrackerServer == NULL)
+ {
+ fdfs_client_destroy();
+ return errno != 0 ? errno : ECONNREFUSED;
+ }
+
+ local_filename = argv[2];
+ if (argc == 3)
+ {
+ upload_type = FDFS_UPLOAD_BY_FILE;
+ }
+ else
+ {
+ if (strcmp(argv[3], "BUFF") == 0)
+ {
+ upload_type = FDFS_UPLOAD_BY_BUFF;
+ }
+ else if (strcmp(argv[3], "CALLBACK") == 0)
+ {
+ upload_type = FDFS_UPLOAD_BY_CALLBACK;
+ }
+ else
+ {
+ upload_type = FDFS_UPLOAD_BY_FILE;
+ }
+ }
+
+ store_path_index = 0;
+ *group_name = '\0';
+ if ((result=tracker_query_storage_store(pTrackerServer, \
+ &storageServer, group_name, &store_path_index)) != 0)
+ {
+ fdfs_client_destroy();
+ printf("tracker_query_storage fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ return result;
+ }
+
+ printf("group_name=%s, ip_addr=%s, port=%d\n", \
+ group_name, storageServer.ip_addr, \
+ storageServer.port);
+
+ if ((pStorageServer=tracker_connect_server(&storageServer, \
+ &result)) == NULL)
+ {
+ fdfs_client_destroy();
+ return result;
+ }
+
+ memset(&meta_list, 0, sizeof(meta_list));
+ meta_count = 0;
+ strcpy(meta_list[meta_count].name, "ext_name");
+ strcpy(meta_list[meta_count].value, "jpg");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "width");
+ strcpy(meta_list[meta_count].value, "160");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "height");
+ strcpy(meta_list[meta_count].value, "80");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "file_size");
+ strcpy(meta_list[meta_count].value, "115120");
+ meta_count++;
+
+ file_ext_name = fdfs_get_file_ext_name(local_filename);
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_upload_appender_by_filename1( \
+ pTrackerServer, pStorageServer, \
+ store_path_index, local_filename, \
+ file_ext_name, meta_list, meta_count, \
+ group_name, file_id);
+ }
+ else
+ {
+ result = errno != 0 ? errno : ENOENT;
+ }
+
+ printf("storage_upload_appender_by_filename1\n");
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ char *file_content;
+ if ((result=getFileContent(local_filename, \
+ &file_content, &file_size)) == 0)
+ {
+ result = storage_upload_appender_by_filebuff1( \
+ pTrackerServer, pStorageServer, \
+ store_path_index, file_content, \
+ file_size, file_ext_name, \
+ meta_list, meta_count, \
+ group_name, file_id);
+ free(file_content);
+ }
+
+ printf("storage_upload_appender_by_filebuff1\n");
+ }
+ else
+ {
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_upload_appender_by_callback1( \
+ pTrackerServer, pStorageServer, \
+ store_path_index, uploadFileCallback, \
+ local_filename, file_size, \
+ file_ext_name, meta_list, meta_count, \
+ group_name, file_id);
+ }
+ else
+ {
+ result = errno != 0 ? errno : ENOENT;
+ }
+
+ printf("storage_upload_appender_by_callback1\n");
+ }
+
+ if (result != 0)
+ {
+ printf("upload file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+
+ if (g_tracker_server_http_port == 80)
+ {
+ *szPortPart = '\0';
+ }
+ else
+ {
+ sprintf(szPortPart, ":%d", g_tracker_server_http_port);
+ }
+
+ url_len = sprintf(file_url, "http://%s%s/%s", \
+ pTrackerServer->ip_addr, szPortPart, file_id);
+ if (g_anti_steal_token)
+ {
+ ts = time(NULL);
+ fdfs_http_gen_token(&g_anti_steal_secret_key, file_id, \
+ ts, token);
+ sprintf(file_url + url_len, "?token=%s&ts=%d", token, (int)ts);
+ }
+
+ printf("fild_id=%s\n", file_id);
+
+ fdfs_get_file_info1(file_id, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ printf("file crc32=%u\n", file_info.crc32);
+ printf("file url: %s\n", file_url);
+
+
+ strcpy(appender_file_id, file_id);
+ if (storage_truncate_file1(pTrackerServer, pStorageServer, \
+ appender_file_id, 0) != 0)
+ {
+ printf("truncate file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+
+ fdfs_get_file_info1(file_id, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ printf("file crc32=%u\n", file_info.crc32);
+ printf("file url: %s\n", file_url);
+ if (file_info.file_size != 0)
+ {
+ fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
+ " != 0!!!", file_info.file_size);
+ }
+
+ //sleep(70);
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ result = storage_append_by_filename1(pTrackerServer, \
+ pStorageServer, local_filename,
+ appender_file_id);
+
+ printf("storage_append_by_filename\n");
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ char *file_content;
+ if ((result=getFileContent(local_filename, \
+ &file_content, &file_size)) == 0)
+ {
+ result = storage_append_by_filebuff1(pTrackerServer, \
+ pStorageServer, file_content, \
+ file_size, appender_file_id);
+ free(file_content);
+ }
+
+ printf("storage_append_by_filebuff1\n");
+ }
+ else
+ {
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_append_by_callback1(pTrackerServer, \
+ pStorageServer, uploadFileCallback, \
+ local_filename, file_size, \
+ appender_file_id);
+ }
+ else
+ {
+ result = errno != 0 ? errno : ENOENT;
+ }
+
+ printf("storage_append_by_callback1\n");
+ }
+
+ if (result != 0)
+ {
+ printf("append file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+ printf("append file successfully.\n");
+ fdfs_get_file_info1(appender_file_id, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ if (file_info.file_size != file_size)
+ {
+ fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
+ " != "INT64_PRINTF_FORMAT"!!!", file_info.file_size, \
+ file_size);
+ }
+
+ file_offset = file_size;
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ result = storage_modify_by_filename1(pTrackerServer, \
+ pStorageServer, local_filename,
+ file_offset, appender_file_id);
+
+ printf("storage_modify_by_filename\n");
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ char *file_content;
+ if ((result=getFileContent(local_filename, \
+ &file_content, &file_size)) == 0)
+ {
+ result = storage_modify_by_filebuff1( \
+ pTrackerServer, pStorageServer, \
+ file_content, file_offset, file_size, \
+ appender_file_id);
+ free(file_content);
+ }
+
+ printf("storage_modify_by_filebuff1\n");
+ }
+ else
+ {
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_modify_by_callback1( \
+ pTrackerServer, pStorageServer, \
+ uploadFileCallback, \
+ local_filename, file_offset, \
+ file_size, appender_file_id);
+ }
+ else
+ {
+ result = errno != 0 ? errno : ENOENT;
+ }
+
+ printf("storage_modify_by_callback1\n");
+ }
+
+ if (result != 0)
+ {
+ printf("modify file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+ printf("modify file successfully.\n");
+ fdfs_get_file_info1(appender_file_id, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ if (file_info.file_size != 2 * file_size)
+ {
+ fprintf(stderr, "file size: "INT64_PRINTF_FORMAT \
+ " != "INT64_PRINTF_FORMAT"!!!", file_info.file_size, \
+ 2 * file_size);
+ }
+
+ tracker_disconnect_server_ex(pStorageServer, true);
+ tracker_disconnect_server_ex(pTrackerServer, true);
+
+ fdfs_client_destroy();
+
+ return result;
+}
+
diff --git a/client/fdfs_client.h b/client/fdfs_client.h
new file mode 100644
index 0000000..d63e792
--- /dev/null
+++ b/client/fdfs_client.h
@@ -0,0 +1,22 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#ifndef FDFS_CLIENT_H
+#define FDFS_CLIENT_H
+
+#include "shared_func.h"
+#include "tracker_types.h"
+#include "tracker_proto.h"
+#include "tracker_client.h"
+#include "storage_client.h"
+#include "storage_client1.h"
+#include "client_func.h"
+#include "client_global.h"
+
+#endif
+
diff --git a/client/fdfs_crc32.c b/client/fdfs_crc32.c
new file mode 100644
index 0000000..b460197
--- /dev/null
+++ b/client/fdfs_crc32.c
@@ -0,0 +1,105 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "hash.h"
+
+int main(int argc, char *argv[])
+{
+ int64_t file_size;
+ int64_t remain_bytes;
+ char *filename;
+ int fd;
+ int read_bytes;
+ int result;
+ int crc32;
+ char buff[512 * 1024];
+
+ if (argc < 2)
+ {
+ printf("Usage: %s \n", argv[0]);
+ return 1;
+ }
+
+ filename = argv[1];
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ {
+ printf("file: "__FILE__", line: %d, " \
+ "open file %s fail, " \
+ "errno: %d, error info: %s\n", \
+ __LINE__, filename, errno, STRERROR(errno));
+ return errno != 0 ? errno : EACCES;
+ }
+
+ if ((file_size=lseek(fd, 0, SEEK_END)) < 0)
+ {
+ printf("file: "__FILE__", line: %d, " \
+ "call lseek fail, " \
+ "errno: %d, error info: %s\n", \
+ __LINE__, errno, STRERROR(errno));
+ close(fd);
+ return errno;
+ }
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ {
+ printf("file: "__FILE__", line: %d, " \
+ "call lseek fail, " \
+ "errno: %d, error info: %s\n", \
+ __LINE__, errno, STRERROR(errno));
+ close(fd);
+ return errno;
+ }
+
+ crc32 = CRC32_XINIT;
+ result = 0;
+ remain_bytes = file_size;
+ while (remain_bytes > 0)
+ {
+ if (remain_bytes > sizeof(buff))
+ {
+ read_bytes = sizeof(buff);
+ }
+ else
+ {
+ read_bytes = remain_bytes;
+ }
+
+ if (read(fd, buff, read_bytes) != read_bytes)
+ {
+ printf("file: "__FILE__", line: %d, " \
+ "call lseek fail, " \
+ "errno: %d, error info: %s\n", \
+ __LINE__, errno, STRERROR(errno));
+ result = errno != 0 ? errno : EIO;
+ break;
+ }
+
+ crc32 = CRC32_ex(buff, read_bytes, crc32);
+ remain_bytes -= read_bytes;
+ }
+
+ close(fd);
+
+ if (result == 0)
+ {
+ crc32 = CRC32_FINAL(crc32);
+ printf("%u\n", crc32);
+ }
+
+ return result;
+}
+
diff --git a/client/fdfs_delete_file.c b/client/fdfs_delete_file.c
new file mode 100644
index 0000000..da3ce1a
--- /dev/null
+++ b/client/fdfs_delete_file.c
@@ -0,0 +1,62 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_client.h"
+#include "logger.h"
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ ConnectionInfo *pTrackerServer;
+ int result;
+ char file_id[128];
+
+ if (argc < 3)
+ {
+ printf("Usage: %s \n", argv[0]);
+ return 1;
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_ERR;
+ ignore_signal_pipe();
+
+ conf_filename = argv[1];
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+
+ pTrackerServer = tracker_get_connection();
+ if (pTrackerServer == NULL)
+ {
+ fdfs_client_destroy();
+ return errno != 0 ? errno : ECONNREFUSED;
+ }
+
+ snprintf(file_id, sizeof(file_id), "%s", argv[2]);
+ if ((result=storage_delete_file1(pTrackerServer, NULL, file_id)) != 0)
+ {
+ printf("delete file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ }
+
+ tracker_disconnect_server_ex(pTrackerServer, true);
+ fdfs_client_destroy();
+
+ return result;
+}
+
diff --git a/client/fdfs_download_file.c b/client/fdfs_download_file.c
new file mode 100644
index 0000000..23c2775
--- /dev/null
+++ b/client/fdfs_download_file.c
@@ -0,0 +1,97 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_client.h"
+#include "logger.h"
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ char *local_filename;
+ ConnectionInfo *pTrackerServer;
+ int result;
+ char file_id[128];
+ int64_t file_size;
+ int64_t file_offset;
+ int64_t download_bytes;
+
+ if (argc < 3)
+ {
+ printf("Usage: %s " \
+ "[local_filename] [ " \
+ "]\n", argv[0]);
+ return 1;
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_ERR;
+ ignore_signal_pipe();
+
+ conf_filename = argv[1];
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+
+ pTrackerServer = tracker_get_connection();
+ if (pTrackerServer == NULL)
+ {
+ fdfs_client_destroy();
+ return errno != 0 ? errno : ECONNREFUSED;
+ }
+
+ snprintf(file_id, sizeof(file_id), "%s", argv[2]);
+
+ file_offset = 0;
+ download_bytes = 0;
+ if (argc >= 4)
+ {
+ local_filename = argv[3];
+ if (argc >= 6)
+ {
+ file_offset = strtoll(argv[4], NULL, 10);
+ download_bytes = strtoll(argv[5], NULL, 10);
+ }
+ }
+ else
+ {
+ local_filename = strrchr(file_id, '/');
+ if (local_filename != NULL)
+ {
+ local_filename++; //skip /
+ }
+ else
+ {
+ local_filename = file_id;
+ }
+ }
+
+ result = storage_do_download_file1_ex(pTrackerServer, \
+ NULL, FDFS_DOWNLOAD_TO_FILE, file_id, \
+ file_offset, download_bytes, \
+ &local_filename, NULL, &file_size);
+ if (result != 0)
+ {
+ printf("download file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ }
+
+ tracker_disconnect_server_ex(pTrackerServer, true);
+ fdfs_client_destroy();
+
+ return 0;
+}
+
diff --git a/client/fdfs_file_info.c b/client/fdfs_file_info.c
new file mode 100644
index 0000000..6f3a2d4
--- /dev/null
+++ b/client/fdfs_file_info.c
@@ -0,0 +1,71 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_client.h"
+#include "logger.h"
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ char file_id[128];
+ int result;
+ FDFSFileInfo file_info;
+
+ if (argc < 3)
+ {
+ printf("Usage: %s \n", argv[0]);
+ return 1;
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_ERR;
+ ignore_signal_pipe();
+
+ conf_filename = argv[1];
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+
+ snprintf(file_id, sizeof(file_id), "%s", argv[2]);
+ memset(&file_info, 0, sizeof(file_info));
+ result = fdfs_get_file_info_ex1(file_id, true, &file_info);
+ if (result != 0)
+ {
+ printf("query file info fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ }
+ else
+ {
+ char szDatetime[32];
+
+ printf("source storage id: %d\n", file_info.source_id);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file create timestamp: %s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size: "INT64_PRINTF_FORMAT"\n", \
+ file_info.file_size);
+ printf("file crc32: %u (0x%08X)\n", \
+ file_info.crc32, file_info.crc32);
+ }
+
+ tracker_close_all_connections();
+ fdfs_client_destroy();
+
+ return 0;
+}
+
diff --git a/client/fdfs_link_library.sh b/client/fdfs_link_library.sh
new file mode 100755
index 0000000..bab18e3
--- /dev/null
+++ b/client/fdfs_link_library.sh
@@ -0,0 +1,28 @@
+tmp_src_filename=_fdfs_check_bits_.c
+cat < $tmp_src_filename
+#include
+#include
+#include
+int main()
+{
+ printf("%d\n", (int)sizeof(long));
+ return 0;
+}
+EOF
+
+gcc -D_FILE_OFFSET_BITS=64 -o a.out $tmp_src_filename
+OS_BITS=`./a.out`
+
+rm $tmp_src_filename a.out
+
+TARGET_LIB="/usr/local/lib"
+if [ "`id -u`" = "0" ]; then
+ ln -fs $TARGET_LIB/libfastcommon.so.1 /usr/lib/libfastcommon.so
+ ln -fs $TARGET_LIB/libfdfsclient.so.1 /usr/lib/libfdfsclient.so
+
+ if [ "$OS_BITS" = "8" ]; then
+ ln -fs $TARGET_LIB/libfastcommon.so.1 /usr/lib64/libfastcommon.so
+ ln -fs $TARGET_LIB/libfdfsclient.so.1 /usr/lib64/libfdfsclient.so
+ fi
+fi
+
diff --git a/client/fdfs_link_library.sh.in b/client/fdfs_link_library.sh.in
new file mode 100755
index 0000000..b04f762
--- /dev/null
+++ b/client/fdfs_link_library.sh.in
@@ -0,0 +1,28 @@
+tmp_src_filename=_fdfs_check_bits_.c
+cat < $tmp_src_filename
+#include
+#include
+#include
+int main()
+{
+ printf("%d\n", (int)sizeof(long));
+ return 0;
+}
+EOF
+
+gcc -D_FILE_OFFSET_BITS=64 -o a.out $tmp_src_filename
+OS_BITS=`./a.out`
+
+rm $tmp_src_filename a.out
+
+TARGET_LIB="$(TARGET_PREFIX)/lib"
+if [ "`id -u`" = "0" ]; then
+ ln -fs $TARGET_LIB/libfastcommon.so.1 /usr/lib/libfastcommon.so
+ ln -fs $TARGET_LIB/libfdfsclient.so.1 /usr/lib/libfdfsclient.so
+
+ if [ "$OS_BITS" = "8" ]; then
+ ln -fs $TARGET_LIB/libfastcommon.so.1 /usr/lib64/libfastcommon.so
+ ln -fs $TARGET_LIB/libfdfsclient.so.1 /usr/lib64/libfdfsclient.so
+ fi
+fi
+
diff --git a/client/fdfs_monitor.c b/client/fdfs_monitor.c
new file mode 100644
index 0000000..708d362
--- /dev/null
+++ b/client/fdfs_monitor.c
@@ -0,0 +1,571 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "sockopt.h"
+#include "logger.h"
+#include "client_global.h"
+#include "fdfs_global.h"
+#include "fdfs_client.h"
+
+static ConnectionInfo *pTrackerServer;
+
+static int list_all_groups(const char *group_name);
+
+static void usage(char *argv[])
+{
+ printf("Usage: %s [-h ] [list|delete|set_trunk_server " \
+ "[storage_id]]\n", argv[0]);
+}
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ int result;
+ char *op_type;
+ char *tracker_server;
+ int arg_index;
+ char *group_name;
+
+ if (argc < 2)
+ {
+ usage(argv);
+ return 1;
+ }
+
+ tracker_server = NULL;
+ conf_filename = argv[1];
+ arg_index = 2;
+
+ if (arg_index >= argc)
+ {
+ op_type = "list";
+ }
+ else
+ {
+ int len;
+
+ len = strlen(argv[arg_index]);
+ if (len >= 2 && strncmp(argv[arg_index], "-h", 2) == 0)
+ {
+ if (len == 2)
+ {
+ arg_index++;
+ if (arg_index >= argc)
+ {
+ usage(argv);
+ return 1;
+ }
+
+ tracker_server = argv[arg_index++];
+ }
+ else
+ {
+ tracker_server = argv[arg_index] + 2;
+ arg_index++;
+ }
+
+ if (arg_index < argc)
+ {
+ op_type = argv[arg_index++];
+ }
+ else
+ {
+ op_type = "list";
+ }
+ }
+ else
+ {
+ op_type = argv[arg_index++];
+ }
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_DEBUG;
+ ignore_signal_pipe();
+
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+ load_log_level_ex(conf_filename);
+
+ if (tracker_server == NULL)
+ {
+ if (g_tracker_group.server_count > 1)
+ {
+ srand(time(NULL));
+ rand(); //discard the first
+ g_tracker_group.server_index = (int)( \
+ (g_tracker_group.server_count * (double)rand()) \
+ / (double)RAND_MAX);
+ }
+ }
+ else
+ {
+ int i;
+ char ip_addr[IP_ADDRESS_SIZE];
+
+ *ip_addr = '\0';
+ if (getIpaddrByName(tracker_server, ip_addr, sizeof(ip_addr)) \
+ == INADDR_NONE)
+ {
+ printf("resolve ip address of tracker server: %s " \
+ "fail!\n", tracker_server);
+ return 2;
+ }
+
+ for (i=0; iip_addr, pTrackerServer->port);
+
+ if (arg_index < argc)
+ {
+ group_name = argv[arg_index++];
+ }
+ else
+ {
+ group_name = NULL;
+ }
+
+ if (strcmp(op_type, "list") == 0)
+ {
+ if (group_name == NULL)
+ {
+ result = list_all_groups(NULL);
+ }
+ else
+ {
+ result = list_all_groups(group_name);
+ }
+ }
+ else if (strcmp(op_type, "delete") == 0)
+ {
+ char *storage_id;
+ if (arg_index >= argc)
+ {
+ usage(argv);
+ return 1;
+ }
+
+ storage_id = argv[arg_index++];
+
+ if ((result=tracker_delete_storage(&g_tracker_group, \
+ group_name, storage_id)) == 0)
+ {
+ printf("delete storage server %s::%s success\n", \
+ group_name, storage_id);
+ }
+ else
+ {
+ printf("delete storage server %s::%s fail, " \
+ "error no: %d, error info: %s\n", \
+ group_name, storage_id, \
+ result, STRERROR(result));
+ }
+ }
+ else if (strcmp(op_type, "set_trunk_server") == 0)
+ {
+ char *storage_id;
+ char new_trunk_server_id[FDFS_STORAGE_ID_MAX_SIZE];
+
+ if (group_name == NULL)
+ {
+ usage(argv);
+ return 1;
+ }
+ if (arg_index >= argc)
+ {
+ storage_id = "";
+ }
+ else
+ {
+ storage_id = argv[arg_index++];
+ }
+
+ if ((result=tracker_set_trunk_server(&g_tracker_group, \
+ group_name, storage_id, new_trunk_server_id)) == 0)
+ {
+ printf("set trunk server %s::%s success, " \
+ "new trunk server: %s\n", group_name, \
+ storage_id, new_trunk_server_id);
+ }
+ else
+ {
+ printf("set trunk server %s::%s fail, " \
+ "error no: %d, error info: %s\n", \
+ group_name, storage_id, \
+ result, STRERROR(result));
+ }
+ }
+ else
+ {
+ printf("Invalid command %s\n\n", op_type);
+ usage(argv);
+ }
+
+ tracker_disconnect_server_ex(pTrackerServer, true);
+ fdfs_client_destroy();
+ return 0;
+}
+
+static int list_storages(FDFSGroupStat *pGroupStat)
+{
+ int result;
+ int storage_count;
+ FDFSStorageInfo storage_infos[FDFS_MAX_SERVERS_EACH_GROUP];
+ FDFSStorageInfo *p;
+ FDFSStorageInfo *pStorage;
+ FDFSStorageInfo *pStorageEnd;
+ FDFSStorageStat *pStorageStat;
+ char szJoinTime[32];
+ char szUpTime[32];
+ char szLastHeartBeatTime[32];
+ char szSrcUpdTime[32];
+ char szSyncUpdTime[32];
+ char szSyncedTimestamp[32];
+ char szSyncedDelaySeconds[128];
+ char szHostname[128];
+ char szHostnamePrompt[128+8];
+ int k;
+ int max_last_source_update;
+
+ printf( "group name = %s\n" \
+ "disk total space = "INT64_PRINTF_FORMAT" MB\n" \
+ "disk free space = "INT64_PRINTF_FORMAT" MB\n" \
+ "trunk free space = "INT64_PRINTF_FORMAT" MB\n" \
+ "storage server count = %d\n" \
+ "active server count = %d\n" \
+ "storage server port = %d\n" \
+ "storage HTTP port = %d\n" \
+ "store path count = %d\n" \
+ "subdir count per path = %d\n" \
+ "current write server index = %d\n" \
+ "current trunk file id = %d\n\n", \
+ pGroupStat->group_name, \
+ pGroupStat->total_mb, \
+ pGroupStat->free_mb, \
+ pGroupStat->trunk_free_mb, \
+ pGroupStat->count, \
+ pGroupStat->active_count, \
+ pGroupStat->storage_port, \
+ pGroupStat->storage_http_port, \
+ pGroupStat->store_path_count, \
+ pGroupStat->subdir_count_per_path, \
+ pGroupStat->current_write_server, \
+ pGroupStat->current_trunk_file_id
+ );
+
+ result = tracker_list_servers(pTrackerServer, \
+ pGroupStat->group_name, NULL, \
+ storage_infos, FDFS_MAX_SERVERS_EACH_GROUP, \
+ &storage_count);
+ if (result != 0)
+ {
+ return result;
+ }
+
+ k = 0;
+ pStorageEnd = storage_infos + storage_count;
+ for (pStorage=storage_infos; pStoragestat.last_source_update
+ > max_last_source_update)
+ {
+ max_last_source_update = \
+ p->stat.last_source_update;
+ }
+ }
+
+ pStorageStat = &(pStorage->stat);
+ if (max_last_source_update == 0)
+ {
+ *szSyncedDelaySeconds = '\0';
+ }
+ else
+ {
+ if (pStorageStat->last_synced_timestamp == 0)
+ {
+ strcpy(szSyncedDelaySeconds, "(never synced)");
+ }
+ else
+ {
+ int delay_seconds;
+ int remain_seconds;
+ int day;
+ int hour;
+ int minute;
+ int second;
+ char szDelayTime[64];
+
+ delay_seconds = (int)(max_last_source_update - \
+ pStorageStat->last_synced_timestamp);
+ day = delay_seconds / (24 * 3600);
+ remain_seconds = delay_seconds % (24 * 3600);
+ hour = remain_seconds / 3600;
+ remain_seconds %= 3600;
+ minute = remain_seconds / 60;
+ second = remain_seconds % 60;
+
+ if (day != 0)
+ {
+ sprintf(szDelayTime, "%d days " \
+ "%02dh:%02dm:%02ds", \
+ day, hour, minute, second);
+ }
+ else if (hour != 0)
+ {
+ sprintf(szDelayTime, "%02dh:%02dm:%02ds", \
+ hour, minute, second);
+ }
+ else if (minute != 0)
+ {
+ sprintf(szDelayTime, "%02dm:%02ds", minute, second);
+ }
+ else
+ {
+ sprintf(szDelayTime, "%ds", second);
+ }
+
+ sprintf(szSyncedDelaySeconds, "(%s delay)", szDelayTime);
+ }
+ }
+
+ getHostnameByIp(pStorage->ip_addr, szHostname, sizeof(szHostname));
+ if (*szHostname != '\0')
+ {
+ sprintf(szHostnamePrompt, " (%s)", szHostname);
+ }
+ else
+ {
+ *szHostnamePrompt = '\0';
+ }
+
+ if (pStorage->up_time != 0)
+ {
+ formatDatetime(pStorage->up_time, \
+ "%Y-%m-%d %H:%M:%S", \
+ szUpTime, sizeof(szUpTime));
+ }
+ else
+ {
+ *szUpTime = '\0';
+ }
+
+ printf( "\tStorage %d:\n" \
+ "\t\tid = %s\n" \
+ "\t\tip_addr = %s%s %s\n" \
+ "\t\thttp domain = %s\n" \
+ "\t\tversion = %s\n" \
+ "\t\tjoin time = %s\n" \
+ "\t\tup time = %s\n" \
+ "\t\ttotal storage = %d MB\n" \
+ "\t\tfree storage = %d MB\n" \
+ "\t\tupload priority = %d\n" \
+ "\t\tstore_path_count = %d\n" \
+ "\t\tsubdir_count_per_path = %d\n" \
+ "\t\tstorage_port = %d\n" \
+ "\t\tstorage_http_port = %d\n" \
+ "\t\tcurrent_write_path = %d\n" \
+ "\t\tsource storage id= %s\n" \
+ "\t\tif_trunk_server= %d\n" \
+ "\t\ttotal_upload_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_upload_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_append_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_append_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_modify_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_modify_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_truncate_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_truncate_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_set_meta_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_set_meta_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_delete_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_delete_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_download_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_download_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_get_meta_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_get_meta_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_create_link_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_create_link_count = "INT64_PRINTF_FORMAT"\n"\
+ "\t\ttotal_delete_link_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_delete_link_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_upload_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_upload_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_append_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_append_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_modify_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_modify_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tstotal_download_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_download_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_sync_in_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_sync_in_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_sync_out_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_sync_out_bytes = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_file_open_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_file_open_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_file_read_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_file_read_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\ttotal_file_write_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tsuccess_file_write_count = "INT64_PRINTF_FORMAT"\n" \
+ "\t\tlast_heart_beat_time = %s\n" \
+ "\t\tlast_source_update = %s\n" \
+ "\t\tlast_sync_update = %s\n" \
+ "\t\tlast_synced_timestamp = %s %s\n", \
+ ++k, pStorage->id, pStorage->ip_addr, \
+ szHostnamePrompt, get_storage_status_caption( \
+ pStorage->status), pStorage->domain_name, \
+ pStorage->version, \
+ formatDatetime(pStorage->join_time, \
+ "%Y-%m-%d %H:%M:%S", \
+ szJoinTime, sizeof(szJoinTime)), \
+ szUpTime, pStorage->total_mb, \
+ pStorage->free_mb, \
+ pStorage->upload_priority, \
+ pStorage->store_path_count, \
+ pStorage->subdir_count_per_path, \
+ pStorage->storage_port, \
+ pStorage->storage_http_port, \
+ pStorage->current_write_path, \
+ pStorage->src_id, \
+ pStorage->if_trunk_server, \
+ pStorageStat->total_upload_count, \
+ pStorageStat->success_upload_count, \
+ pStorageStat->total_append_count, \
+ pStorageStat->success_append_count, \
+ pStorageStat->total_modify_count, \
+ pStorageStat->success_modify_count, \
+ pStorageStat->total_truncate_count, \
+ pStorageStat->success_truncate_count, \
+ pStorageStat->total_set_meta_count, \
+ pStorageStat->success_set_meta_count, \
+ pStorageStat->total_delete_count, \
+ pStorageStat->success_delete_count, \
+ pStorageStat->total_download_count, \
+ pStorageStat->success_download_count, \
+ pStorageStat->total_get_meta_count, \
+ pStorageStat->success_get_meta_count, \
+ pStorageStat->total_create_link_count, \
+ pStorageStat->success_create_link_count, \
+ pStorageStat->total_delete_link_count, \
+ pStorageStat->success_delete_link_count, \
+ pStorageStat->total_upload_bytes, \
+ pStorageStat->success_upload_bytes, \
+ pStorageStat->total_append_bytes, \
+ pStorageStat->success_append_bytes, \
+ pStorageStat->total_modify_bytes, \
+ pStorageStat->success_modify_bytes, \
+ pStorageStat->total_download_bytes, \
+ pStorageStat->success_download_bytes, \
+ pStorageStat->total_sync_in_bytes, \
+ pStorageStat->success_sync_in_bytes, \
+ pStorageStat->total_sync_out_bytes, \
+ pStorageStat->success_sync_out_bytes, \
+ pStorageStat->total_file_open_count, \
+ pStorageStat->success_file_open_count, \
+ pStorageStat->total_file_read_count, \
+ pStorageStat->success_file_read_count, \
+ pStorageStat->total_file_write_count, \
+ pStorageStat->success_file_write_count, \
+ formatDatetime(pStorageStat->last_heart_beat_time, \
+ "%Y-%m-%d %H:%M:%S", \
+ szLastHeartBeatTime, sizeof(szLastHeartBeatTime)), \
+ formatDatetime(pStorageStat->last_source_update, \
+ "%Y-%m-%d %H:%M:%S", \
+ szSrcUpdTime, sizeof(szSrcUpdTime)), \
+ formatDatetime(pStorageStat->last_sync_update, \
+ "%Y-%m-%d %H:%M:%S", \
+ szSyncUpdTime, sizeof(szSyncUpdTime)), \
+ formatDatetime(pStorageStat->last_synced_timestamp, \
+ "%Y-%m-%d %H:%M:%S", \
+ szSyncedTimestamp, sizeof(szSyncedTimestamp)),\
+ szSyncedDelaySeconds);
+ }
+
+ return 0;
+}
+
+static int list_all_groups(const char *group_name)
+{
+ int result;
+ int group_count;
+ FDFSGroupStat group_stats[FDFS_MAX_GROUPS];
+ FDFSGroupStat *pGroupStat;
+ FDFSGroupStat *pGroupEnd;
+ int i;
+
+ result = tracker_list_groups(pTrackerServer, \
+ group_stats, FDFS_MAX_GROUPS, \
+ &group_count);
+ if (result != 0)
+ {
+ tracker_close_all_connections();
+ fdfs_client_destroy();
+ return result;
+ }
+
+ pGroupEnd = group_stats + group_count;
+ if (group_name == NULL)
+ {
+ printf("group count: %d\n", group_count);
+ i = 0;
+ for (pGroupStat=group_stats; pGroupStatgroup_name, group_name) == 0)
+ {
+ list_storages(pGroupStat);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
diff --git a/client/fdfs_test.c b/client/fdfs_test.c
new file mode 100644
index 0000000..64d467d
--- /dev/null
+++ b/client/fdfs_test.c
@@ -0,0 +1,691 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_client.h"
+#include "fdfs_global.h"
+#include "base64.h"
+#include "sockopt.h"
+#include "logger.h"
+#include "fdfs_http_shared.h"
+
+int writeToFileCallback(void *arg, const int64_t file_size, const char *data, \
+ const int current_size)
+{
+ if (arg == NULL)
+ {
+ return EINVAL;
+ }
+
+ if (fwrite(data, current_size, 1, (FILE *)arg) != 1)
+ {
+ return errno != 0 ? errno : EIO;
+ }
+
+ return 0;
+}
+
+int uploadFileCallback(void *arg, const int64_t file_size, int sock)
+{
+ int64_t total_send_bytes;
+ char *filename;
+
+ if (arg == NULL)
+ {
+ return EINVAL;
+ }
+
+ filename = (char *)arg;
+ return tcpsendfile(sock, filename, file_size, \
+ g_fdfs_network_timeout, &total_send_bytes);
+}
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ char *local_filename;
+ ConnectionInfo *pTrackerServer;
+ ConnectionInfo *pStorageServer;
+ int result;
+ ConnectionInfo storageServer;
+ char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ char remote_filename[256];
+ char master_filename[256];
+ FDFSMetaData meta_list[32];
+ int meta_count;
+ int i;
+ FDFSMetaData *pMetaList;
+ char token[32 + 1];
+ char file_id[128];
+ char file_url[256];
+ char szDatetime[20];
+ char szPortPart[16];
+ int url_len;
+ time_t ts;
+ char *file_buff;
+ int64_t file_size;
+ char *operation;
+ char *meta_buff;
+ int store_path_index;
+ FDFSFileInfo file_info;
+
+ printf("This is FastDFS client test program v%d.%02d\n" \
+"\nCopyright (C) 2008, Happy Fish / YuQing\n" \
+"\nFastDFS may be copied only under the terms of the GNU General\n" \
+"Public License V3, which may be found in the FastDFS source kit.\n" \
+"Please visit the FastDFS Home Page http://www.csource.org/ \n" \
+"for more detail.\n\n" \
+, g_fdfs_version.major, g_fdfs_version.minor);
+
+ if (argc < 3)
+ {
+ printf("Usage: %s \n" \
+ "\toperation: upload, download, getmeta, setmeta, " \
+ "delete and query_servers\n", argv[0]);
+ return 1;
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_DEBUG;
+
+ conf_filename = argv[1];
+ operation = argv[2];
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+
+ pTrackerServer = tracker_get_connection();
+ if (pTrackerServer == NULL)
+ {
+ fdfs_client_destroy();
+ return errno != 0 ? errno : ECONNREFUSED;
+ }
+
+ pStorageServer = NULL;
+ *group_name = '\0';
+ local_filename = NULL;
+ if (strcmp(operation, "upload") == 0)
+ {
+ int upload_type;
+ char *prefix_name;
+ const char *file_ext_name;
+ char slave_filename[256];
+ int slave_filename_len;
+
+ if (argc < 4)
+ {
+ printf("Usage: %s upload " \
+ " [FILE | BUFF | CALLBACK] \n",\
+ argv[0]);
+ fdfs_client_destroy();
+ return EINVAL;
+ }
+
+ local_filename = argv[3];
+ if (argc == 4)
+ {
+ upload_type = FDFS_UPLOAD_BY_FILE;
+ }
+ else
+ {
+ if (strcmp(argv[4], "BUFF") == 0)
+ {
+ upload_type = FDFS_UPLOAD_BY_BUFF;
+ }
+ else if (strcmp(argv[4], "CALLBACK") == 0)
+ {
+ upload_type = FDFS_UPLOAD_BY_CALLBACK;
+ }
+ else
+ {
+ upload_type = FDFS_UPLOAD_BY_FILE;
+ }
+ }
+
+ store_path_index = 0;
+
+ {
+ ConnectionInfo storageServers[FDFS_MAX_SERVERS_EACH_GROUP];
+ ConnectionInfo *pServer;
+ ConnectionInfo *pServerEnd;
+ int storage_count;
+
+ if ((result=tracker_query_storage_store_list_without_group( \
+ pTrackerServer, storageServers, \
+ FDFS_MAX_SERVERS_EACH_GROUP, &storage_count, \
+ group_name, &store_path_index)) == 0)
+ {
+ printf("tracker_query_storage_store_list_without_group: \n");
+ pServerEnd = storageServers + storage_count;
+ for (pServer=storageServers; pServerip_addr, pServer->port);
+ }
+ printf("\n");
+ }
+ }
+
+ if ((result=tracker_query_storage_store(pTrackerServer, \
+ &storageServer, group_name, &store_path_index)) != 0)
+ {
+ fdfs_client_destroy();
+ printf("tracker_query_storage fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ return result;
+ }
+
+ printf("group_name=%s, ip_addr=%s, port=%d\n", \
+ group_name, storageServer.ip_addr, \
+ storageServer.port);
+
+ if ((pStorageServer=tracker_connect_server(&storageServer, \
+ &result)) == NULL)
+ {
+ fdfs_client_destroy();
+ return result;
+ }
+
+ memset(&meta_list, 0, sizeof(meta_list));
+ meta_count = 0;
+ strcpy(meta_list[meta_count].name, "ext_name");
+ strcpy(meta_list[meta_count].value, "jpg");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "width");
+ strcpy(meta_list[meta_count].value, "160");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "height");
+ strcpy(meta_list[meta_count].value, "80");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "file_size");
+ strcpy(meta_list[meta_count].value, "115120");
+ meta_count++;
+
+ file_ext_name = fdfs_get_file_ext_name(local_filename);
+ *group_name = '\0';
+
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ result = storage_upload_by_filename(pTrackerServer, \
+ pStorageServer, store_path_index, \
+ local_filename, file_ext_name, \
+ meta_list, meta_count, \
+ group_name, remote_filename);
+
+ printf("storage_upload_by_filename\n");
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ char *file_content;
+ if ((result=getFileContent(local_filename, \
+ &file_content, &file_size)) == 0)
+ {
+ result = storage_upload_by_filebuff(pTrackerServer, \
+ pStorageServer, store_path_index, \
+ file_content, file_size, file_ext_name, \
+ meta_list, meta_count, \
+ group_name, remote_filename);
+ free(file_content);
+ }
+
+ printf("storage_upload_by_filebuff\n");
+ }
+ else
+ {
+ struct stat stat_buf;
+
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_upload_by_callback(pTrackerServer, \
+ pStorageServer, store_path_index, \
+ uploadFileCallback, local_filename, \
+ file_size, file_ext_name, \
+ meta_list, meta_count, \
+ group_name, remote_filename);
+ }
+
+ printf("storage_upload_by_callback\n");
+ }
+
+ if (result != 0)
+ {
+ printf("upload file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+
+ if (g_tracker_server_http_port == 80)
+ {
+ *szPortPart = '\0';
+ }
+ else
+ {
+ sprintf(szPortPart, ":%d", g_tracker_server_http_port);
+ }
+
+ sprintf(file_id, "%s/%s", group_name, remote_filename);
+ url_len = sprintf(file_url, "http://%s%s/%s", \
+ pStorageServer->ip_addr, szPortPart, file_id);
+ if (g_anti_steal_token)
+ {
+ ts = time(NULL);
+ fdfs_http_gen_token(&g_anti_steal_secret_key, file_id, \
+ ts, token);
+ sprintf(file_url + url_len, "?token=%s&ts=%d", \
+ token, (int)ts);
+ }
+
+ printf("group_name=%s, remote_filename=%s\n", \
+ group_name, remote_filename);
+
+ fdfs_get_file_info(group_name, remote_filename, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ printf("file crc32=%u\n", file_info.crc32);
+ printf("example file url: %s\n", file_url);
+
+ strcpy(master_filename, remote_filename);
+ *remote_filename = '\0';
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ prefix_name = "_big";
+ result = storage_upload_slave_by_filename(pTrackerServer,
+ NULL, local_filename, master_filename, \
+ prefix_name, file_ext_name, \
+ meta_list, meta_count, \
+ group_name, remote_filename);
+
+ printf("storage_upload_slave_by_filename\n");
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ char *file_content;
+ prefix_name = "1024x1024";
+ if ((result=getFileContent(local_filename, \
+ &file_content, &file_size)) == 0)
+ {
+ result = storage_upload_slave_by_filebuff(pTrackerServer, \
+ NULL, file_content, file_size, master_filename,
+ prefix_name, file_ext_name, \
+ meta_list, meta_count, \
+ group_name, remote_filename);
+ free(file_content);
+ }
+
+ printf("storage_upload_slave_by_filebuff\n");
+ }
+ else
+ {
+ struct stat stat_buf;
+
+ prefix_name = "-small";
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_upload_slave_by_callback(pTrackerServer, \
+ NULL, uploadFileCallback, local_filename, \
+ file_size, master_filename, prefix_name, \
+ file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename);
+ }
+
+ printf("storage_upload_slave_by_callback\n");
+ }
+
+ if (result != 0)
+ {
+ printf("upload slave file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+
+ if (g_tracker_server_http_port == 80)
+ {
+ *szPortPart = '\0';
+ }
+ else
+ {
+ sprintf(szPortPart, ":%d", g_tracker_server_http_port);
+ }
+
+ sprintf(file_id, "%s/%s", group_name, remote_filename);
+ url_len = sprintf(file_url, "http://%s%s/%s", \
+ pStorageServer->ip_addr, szPortPart, file_id);
+ if (g_anti_steal_token)
+ {
+ ts = time(NULL);
+ fdfs_http_gen_token(&g_anti_steal_secret_key, file_id, \
+ ts, token);
+ sprintf(file_url + url_len, "?token=%s&ts=%d", \
+ token, (int)ts);
+ }
+
+ printf("group_name=%s, remote_filename=%s\n", \
+ group_name, remote_filename);
+
+ fdfs_get_file_info(group_name, remote_filename, &file_info);
+
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ printf("file crc32=%u\n", file_info.crc32);
+ printf("example file url: %s\n", file_url);
+
+ if (fdfs_gen_slave_filename(master_filename, \
+ prefix_name, file_ext_name, \
+ slave_filename, &slave_filename_len) == 0)
+ {
+
+ if (strcmp(remote_filename, slave_filename) != 0)
+ {
+ printf("slave_filename=%s\n" \
+ "remote_filename=%s\n" \
+ "not equal!\n", \
+ slave_filename, remote_filename);
+ }
+ }
+ }
+ else if (strcmp(operation, "download") == 0 ||
+ strcmp(operation, "getmeta") == 0 ||
+ strcmp(operation, "setmeta") == 0 ||
+ strcmp(operation, "query_servers") == 0 ||
+ strcmp(operation, "delete") == 0)
+ {
+ if (argc < 5)
+ {
+ printf("Usage: %s %s " \
+ " \n", \
+ argv[0], operation);
+ fdfs_client_destroy();
+ return EINVAL;
+ }
+
+ snprintf(group_name, sizeof(group_name), "%s", argv[3]);
+ snprintf(remote_filename, sizeof(remote_filename), \
+ "%s", argv[4]);
+ if (strcmp(operation, "setmeta") == 0 ||
+ strcmp(operation, "delete") == 0)
+ {
+ result = tracker_query_storage_update(pTrackerServer, \
+ &storageServer, group_name, remote_filename);
+ }
+ else if (strcmp(operation, "query_servers") == 0)
+ {
+ ConnectionInfo storageServers[FDFS_MAX_SERVERS_EACH_GROUP];
+ int server_count;
+
+ result = tracker_query_storage_list(pTrackerServer, \
+ storageServers, FDFS_MAX_SERVERS_EACH_GROUP, \
+ &server_count, group_name, remote_filename);
+
+ if (result != 0)
+ {
+ printf("tracker_query_storage_list fail, "\
+ "group_name=%s, filename=%s, " \
+ "error no: %d, error info: %s\n", \
+ group_name, remote_filename, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ printf("server list (%d):\n", server_count);
+ for (i=0; i= 6)
+ {
+ local_filename = argv[5];
+ if (strcmp(local_filename, "CALLBACK") == 0)
+ {
+ FILE *fp;
+ fp = fopen(local_filename, "wb");
+ if (fp == NULL)
+ {
+ result = errno != 0 ? errno : EPERM;
+ printf("open file \"%s\" fail, " \
+ "errno: %d, error info: %s", \
+ local_filename, result, \
+ STRERROR(result));
+ }
+ else
+ {
+ result = storage_download_file_ex( \
+ pTrackerServer, pStorageServer, \
+ group_name, remote_filename, 0, 0, \
+ writeToFileCallback, fp, &file_size);
+ fclose(fp);
+ }
+ }
+ else
+ {
+ result = storage_download_file_to_file( \
+ pTrackerServer, pStorageServer, \
+ group_name, remote_filename, \
+ local_filename, &file_size);
+ }
+ }
+ else
+ {
+ file_buff = NULL;
+ if ((result=storage_download_file_to_buff( \
+ pTrackerServer, pStorageServer, \
+ group_name, remote_filename, \
+ &file_buff, &file_size)) == 0)
+ {
+ local_filename = strrchr( \
+ remote_filename, '/');
+ if (local_filename != NULL)
+ {
+ local_filename++; //skip /
+ }
+ else
+ {
+ local_filename=remote_filename;
+ }
+
+ result = writeToFile(local_filename, \
+ file_buff, file_size);
+
+ free(file_buff);
+ }
+ }
+
+ if (result == 0)
+ {
+ printf("download file success, " \
+ "file size="INT64_PRINTF_FORMAT", file save to %s\n", \
+ file_size, local_filename);
+ }
+ else
+ {
+ printf("download file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ }
+ }
+ else if (strcmp(operation, "getmeta") == 0)
+ {
+ if ((result=storage_get_metadata(pTrackerServer, \
+ pStorageServer, group_name, remote_filename, \
+ &pMetaList, &meta_count)) == 0)
+ {
+ printf("get meta data success, " \
+ "meta count=%d\n", meta_count);
+ for (i=0; i %s " \
+ " " \
+ " \n" \
+ "\top_flag: %c for overwrite, " \
+ "%c for merge\n" \
+ "\tmetadata_list: name1=value1," \
+ "name2=value2,...\n", \
+ argv[0], operation, \
+ STORAGE_SET_METADATA_FLAG_OVERWRITE, \
+ STORAGE_SET_METADATA_FLAG_MERGE);
+ fdfs_client_destroy();
+ return EINVAL;
+ }
+
+ meta_buff = strdup(argv[6]);
+ if (meta_buff == NULL)
+ {
+ printf("Out of memory!\n");
+ return ENOMEM;
+ }
+
+ pMetaList = fdfs_split_metadata_ex(meta_buff, \
+ ',', '=', &meta_count, &result);
+ if (pMetaList == NULL)
+ {
+ printf("Out of memory!\n");
+ free(meta_buff);
+ return ENOMEM;
+ }
+
+ if ((result=storage_set_metadata(pTrackerServer, \
+ NULL, group_name, remote_filename, \
+ pMetaList, meta_count, *argv[5])) == 0)
+ {
+ printf("set meta data success\n");
+ }
+ else
+ {
+ printf("setmeta fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ }
+
+ free(meta_buff);
+ free(pMetaList);
+ }
+ else if(strcmp(operation, "delete") == 0)
+ {
+ if ((result=storage_delete_file(pTrackerServer, \
+ NULL, group_name, remote_filename)) == 0)
+ {
+ printf("delete file success\n");
+ }
+ else
+ {
+ printf("delete file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ }
+ }
+ }
+ else
+ {
+ fdfs_client_destroy();
+ printf("invalid operation: %s\n", operation);
+ return EINVAL;
+ }
+
+ /* for test only */
+ if ((result=fdfs_active_test(pTrackerServer)) != 0)
+ {
+ printf("active_test to tracker server %s:%d fail, errno: %d\n", \
+ pTrackerServer->ip_addr, pTrackerServer->port, result);
+ }
+
+ /* for test only */
+ if ((result=fdfs_active_test(pStorageServer)) != 0)
+ {
+ printf("active_test to storage server %s:%d fail, errno: %d\n", \
+ pStorageServer->ip_addr, pStorageServer->port, result);
+ }
+
+ tracker_disconnect_server_ex(pStorageServer, true);
+ tracker_disconnect_server_ex(pTrackerServer, true);
+
+ fdfs_client_destroy();
+
+ return result;
+}
+
diff --git a/client/fdfs_test1.c b/client/fdfs_test1.c
new file mode 100644
index 0000000..b9d0720
--- /dev/null
+++ b/client/fdfs_test1.c
@@ -0,0 +1,658 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_client.h"
+#include "fdfs_global.h"
+#include "base64.h"
+#include "fdfs_http_shared.h"
+#include "sockopt.h"
+#include "logger.h"
+
+int writeToFileCallback(void *arg, const int64_t file_size, const char *data, \
+ const int current_size)
+{
+ if (arg == NULL)
+ {
+ return EINVAL;
+ }
+
+ if (fwrite(data, current_size, 1, (FILE *)arg) != 1)
+ {
+ return errno != 0 ? errno : EIO;
+ }
+
+ return 0;
+}
+
+int uploadFileCallback(void *arg, const int64_t file_size, int sock)
+{
+ int64_t total_send_bytes;
+ char *filename;
+ if (arg == NULL)
+ {
+ return EINVAL;
+ }
+
+ filename = (char *)arg;
+ return tcpsendfile(sock, filename, file_size, \
+ g_fdfs_network_timeout, &total_send_bytes);
+}
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ char *local_filename;
+ ConnectionInfo *pTrackerServer;
+ ConnectionInfo *pStorageServer;
+ int result;
+ ConnectionInfo storageServer;
+ char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ FDFSMetaData meta_list[32];
+ int meta_count;
+ int i;
+ FDFSMetaData *pMetaList;
+ char token[32 + 1];
+ char file_id[128];
+ char master_file_id[128];
+ char file_url[256];
+ char szDatetime[20];
+ char szPortPart[16];
+ int url_len;
+ time_t ts;
+ char *file_buff;
+ int64_t file_size;
+ char *operation;
+ char *meta_buff;
+ int store_path_index;
+ FDFSFileInfo file_info;
+
+ printf("This is FastDFS client test program v%d.%02d\n" \
+"\nCopyright (C) 2008, Happy Fish / YuQing\n" \
+"\nFastDFS may be copied only under the terms of the GNU General\n" \
+"Public License V3, which may be found in the FastDFS source kit.\n" \
+"Please visit the FastDFS Home Page http://www.csource.org/ \n" \
+"for more detail.\n\n" \
+, g_fdfs_version.major, g_fdfs_version.minor);
+
+ if (argc < 3)
+ {
+ printf("Usage: %s \n" \
+ "\toperation: upload, download, getmeta, setmeta, " \
+ "delete and query_servers\n", argv[0]);
+ return 1;
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_DEBUG;
+
+ conf_filename = argv[1];
+ operation = argv[2];
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+
+ pTrackerServer = tracker_get_connection();
+ if (pTrackerServer == NULL)
+ {
+ fdfs_client_destroy();
+ return errno != 0 ? errno : ECONNREFUSED;
+ }
+
+ local_filename = NULL;
+ if (strcmp(operation, "upload") == 0)
+ {
+ int upload_type;
+ char *prefix_name;
+ const char *file_ext_name;
+ char slave_file_id[256];
+ int slave_file_id_len;
+
+ if (argc < 4)
+ {
+ printf("Usage: %s upload " \
+ " [FILE | BUFF | CALLBACK] \n",\
+ argv[0]);
+ fdfs_client_destroy();
+ return EINVAL;
+ }
+
+ local_filename = argv[3];
+ if (argc == 4)
+ {
+ upload_type = FDFS_UPLOAD_BY_FILE;
+ }
+ else
+ {
+ if (strcmp(argv[4], "BUFF") == 0)
+ {
+ upload_type = FDFS_UPLOAD_BY_BUFF;
+ }
+ else if (strcmp(argv[4], "CALLBACK") == 0)
+ {
+ upload_type = FDFS_UPLOAD_BY_CALLBACK;
+ }
+ else
+ {
+ upload_type = FDFS_UPLOAD_BY_FILE;
+ }
+ }
+
+ {
+ ConnectionInfo storageServers[FDFS_MAX_SERVERS_EACH_GROUP];
+ ConnectionInfo *pServer;
+ ConnectionInfo *pServerEnd;
+ int storage_count;
+
+ strcpy(group_name, "group1");
+ if ((result=tracker_query_storage_store_list_with_group( \
+ pTrackerServer, group_name, storageServers, \
+ FDFS_MAX_SERVERS_EACH_GROUP, &storage_count, \
+ &store_path_index)) == 0)
+ {
+ printf("tracker_query_storage_store_list_with_group: \n");
+ pServerEnd = storageServers + storage_count;
+ for (pServer=storageServers; pServerip_addr, \
+ pServer->port);
+ }
+ printf("\n");
+ }
+ }
+
+ *group_name = '\0';
+ if ((result=tracker_query_storage_store(pTrackerServer, \
+ &storageServer, group_name, &store_path_index)) != 0)
+ {
+ fdfs_client_destroy();
+ printf("tracker_query_storage fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ return result;
+ }
+
+ printf("group_name=%s, ip_addr=%s, port=%d\n", \
+ group_name, storageServer.ip_addr, \
+ storageServer.port);
+
+ if ((pStorageServer=tracker_connect_server(&storageServer, \
+ &result)) == NULL)
+ {
+ fdfs_client_destroy();
+ return result;
+ }
+
+ memset(&meta_list, 0, sizeof(meta_list));
+ meta_count = 0;
+ strcpy(meta_list[meta_count].name, "ext_name");
+ strcpy(meta_list[meta_count].value, "jpg");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "width");
+ strcpy(meta_list[meta_count].value, "160");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "height");
+ strcpy(meta_list[meta_count].value, "80");
+ meta_count++;
+ strcpy(meta_list[meta_count].name, "file_size");
+ strcpy(meta_list[meta_count].value, "115120");
+ meta_count++;
+
+ file_ext_name = fdfs_get_file_ext_name(local_filename);
+ strcpy(group_name, "");
+
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ printf("storage_upload_by_filename\n");
+ result = storage_upload_by_filename1(pTrackerServer, \
+ pStorageServer, store_path_index, \
+ local_filename, file_ext_name, \
+ meta_list, meta_count, \
+ group_name, file_id);
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ char *file_content;
+ printf("storage_upload_by_filebuff\n");
+ if ((result=getFileContent(local_filename, \
+ &file_content, &file_size)) == 0)
+ {
+ result = storage_upload_by_filebuff1(pTrackerServer, \
+ pStorageServer, store_path_index, \
+ file_content, file_size, file_ext_name, \
+ meta_list, meta_count, \
+ group_name, file_id);
+ free(file_content);
+ }
+ }
+ else
+ {
+ struct stat stat_buf;
+
+ printf("storage_upload_by_callback\n");
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_upload_by_callback1(pTrackerServer, \
+ pStorageServer, store_path_index, \
+ uploadFileCallback, local_filename, \
+ file_size, file_ext_name, \
+ meta_list, meta_count, \
+ group_name, file_id);
+ }
+ }
+
+ if (result != 0)
+ {
+ printf("upload file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+
+ if (g_tracker_server_http_port == 80)
+ {
+ *szPortPart = '\0';
+ }
+ else
+ {
+ sprintf(szPortPart, ":%d", g_tracker_server_http_port);
+ }
+
+ url_len = sprintf(file_url, "http://%s%s/%s", \
+ pStorageServer->ip_addr, szPortPart, file_id);
+ if (g_anti_steal_token)
+ {
+ ts = time(NULL);
+ fdfs_http_gen_token(&g_anti_steal_secret_key, \
+ file_id, ts, token);
+ sprintf(file_url + url_len, "?token=%s&ts=%d", \
+ token, (int)ts);
+ }
+
+ fdfs_get_file_info1(file_id, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ printf("file crc32=%u\n", file_info.crc32);
+ printf("example file url: %s\n", file_url);
+
+ strcpy(master_file_id, file_id);
+ *file_id = '\0';
+
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ prefix_name = "_big";
+ printf("storage_upload_slave_by_filename\n");
+ result = storage_upload_slave_by_filename1( \
+ pTrackerServer, NULL, \
+ local_filename, master_file_id, \
+ prefix_name, file_ext_name, \
+ meta_list, meta_count, file_id);
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ char *file_content;
+ prefix_name = "1024x1024";
+ printf("storage_upload_slave_by_filebuff\n");
+ if ((result=getFileContent(local_filename, \
+ &file_content, &file_size)) == 0)
+ {
+ result = storage_upload_slave_by_filebuff1( \
+ pTrackerServer, NULL, file_content, file_size, \
+ master_file_id, prefix_name, file_ext_name, \
+ meta_list, meta_count, file_id);
+ free(file_content);
+ }
+ }
+ else
+ {
+ struct stat stat_buf;
+
+ prefix_name = "_small";
+ printf("storage_upload_slave_by_callback\n");
+ if (stat(local_filename, &stat_buf) == 0 && \
+ S_ISREG(stat_buf.st_mode))
+ {
+ file_size = stat_buf.st_size;
+ result = storage_upload_slave_by_callback1( \
+ pTrackerServer, NULL, \
+ uploadFileCallback, local_filename, \
+ file_size, master_file_id, \
+ prefix_name, file_ext_name, \
+ meta_list, meta_count, file_id);
+ }
+ }
+
+ if (result != 0)
+ {
+ printf("upload slave file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ tracker_disconnect_server_ex(pStorageServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+
+ if (g_tracker_server_http_port == 80)
+ {
+ *szPortPart = '\0';
+ }
+ else
+ {
+ sprintf(szPortPart, ":%d", g_tracker_server_http_port);
+ }
+ url_len = sprintf(file_url, "http://%s%s/%s", \
+ pStorageServer->ip_addr, szPortPart, file_id);
+ if (g_anti_steal_token)
+ {
+ ts = time(NULL);
+ fdfs_http_gen_token(&g_anti_steal_secret_key, \
+ file_id, ts, token);
+ sprintf(file_url + url_len, "?token=%s&ts=%d", \
+ token, (int)ts);
+ }
+
+ fdfs_get_file_info1(file_id, &file_info);
+ printf("source ip address: %s\n", file_info.source_ip_addr);
+ printf("file timestamp=%s\n", formatDatetime(
+ file_info.create_timestamp, "%Y-%m-%d %H:%M:%S", \
+ szDatetime, sizeof(szDatetime)));
+ printf("file size="INT64_PRINTF_FORMAT"\n", file_info.file_size);
+ printf("file crc32=%u\n", file_info.crc32);
+ printf("example file url: %s\n", file_url);
+
+ if (fdfs_gen_slave_filename(master_file_id, \
+ prefix_name, file_ext_name, \
+ slave_file_id, &slave_file_id_len) == 0)
+ {
+ if (strcmp(file_id, slave_file_id) != 0)
+ {
+ printf("slave_file_id=%s\n" \
+ "file_id=%s\n" \
+ "not equal!\n", \
+ slave_file_id, file_id);
+ }
+ }
+ }
+ else if (strcmp(operation, "download") == 0 ||
+ strcmp(operation, "getmeta") == 0 ||
+ strcmp(operation, "setmeta") == 0 ||
+ strcmp(operation, "query_servers") == 0 ||
+ strcmp(operation, "delete") == 0)
+ {
+ if (argc < 4)
+ {
+ printf("Usage: %s %s " \
+ "\n", \
+ argv[0], operation);
+ fdfs_client_destroy();
+ return EINVAL;
+ }
+
+ snprintf(file_id, sizeof(file_id), "%s", argv[3]);
+ if (strcmp(operation, "query_servers") == 0)
+ {
+ ConnectionInfo storageServers[FDFS_MAX_SERVERS_EACH_GROUP];
+ int server_count;
+
+ result = tracker_query_storage_list1(pTrackerServer, \
+ storageServers, FDFS_MAX_SERVERS_EACH_GROUP, \
+ &server_count, file_id);
+
+ if (result != 0)
+ {
+ printf("tracker_query_storage_list1 fail, "\
+ "file_id=%s, " \
+ "error no: %d, error info: %s\n", \
+ file_id, result, STRERROR(result));
+ }
+ else
+ {
+ printf("server list (%d):\n", server_count);
+ for (i=0; i= 5)
+ {
+ local_filename = argv[4];
+ if (strcmp(local_filename, "CALLBACK") == 0)
+ {
+ FILE *fp;
+ fp = fopen(local_filename, "wb");
+ if (fp == NULL)
+ {
+ result = errno != 0 ? errno : EPERM;
+ printf("open file \"%s\" fail, " \
+ "errno: %d, error info: %s", \
+ local_filename, result, \
+ STRERROR(result));
+ }
+ else
+ {
+ result = storage_download_file_ex1( \
+ pTrackerServer, pStorageServer, \
+ file_id, 0, 0, \
+ writeToFileCallback, fp, &file_size);
+ fclose(fp);
+ }
+ }
+ else
+ {
+ result = storage_download_file_to_file1( \
+ pTrackerServer, pStorageServer, \
+ file_id, \
+ local_filename, &file_size);
+ }
+ }
+ else
+ {
+ file_buff = NULL;
+ if ((result=storage_download_file_to_buff1( \
+ pTrackerServer, pStorageServer, \
+ file_id, \
+ &file_buff, &file_size)) == 0)
+ {
+ local_filename = strrchr( \
+ file_id, '/');
+ if (local_filename != NULL)
+ {
+ local_filename++; //skip /
+ }
+ else
+ {
+ local_filename=file_id;
+ }
+
+ result = writeToFile(local_filename, \
+ file_buff, file_size);
+
+ free(file_buff);
+ }
+ }
+
+ if (result == 0)
+ {
+ printf("download file success, " \
+ "file size="INT64_PRINTF_FORMAT", file save to %s\n", \
+ file_size, local_filename);
+ }
+ else
+ {
+ printf("download file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ }
+ }
+ else if (strcmp(operation, "getmeta") == 0)
+ {
+ if ((result=storage_get_metadata1(pTrackerServer, \
+ NULL, file_id, \
+ &pMetaList, &meta_count)) == 0)
+ {
+ printf("get meta data success, " \
+ "meta count=%d\n", meta_count);
+ for (i=0; i %s " \
+ " " \
+ " \n" \
+ "\top_flag: %c for overwrite, " \
+ "%c for merge\n" \
+ "\tmetadata_list: name1=value1," \
+ "name2=value2,...\n", \
+ argv[0], operation, \
+ STORAGE_SET_METADATA_FLAG_OVERWRITE, \
+ STORAGE_SET_METADATA_FLAG_MERGE);
+ fdfs_client_destroy();
+ return EINVAL;
+ }
+
+ meta_buff = strdup(argv[5]);
+ if (meta_buff == NULL)
+ {
+ printf("Out of memory!\n");
+ return ENOMEM;
+ }
+
+ pMetaList = fdfs_split_metadata_ex(meta_buff, \
+ ',', '=', &meta_count, &result);
+ if (pMetaList == NULL)
+ {
+ printf("Out of memory!\n");
+ free(meta_buff);
+ return ENOMEM;
+ }
+
+ if ((result=storage_set_metadata1(pTrackerServer, \
+ NULL, file_id, \
+ pMetaList, meta_count, *argv[4])) == 0)
+ {
+ printf("set meta data success\n");
+ }
+ else
+ {
+ printf("setmeta fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ }
+
+ free(meta_buff);
+ free(pMetaList);
+ }
+ else if(strcmp(operation, "delete") == 0)
+ {
+ if ((result=storage_delete_file1(pTrackerServer, \
+ NULL, file_id)) == 0)
+ {
+ printf("delete file success\n");
+ }
+ else
+ {
+ printf("delete file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ }
+ }
+ }
+ else
+ {
+ fdfs_client_destroy();
+ printf("invalid operation: %s\n", operation);
+ return EINVAL;
+ }
+
+ /* for test only */
+ if ((result=fdfs_active_test(pTrackerServer)) != 0)
+ {
+ printf("active_test to tracker server %s:%d fail, errno: %d\n", \
+ pTrackerServer->ip_addr, pTrackerServer->port, result);
+ }
+
+ /* for test only */
+ if ((result=fdfs_active_test(pStorageServer)) != 0)
+ {
+ printf("active_test to storage server %s:%d fail, errno: %d\n", \
+ pStorageServer->ip_addr, pStorageServer->port, result);
+ }
+
+ tracker_disconnect_server_ex(pStorageServer, true);
+ tracker_disconnect_server_ex(pTrackerServer, true);
+
+ fdfs_client_destroy();
+
+ return result;
+}
+
diff --git a/client/fdfs_upload_appender.c b/client/fdfs_upload_appender.c
new file mode 100644
index 0000000..ebd02b7
--- /dev/null
+++ b/client/fdfs_upload_appender.c
@@ -0,0 +1,88 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_client.h"
+#include "logger.h"
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ char *local_filename;
+ char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ ConnectionInfo *pTrackerServer;
+ int result;
+ int store_path_index;
+ ConnectionInfo storageServer;
+ char file_id[128];
+
+ if (argc < 3)
+ {
+ printf("Usage: %s \n", argv[0]);
+ return 1;
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_ERR;
+ ignore_signal_pipe();
+
+ conf_filename = argv[1];
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+
+ pTrackerServer = tracker_get_connection();
+ if (pTrackerServer == NULL)
+ {
+ fdfs_client_destroy();
+ return errno != 0 ? errno : ECONNREFUSED;
+ }
+
+
+ *group_name = '\0';
+ store_path_index = 0;
+ if ((result=tracker_query_storage_store(pTrackerServer, \
+ &storageServer, group_name, &store_path_index)) != 0)
+ {
+ fdfs_client_destroy();
+ fprintf(stderr, "tracker_query_storage fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ return result;
+ }
+
+ local_filename = argv[2];
+ result = storage_upload_appender_by_filename1(pTrackerServer, \
+ &storageServer, store_path_index, \
+ local_filename, NULL, \
+ NULL, 0, group_name, file_id);
+ if (result != 0)
+ {
+ fprintf(stderr, "upload file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+
+ tracker_disconnect_server_ex(pTrackerServer, true);
+ fdfs_client_destroy();
+ return result;
+ }
+
+ printf("%s\n", file_id);
+
+ tracker_disconnect_server_ex(pTrackerServer, true);
+ fdfs_client_destroy();
+
+ return 0;
+}
+
diff --git a/client/fdfs_upload_file.c b/client/fdfs_upload_file.c
new file mode 100644
index 0000000..4ac119f
--- /dev/null
+++ b/client/fdfs_upload_file.c
@@ -0,0 +1,120 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_client.h"
+#include "logger.h"
+
+static void usage(char *argv[])
+{
+ printf("Usage: %s " \
+ "[storage_ip:port] [store_path_index]\n", argv[0]);
+}
+
+int main(int argc, char *argv[])
+{
+ char *conf_filename;
+ char *local_filename;
+ char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ ConnectionInfo *pTrackerServer;
+ int result;
+ int store_path_index;
+ ConnectionInfo storageServer;
+ char file_id[128];
+
+ if (argc < 3)
+ {
+ usage(argv);
+ return 1;
+ }
+
+ log_init();
+ g_log_context.log_level = LOG_ERR;
+ ignore_signal_pipe();
+
+ conf_filename = argv[1];
+ if ((result=fdfs_client_init(conf_filename)) != 0)
+ {
+ return result;
+ }
+
+ pTrackerServer = tracker_get_connection();
+ if (pTrackerServer == NULL)
+ {
+ fdfs_client_destroy();
+ return errno != 0 ? errno : ECONNREFUSED;
+ }
+
+ local_filename = argv[2];
+ *group_name = '\0';
+ if (argc >= 4)
+ {
+ const char *pPort;
+ const char *pIpAndPort;
+
+ pIpAndPort = argv[3];
+ pPort = strchr(pIpAndPort, ':');
+ if (pPort == NULL)
+ {
+ fdfs_client_destroy();
+ fprintf(stderr, "invalid storage ip address and " \
+ "port: %s\n", pIpAndPort);
+ usage(argv);
+ return 1;
+ }
+
+ storageServer.sock = -1;
+ snprintf(storageServer.ip_addr, sizeof(storageServer.ip_addr), \
+ "%.*s", (int)(pPort - pIpAndPort), pIpAndPort);
+ storageServer.port = atoi(pPort + 1);
+ if (argc >= 5)
+ {
+ store_path_index = atoi(argv[4]);
+ }
+ else
+ {
+ store_path_index = -1;
+ }
+ }
+ else if ((result=tracker_query_storage_store(pTrackerServer, \
+ &storageServer, group_name, &store_path_index)) != 0)
+ {
+ fdfs_client_destroy();
+ fprintf(stderr, "tracker_query_storage fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ return result;
+ }
+
+ result = storage_upload_by_filename1(pTrackerServer, \
+ &storageServer, store_path_index, \
+ local_filename, NULL, \
+ NULL, 0, group_name, file_id);
+ if (result == 0)
+ {
+ printf("%s\n", file_id);
+ }
+ else
+ {
+ fprintf(stderr, "upload file fail, " \
+ "error no: %d, error info: %s\n", \
+ result, STRERROR(result));
+ }
+
+ tracker_disconnect_server_ex(pTrackerServer, true);
+ fdfs_client_destroy();
+
+ return result;
+}
+
diff --git a/client/storage_client.c b/client/storage_client.c
new file mode 100644
index 0000000..aabd37a
--- /dev/null
+++ b/client/storage_client.c
@@ -0,0 +1,2315 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_define.h"
+#include "logger.h"
+#include "fdfs_global.h"
+#include "sockopt.h"
+#include "shared_func.h"
+#include "tracker_types.h"
+#include "tracker_proto.h"
+#include "client_func.h"
+#include "tracker_client.h"
+#include "storage_client.h"
+#include "storage_client1.h"
+#include "client_global.h"
+#include "base64.h"
+
+static struct base64_context the_base64_context;
+static int the_base64_context_inited = 0;
+
+#define FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id) \
+ char new_file_id[FDFS_GROUP_NAME_MAX_LEN + 128]; \
+ char *group_name; \
+ char *filename; \
+ char *pSeperator; \
+ \
+ snprintf(new_file_id, sizeof(new_file_id), "%s", file_id); \
+ pSeperator = strchr(new_file_id, FDFS_FILE_ID_SEPERATOR); \
+ if (pSeperator == NULL) \
+ { \
+ return EINVAL; \
+ } \
+ \
+ *pSeperator = '\0'; \
+ group_name = new_file_id; \
+ filename = pSeperator + 1; \
+
+#define storage_get_read_connection(pTrackerServer, \
+ ppStorageServer, group_name, filename, \
+ pNewStorage, new_connection) \
+ storage_get_connection(pTrackerServer, \
+ ppStorageServer, TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE, \
+ group_name, filename, pNewStorage, new_connection)
+
+#define storage_get_update_connection(pTrackerServer, \
+ ppStorageServer, group_name, filename, \
+ pNewStorage, new_connection) \
+ storage_get_connection(pTrackerServer, \
+ ppStorageServer, TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE, \
+ group_name, filename, pNewStorage, new_connection)
+
+static int storage_get_connection(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo **ppStorageServer, const byte cmd, \
+ const char *group_name, const char *filename, \
+ ConnectionInfo *pNewStorage, bool *new_connection)
+{
+ int result;
+ bool new_tracker_connection;
+ ConnectionInfo *pNewTracker;
+ if (*ppStorageServer == NULL)
+ {
+ CHECK_CONNECTION(pTrackerServer, pNewTracker, result, \
+ new_tracker_connection);
+ if (cmd == TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE)
+ {
+ result = tracker_query_storage_fetch(pNewTracker, \
+ pNewStorage, group_name, filename);
+ }
+ else
+ {
+ result = tracker_query_storage_update(pNewTracker, \
+ pNewStorage, group_name, filename);
+ }
+
+ if (new_tracker_connection)
+ {
+ tracker_disconnect_server_ex(pNewTracker, result != 0);
+ }
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ if ((*ppStorageServer=tracker_connect_server(pNewStorage, \
+ &result)) == NULL)
+ {
+ return result;
+ }
+
+ *new_connection = true;
+ }
+ else
+ {
+ if ((*ppStorageServer)->sock >= 0)
+ {
+ *new_connection = false;
+ }
+ else
+ {
+ if ((*ppStorageServer=tracker_connect_server( \
+ *ppStorageServer, &result)) == NULL)
+ {
+ return result;
+ }
+
+ *new_connection = true;
+ }
+ }
+
+ return 0;
+}
+
+static int storage_get_upload_connection(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo **ppStorageServer, char *group_name, \
+ ConnectionInfo *pNewStorage, int *store_path_index, \
+ bool *new_connection)
+{
+ int result;
+ bool new_tracker_connection;
+ ConnectionInfo *pNewTracker;
+
+ if (*ppStorageServer == NULL)
+ {
+ CHECK_CONNECTION(pTrackerServer, pNewTracker, result, \
+ new_tracker_connection);
+ if (*group_name == '\0')
+ {
+ result = tracker_query_storage_store_without_group( \
+ pNewTracker, pNewStorage, group_name, \
+ store_path_index);
+ }
+ else
+ {
+ result = tracker_query_storage_store_with_group( \
+ pNewTracker, group_name, pNewStorage, \
+ store_path_index);
+ }
+
+ if (new_tracker_connection)
+ {
+ tracker_disconnect_server_ex(pNewTracker, result != 0);
+ }
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ if ((*ppStorageServer=tracker_connect_server(pNewStorage, \
+ &result)) == NULL)
+ {
+ return result;
+ }
+
+ *new_connection = true;
+ }
+ else
+ {
+ if ((*ppStorageServer)->sock >= 0)
+ {
+ *new_connection = false;
+ }
+ else
+ {
+ if ((*ppStorageServer=tracker_connect_server( \
+ *ppStorageServer, &result)) == NULL)
+ {
+ return result;
+ }
+
+ *new_connection = true;
+ }
+ }
+
+ return 0;
+}
+
+int storage_get_metadata1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id, \
+ FDFSMetaData **meta_list, int *meta_count)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+
+ return storage_get_metadata(pTrackerServer, pStorageServer, \
+ group_name, filename, meta_list, meta_count);
+}
+
+int storage_get_metadata(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *filename, \
+ FDFSMetaData **meta_list, \
+ int *meta_count)
+{
+ TrackerHeader *pHeader;
+ int result;
+ ConnectionInfo storageServer;
+ char out_buff[sizeof(TrackerHeader)+FDFS_GROUP_NAME_MAX_LEN+128];
+ int64_t in_bytes;
+ int filename_len;
+ char *file_buff;
+ int64_t file_size;
+ bool new_connection;
+
+ file_buff = NULL;
+ *meta_list = NULL;
+ *meta_count = 0;
+
+ if ((result=storage_get_update_connection(pTrackerServer, \
+ &pStorageServer, group_name, filename, \
+ &storageServer, &new_connection)) != 0)
+ {
+ return result;
+ }
+
+ do
+ {
+ /**
+ send pkg format:
+ FDFS_GROUP_NAME_MAX_LEN bytes: group_name
+ remain bytes: filename
+ **/
+
+ pHeader = (TrackerHeader *)out_buff;
+ memset(out_buff, 0, sizeof(out_buff));
+ snprintf(out_buff + sizeof(TrackerHeader), sizeof(out_buff) - \
+ sizeof(TrackerHeader), "%s", group_name);
+ filename_len = snprintf(out_buff + sizeof(TrackerHeader) + \
+ FDFS_GROUP_NAME_MAX_LEN, \
+ sizeof(out_buff) - sizeof(TrackerHeader) - \
+ FDFS_GROUP_NAME_MAX_LEN, "%s", filename);
+
+ long2buff(FDFS_GROUP_NAME_MAX_LEN + filename_len, pHeader->pkg_len);
+ pHeader->cmd = STORAGE_PROTO_CMD_GET_METADATA;
+
+ if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
+ sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + \
+ filename_len, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+
+ break;
+ }
+
+ if ((result=fdfs_recv_response(pStorageServer, \
+ &file_buff, 0, &in_bytes)) != 0)
+ {
+ break;
+ }
+
+ file_size = in_bytes;
+ if (file_size == 0)
+ {
+ break;
+ }
+
+ file_buff[in_bytes] = '\0';
+ *meta_list = fdfs_split_metadata(file_buff, meta_count, &result);
+ } while (0);
+
+ if (file_buff != NULL)
+ {
+ free(file_buff);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(pStorageServer, result != 0);
+ }
+
+ return result;
+}
+
+int storage_query_file_info_ex1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_id, \
+ FDFSFileInfo *pFileInfo, const bool bSilence)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+ return storage_query_file_info_ex(pTrackerServer, pStorageServer, \
+ group_name, filename, pFileInfo, bSilence);
+}
+
+int storage_query_file_info_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *filename, \
+ FDFSFileInfo *pFileInfo, const bool bSilence)
+{
+ TrackerHeader *pHeader;
+ int result;
+ ConnectionInfo storageServer;
+ char out_buff[sizeof(TrackerHeader)+FDFS_GROUP_NAME_MAX_LEN+128];
+ char in_buff[3 * FDFS_PROTO_PKG_LEN_SIZE + IP_ADDRESS_SIZE];
+ char buff[64];
+ int64_t in_bytes;
+ int filename_len;
+ int buff_len;
+ char *pInBuff;
+ char *p;
+ bool new_connection;
+
+ if ((result=storage_get_read_connection(pTrackerServer, \
+ &pStorageServer, group_name, filename, \
+ &storageServer, &new_connection)) != 0)
+ {
+ return result;
+ }
+
+ do
+ {
+ /**
+ send pkg format:
+ FDFS_GROUP_NAME_MAX_LEN bytes: group_name
+ remain bytes: filename
+ **/
+
+ pHeader = (TrackerHeader *)out_buff;
+ memset(out_buff, 0, sizeof(out_buff));
+ snprintf(out_buff + sizeof(TrackerHeader), sizeof(out_buff) - \
+ sizeof(TrackerHeader), "%s", group_name);
+ filename_len = snprintf(out_buff + sizeof(TrackerHeader) + \
+ FDFS_GROUP_NAME_MAX_LEN, \
+ sizeof(out_buff) - sizeof(TrackerHeader) - \
+ FDFS_GROUP_NAME_MAX_LEN, "%s", filename);
+
+ long2buff(FDFS_GROUP_NAME_MAX_LEN + filename_len, pHeader->pkg_len);
+ pHeader->cmd = STORAGE_PROTO_CMD_QUERY_FILE_INFO;
+ pHeader->status = bSilence ? ENOENT : 0;
+
+ if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
+ sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + \
+ filename_len, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+
+ break;
+ }
+
+ pInBuff = in_buff;
+ if ((result=fdfs_recv_response(pStorageServer, \
+ &pInBuff, sizeof(in_buff), &in_bytes)) != 0)
+ {
+ break;
+ }
+
+ if (in_bytes != sizeof(in_buff))
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "recv data from storage server %s:%d fail, " \
+ "recv bytes: "INT64_PRINTF_FORMAT" != %d", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ in_bytes, (int)sizeof(in_buff));
+ result = EINVAL;
+ }
+
+ if (!the_base64_context_inited)
+ {
+ the_base64_context_inited = 1;
+ base64_init_ex(&the_base64_context, 0, '-', '_', '.');
+ }
+
+ memset(buff, 0, sizeof(buff));
+ if (filename_len >= FDFS_LOGIC_FILE_PATH_LEN \
+ + FDFS_FILENAME_BASE64_LENGTH + FDFS_FILE_EXT_NAME_MAX_LEN + 1)
+ {
+ base64_decode_auto(&the_base64_context, (char *)filename + \
+ FDFS_LOGIC_FILE_PATH_LEN, FDFS_FILENAME_BASE64_LENGTH, \
+ buff, &buff_len);
+ }
+
+ p = in_buff;
+ pFileInfo->file_size = buff2long(p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+ pFileInfo->create_timestamp = buff2long(p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+ pFileInfo->crc32 = buff2long(p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+ memcpy(pFileInfo->source_ip_addr, p, IP_ADDRESS_SIZE);
+ *(pFileInfo->source_ip_addr + IP_ADDRESS_SIZE - 1) = '\0';
+ } while (0);
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(pStorageServer, result != 0);
+ }
+
+ return result;
+}
+
+int storage_delete_file1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+
+ return storage_delete_file(pTrackerServer, \
+ pStorageServer, group_name, filename);
+}
+
+int storage_truncate_file1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer,
+ const char *appender_file_id, \
+ const int64_t truncated_file_size)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(appender_file_id)
+
+ return storage_truncate_file(pTrackerServer, \
+ pStorageServer, group_name, filename, \
+ truncated_file_size);
+}
+
+int storage_delete_file(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *filename)
+{
+ TrackerHeader *pHeader;
+ int result;
+ ConnectionInfo storageServer;
+ char out_buff[sizeof(TrackerHeader)+FDFS_GROUP_NAME_MAX_LEN+128];
+ char in_buff[1];
+ char *pBuff;
+ int64_t in_bytes;
+ int filename_len;
+ bool new_connection;
+
+ if ((result=storage_get_update_connection(pTrackerServer, \
+ &pStorageServer, group_name, filename, \
+ &storageServer, &new_connection)) != 0)
+ {
+ return result;
+ }
+
+ do
+ {
+ /**
+ send pkg format:
+ FDFS_GROUP_NAME_MAX_LEN bytes: group_name
+ remain bytes: filename
+ **/
+
+ memset(out_buff, 0, sizeof(out_buff));
+ snprintf(out_buff + sizeof(TrackerHeader), sizeof(out_buff) - \
+ sizeof(TrackerHeader), "%s", group_name);
+ filename_len = snprintf(out_buff + sizeof(TrackerHeader) + \
+ FDFS_GROUP_NAME_MAX_LEN, \
+ sizeof(out_buff) - sizeof(TrackerHeader) - \
+ FDFS_GROUP_NAME_MAX_LEN, "%s", filename);
+
+ pHeader = (TrackerHeader *)out_buff;
+ long2buff(FDFS_GROUP_NAME_MAX_LEN + filename_len, pHeader->pkg_len);
+ pHeader->cmd = STORAGE_PROTO_CMD_DELETE_FILE;
+
+ if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
+ sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + \
+ filename_len, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+
+ pBuff = in_buff;
+ if ((result=fdfs_recv_response(pStorageServer, \
+ &pBuff, 0, &in_bytes)) != 0)
+ {
+ break;
+ }
+
+ } while (0);
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(pStorageServer, result != 0);
+ }
+
+ return result;
+}
+
+int storage_do_download_file1_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const int download_type, const char *file_id, \
+ const int64_t file_offset, const int64_t download_bytes, \
+ char **file_buff, void *arg, int64_t *file_size)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+
+ return storage_do_download_file_ex(pTrackerServer, pStorageServer, \
+ download_type, group_name, filename, \
+ file_offset, download_bytes, file_buff, arg, file_size);
+}
+
+int storage_do_download_file_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const int download_type, \
+ const char *group_name, const char *remote_filename, \
+ const int64_t file_offset, const int64_t download_bytes, \
+ char **file_buff, void *arg, int64_t *file_size)
+{
+ TrackerHeader *pHeader;
+ int result;
+ ConnectionInfo storageServer;
+ char out_buff[sizeof(TrackerHeader)+FDFS_GROUP_NAME_MAX_LEN+128];
+ char *p;
+ int out_bytes;
+ int64_t in_bytes;
+ int64_t total_recv_bytes;
+ int filename_len;
+ bool new_connection;
+
+ *file_size = 0;
+ if ((result=storage_get_read_connection(pTrackerServer, \
+ &pStorageServer, group_name, remote_filename, \
+ &storageServer, &new_connection)) != 0)
+ {
+ return result;
+ }
+
+ do
+ {
+ /**
+ send pkg format:
+ 8 bytes: file offset
+ 8 bytes: download file bytes
+ FDFS_GROUP_NAME_MAX_LEN bytes: group_name
+ remain bytes: filename
+ **/
+
+ memset(out_buff, 0, sizeof(out_buff));
+ pHeader = (TrackerHeader *)out_buff;
+ p = out_buff + sizeof(TrackerHeader);
+ long2buff(file_offset, p);
+ p += 8;
+ long2buff(download_bytes, p);
+ p += 8;
+ snprintf(p, sizeof(out_buff) - (p - out_buff), "%s", group_name);
+ p += FDFS_GROUP_NAME_MAX_LEN;
+ filename_len = snprintf(p, sizeof(out_buff) - (p - out_buff), \
+ "%s", remote_filename);
+ p += filename_len;
+ out_bytes = p - out_buff;
+ long2buff(out_bytes - sizeof(TrackerHeader), pHeader->pkg_len);
+ pHeader->cmd = STORAGE_PROTO_CMD_DOWNLOAD_FILE;
+
+ if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
+ out_bytes, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+
+ if (download_type == FDFS_DOWNLOAD_TO_FILE)
+ {
+ if ((result=fdfs_recv_header(pStorageServer, \
+ &in_bytes)) != 0)
+ {
+ break;
+ }
+
+ if ((result=tcprecvfile(pStorageServer->sock, \
+ *file_buff, in_bytes, 0, \
+ g_fdfs_network_timeout, \
+ &total_recv_bytes)) != 0)
+ {
+ break;
+ }
+ }
+ else if (download_type == FDFS_DOWNLOAD_TO_BUFF)
+ {
+ *file_buff = NULL;
+ if ((result=fdfs_recv_response(pStorageServer, \
+ file_buff, 0, &in_bytes)) != 0)
+ {
+ break;
+ }
+ }
+ else
+ {
+ DownloadCallback callback;
+ char buff[2048];
+ int recv_bytes;
+ int64_t remain_bytes;
+
+ if ((result=fdfs_recv_header(pStorageServer, \
+ &in_bytes)) != 0)
+ {
+ break;
+ }
+
+ callback = (DownloadCallback)*file_buff;
+ remain_bytes = in_bytes;
+ while (remain_bytes > 0)
+ {
+ if (remain_bytes > sizeof(buff))
+ {
+ recv_bytes = sizeof(buff);
+ }
+ else
+ {
+ recv_bytes = remain_bytes;
+ }
+
+ if ((result=tcprecvdata_nb(pStorageServer->sock, buff, \
+ recv_bytes, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "recv data from storage server " \
+ "%s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, \
+ pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+
+ result = callback(arg, in_bytes, buff, recv_bytes);
+ if (result != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "call callback function fail, " \
+ "error code: %d", __LINE__, result);
+ break;
+ }
+
+ remain_bytes -= recv_bytes;
+ }
+
+ if (remain_bytes != 0)
+ {
+ break;
+ }
+ }
+
+ *file_size = in_bytes;
+ } while (0);
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(pStorageServer, result != 0);
+ }
+
+ return result;
+}
+
+int storage_download_file_to_file1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id, \
+ const char *local_filename, int64_t *file_size)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+
+ return storage_download_file_to_file(pTrackerServer, \
+ pStorageServer, group_name, filename, \
+ local_filename, file_size);
+}
+
+int storage_download_file_to_file(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *remote_filename, \
+ const char *local_filename, int64_t *file_size)
+{
+ char *pLocalFilename;
+ pLocalFilename = (char *)local_filename;
+ return storage_do_download_file(pTrackerServer, pStorageServer, \
+ FDFS_DOWNLOAD_TO_FILE, group_name, remote_filename, \
+ &pLocalFilename, NULL, file_size);
+}
+
+int storage_upload_by_filename1_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, const char *local_filename, \
+ const char *file_ext_name, const FDFSMetaData *meta_list, \
+ const int meta_count, const char *group_name, char *file_id)
+{
+ char new_group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ char remote_filename[128];
+ int result;
+
+ if (group_name == NULL)
+ {
+ *new_group_name = '\0';
+ }
+ else
+ {
+ snprintf(new_group_name, sizeof(new_group_name), \
+ "%s", group_name);
+ }
+
+ result = storage_upload_by_filename_ex(pTrackerServer, \
+ pStorageServer, store_path_index, cmd, \
+ local_filename, file_ext_name, \
+ meta_list, meta_count, \
+ new_group_name, remote_filename);
+ if (result == 0)
+ {
+ sprintf(file_id, "%s%c%s", new_group_name, \
+ FDFS_FILE_ID_SEPERATOR, remote_filename);
+ }
+ else
+ {
+ file_id[0] = '\0';
+ }
+
+ return result;
+}
+
+int storage_do_upload_file1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, const int upload_type, \
+ const char *file_buff, void *arg, const int64_t file_size, \
+ const char *file_ext_name, const FDFSMetaData *meta_list, \
+ const int meta_count, const char *group_name, char *file_id)
+{
+ char new_group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ char remote_filename[128];
+ int result;
+
+ if (group_name == NULL)
+ {
+ *new_group_name = '\0';
+ }
+ else
+ {
+ snprintf(new_group_name, sizeof(new_group_name), \
+ "%s", group_name);
+ }
+
+ result = storage_do_upload_file(pTrackerServer, \
+ pStorageServer, store_path_index, cmd, upload_type, \
+ file_buff, arg, file_size, NULL, NULL, file_ext_name, \
+ meta_list, meta_count, new_group_name, remote_filename);
+ if (result == 0)
+ {
+ sprintf(file_id, "%s%c%s", new_group_name, \
+ FDFS_FILE_ID_SEPERATOR, remote_filename);
+ }
+ else
+ {
+ file_id[0] = '\0';
+ }
+
+ return result;
+}
+
+/**
+STORAGE_PROTO_CMD_UPLOAD_FILE and
+STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE:
+1 byte: store path index
+8 bytes: meta data bytes
+8 bytes: file size
+FDFS_FILE_EXT_NAME_MAX_LEN bytes: file ext name
+meta data bytes: each meta data seperated by \x01,
+ name and value seperated by \x02
+file size bytes: file content
+
+STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE:
+8 bytes: master filename length
+8 bytes: meta data bytes
+8 bytes: file size
+FDFS_FILE_PREFIX_MAX_LEN bytes : filename prefix
+FDFS_FILE_EXT_NAME_MAX_LEN bytes: file ext name, do not include dot (.)
+master filename bytes: master filename
+meta data bytes: each meta data seperated by \x01,
+ name and value seperated by \x02
+file size bytes: file content
+**/
+int storage_do_upload_file(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, const int upload_type, const char *file_buff, \
+ void *arg, const int64_t file_size, const char *master_filename, \
+ const char *prefix_name, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *group_name, char *remote_filename)
+{
+ TrackerHeader *pHeader;
+ int result;
+ char out_buff[512];
+ char *p;
+ int64_t in_bytes;
+ int64_t total_send_bytes;
+ char in_buff[128];
+ char *pInBuff;
+ ConnectionInfo storageServer;
+ bool new_connection;
+ bool bUploadSlave;
+ int new_store_path;
+ int master_filename_len;
+ int prefix_len;
+
+ *remote_filename = '\0';
+ new_store_path = store_path_index;
+ if (master_filename != NULL)
+ {
+ master_filename_len = strlen(master_filename);
+ }
+ else
+ {
+ master_filename_len = 0;
+ }
+
+ if (prefix_name != NULL)
+ {
+ prefix_len = strlen(prefix_name);
+ }
+ else
+ {
+ prefix_len = 0;
+ }
+
+ bUploadSlave = (strlen(group_name) > 0 && master_filename_len > 0);
+ if (bUploadSlave)
+ {
+ if ((result=storage_get_update_connection(pTrackerServer, \
+ &pStorageServer, group_name, master_filename, \
+ &storageServer, &new_connection)) != 0)
+ {
+ return result;
+ }
+ }
+ else if ((result=storage_get_upload_connection(pTrackerServer, \
+ &pStorageServer, group_name, &storageServer, \
+ &new_store_path, &new_connection)) != 0)
+ {
+ *group_name = '\0';
+ return result;
+ }
+
+ *group_name = '\0';
+
+ /*
+ //logInfo("upload to storage %s:%d\n", \
+ pStorageServer->ip_addr, pStorageServer->port);
+ */
+
+ do
+ {
+ pHeader = (TrackerHeader *)out_buff;
+ p = out_buff + sizeof(TrackerHeader);
+ if (bUploadSlave)
+ {
+ long2buff(master_filename_len, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+ }
+ else
+ {
+ *p++ = (char)new_store_path;
+ }
+
+ long2buff(file_size, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ if (bUploadSlave)
+ {
+ memset(p, 0, FDFS_FILE_PREFIX_MAX_LEN + \
+ FDFS_FILE_EXT_NAME_MAX_LEN);
+ if (prefix_len > FDFS_FILE_PREFIX_MAX_LEN)
+ {
+ prefix_len = FDFS_FILE_PREFIX_MAX_LEN;
+ }
+ if (prefix_len > 0)
+ {
+ memcpy(p, prefix_name, prefix_len);
+ }
+ p += FDFS_FILE_PREFIX_MAX_LEN;
+ }
+ else
+ {
+ memset(p, 0, FDFS_FILE_EXT_NAME_MAX_LEN);
+ }
+
+ if (file_ext_name != NULL)
+ {
+ int file_ext_len;
+
+ file_ext_len = strlen(file_ext_name);
+ if (file_ext_len > FDFS_FILE_EXT_NAME_MAX_LEN)
+ {
+ file_ext_len = FDFS_FILE_EXT_NAME_MAX_LEN;
+ }
+ if (file_ext_len > 0)
+ {
+ memcpy(p, file_ext_name, file_ext_len);
+ }
+ }
+ p += FDFS_FILE_EXT_NAME_MAX_LEN;
+
+ if (bUploadSlave)
+ {
+ memcpy(p, master_filename, master_filename_len);
+ p += master_filename_len;
+ }
+
+ long2buff((p - out_buff) + file_size - sizeof(TrackerHeader), \
+ pHeader->pkg_len);
+ pHeader->cmd = cmd;
+ pHeader->status = 0;
+
+ if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
+ p - out_buff, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ if ((result=tcpsendfile(pStorageServer->sock, file_buff, \
+ file_size, g_fdfs_network_timeout, \
+ &total_send_bytes)) != 0)
+ {
+ break;
+ }
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ if ((result=tcpsenddata_nb(pStorageServer->sock, \
+ (char *)file_buff, file_size, \
+ g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+ }
+ else //FDFS_UPLOAD_BY_CALLBACK
+ {
+ UploadCallback callback;
+ callback = (UploadCallback)file_buff;
+ if ((result=callback(arg, file_size, pStorageServer->sock))!=0)
+ {
+ break;
+ }
+ }
+
+ pInBuff = in_buff;
+ if ((result=fdfs_recv_response(pStorageServer, \
+ &pInBuff, sizeof(in_buff), &in_bytes)) != 0)
+ {
+ break;
+ }
+
+ if (in_bytes <= FDFS_GROUP_NAME_MAX_LEN)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "storage server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid, " \
+ "should > %d", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ in_bytes, FDFS_GROUP_NAME_MAX_LEN);
+ result = EINVAL;
+ break;
+ }
+
+ in_buff[in_bytes] = '\0';
+ memcpy(group_name, in_buff, FDFS_GROUP_NAME_MAX_LEN);
+ group_name[FDFS_GROUP_NAME_MAX_LEN] = '\0';
+
+ memcpy(remote_filename, in_buff + FDFS_GROUP_NAME_MAX_LEN, \
+ in_bytes - FDFS_GROUP_NAME_MAX_LEN + 1);
+
+ } while (0);
+
+ if (result == 0 && meta_count > 0)
+ {
+ result = storage_set_metadata(pTrackerServer, \
+ pStorageServer, group_name, remote_filename, \
+ meta_list, meta_count, \
+ STORAGE_SET_METADATA_FLAG_OVERWRITE);
+ if (result != 0) //rollback
+ {
+ storage_delete_file(pTrackerServer, pStorageServer, \
+ group_name, remote_filename);
+ *group_name = '\0';
+ *remote_filename = '\0';
+ }
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(pStorageServer, result != 0);
+ }
+
+ return result;
+}
+
+int storage_upload_by_callback_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, UploadCallback callback, void *arg, \
+ const int64_t file_size, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *group_name, char *remote_filename)
+{
+ return storage_do_upload_file(pTrackerServer, pStorageServer, \
+ store_path_index, cmd, FDFS_UPLOAD_BY_CALLBACK, \
+ (char *)callback, arg, file_size, NULL, NULL, \
+ file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename);
+}
+
+int storage_upload_by_callback1_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, UploadCallback callback, void *arg, \
+ const int64_t file_size, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ const char *group_name, char *file_id)
+{
+ char new_group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ char remote_filename[128];
+ int result;
+
+ if (group_name == NULL)
+ {
+ *new_group_name = '\0';
+ }
+ else
+ {
+ snprintf(new_group_name, sizeof(new_group_name), \
+ "%s", group_name);
+ }
+
+ result = storage_do_upload_file(pTrackerServer, \
+ pStorageServer, store_path_index, \
+ cmd, FDFS_UPLOAD_BY_CALLBACK, (char *)callback, arg, \
+ file_size, NULL, NULL, file_ext_name, \
+ meta_list, meta_count, \
+ new_group_name, remote_filename);
+ if (result == 0)
+ {
+ sprintf(file_id, "%s%c%s", new_group_name, \
+ FDFS_FILE_ID_SEPERATOR, remote_filename);
+ }
+ else
+ {
+ file_id[0] = '\0';
+ }
+
+ return result;
+}
+
+int storage_upload_by_filename_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, const char *local_filename, \
+ const char *file_ext_name, const FDFSMetaData *meta_list, \
+ const int meta_count, char *group_name, char *remote_filename)
+{
+ struct stat stat_buf;
+
+ if (stat(local_filename, &stat_buf) != 0)
+ {
+ group_name[0] = '\0';
+ remote_filename[0] = '\0';
+ return errno;
+ }
+
+ if (!S_ISREG(stat_buf.st_mode))
+ {
+ group_name[0] = '\0';
+ remote_filename[0] = '\0';
+ return EINVAL;
+ }
+
+ if (file_ext_name == NULL)
+ {
+ file_ext_name = fdfs_get_file_ext_name(local_filename);
+ }
+
+ return storage_do_upload_file(pTrackerServer, pStorageServer, \
+ store_path_index, cmd, \
+ FDFS_UPLOAD_BY_FILE, local_filename, \
+ NULL, stat_buf.st_size, NULL, NULL, file_ext_name, \
+ meta_list, meta_count, group_name, remote_filename);
+}
+
+int storage_set_metadata1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ const char op_flag)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+
+ return storage_set_metadata(pTrackerServer, pStorageServer, \
+ group_name, filename, \
+ meta_list, meta_count, op_flag);
+}
+
+/**
+8 bytes: filename length
+8 bytes: meta data size
+1 bytes: operation flag,
+ 'O' for overwrite all old metadata
+ 'M' for merge, insert when the meta item not exist, otherwise update it
+FDFS_GROUP_NAME_MAX_LEN bytes: group_name
+filename
+meta data bytes: each meta data seperated by \x01,
+ name and value seperated by \x02
+**/
+int storage_set_metadata(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *filename, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ const char op_flag)
+{
+ TrackerHeader *pHeader;
+ int result;
+ ConnectionInfo storageServer;
+ char out_buff[sizeof(TrackerHeader)+2*FDFS_PROTO_PKG_LEN_SIZE+\
+ FDFS_GROUP_NAME_MAX_LEN+128];
+ char in_buff[1];
+ int64_t in_bytes;
+ char *pBuff;
+ int filename_len;
+ char *meta_buff;
+ int meta_bytes;
+ char *p;
+ char *pEnd;
+ bool new_connection;
+
+ if ((result=storage_get_update_connection(pTrackerServer, \
+ &pStorageServer, group_name, filename, \
+ &storageServer, &new_connection)) != 0)
+ {
+ return result;
+ }
+
+ meta_buff = NULL;
+ do
+ {
+ memset(out_buff, 0, sizeof(out_buff));
+ filename_len = strlen(filename);
+
+ if (meta_count > 0)
+ {
+ meta_buff = fdfs_pack_metadata(meta_list, meta_count, \
+ NULL, &meta_bytes);
+ if (meta_buff == NULL)
+ {
+ result = ENOMEM;
+ break;
+ }
+ }
+ else
+ {
+ meta_bytes = 0;
+ }
+
+ pEnd = out_buff + sizeof(out_buff);
+ p = out_buff + sizeof(TrackerHeader);
+
+ long2buff(filename_len, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ long2buff(meta_bytes, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ *p++ = op_flag;
+
+ snprintf(p, pEnd - p, "%s", group_name);
+ p += FDFS_GROUP_NAME_MAX_LEN;
+
+ filename_len = snprintf(p, pEnd - p, "%s", filename);
+ p += filename_len;
+
+ pHeader = (TrackerHeader *)out_buff;
+ long2buff((int)(p - (out_buff + sizeof(TrackerHeader))) + \
+ meta_bytes, pHeader->pkg_len);
+ pHeader->cmd = STORAGE_PROTO_CMD_SET_METADATA;
+
+ if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
+ p - out_buff, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+
+ break;
+ }
+
+ if (meta_bytes > 0 && (result=tcpsenddata_nb(pStorageServer->sock, \
+ meta_buff, meta_bytes, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+
+ pBuff = in_buff;
+ result = fdfs_recv_response(pStorageServer, \
+ &pBuff, 0, &in_bytes);
+ } while (0);
+
+ if (meta_buff != NULL)
+ {
+ free(meta_buff);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(pStorageServer, result != 0);
+ }
+
+ return result;
+}
+
+int storage_download_file_ex1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id, \
+ const int64_t file_offset, const int64_t download_bytes, \
+ DownloadCallback callback, void *arg, int64_t *file_size)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+
+ return storage_download_file_ex(pTrackerServer, pStorageServer, \
+ group_name, filename, file_offset, download_bytes, \
+ callback, arg, file_size);
+}
+
+int storage_download_file_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *remote_filename, \
+ const int64_t file_offset, const int64_t download_bytes, \
+ DownloadCallback callback, void *arg, int64_t *file_size)
+{
+ char *pCallback;
+ pCallback = (char *)callback;
+ return storage_do_download_file_ex(pTrackerServer, pStorageServer, \
+ FDFS_DOWNLOAD_TO_CALLBACK, group_name, remote_filename, \
+ file_offset, download_bytes, &pCallback, arg, file_size);
+}
+
+int tracker_query_storage_fetch1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+
+ return tracker_query_storage_fetch(pTrackerServer, \
+ pStorageServer, group_name, filename);
+}
+
+int tracker_query_storage_update1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+
+ return tracker_query_storage_update(pTrackerServer, \
+ pStorageServer, group_name, filename);
+}
+
+/**
+pkg format:
+Header
+8 bytes: master filename len
+8 bytes: source filename len
+8 bytes: source file signature len
+FDFS_GROUP_NAME_MAX_LEN bytes: group_name
+FDFS_FILE_PREFIX_MAX_LEN bytes : filename prefix, can be empty
+FDFS_FILE_EXT_NAME_MAX_LEN bytes: file ext name, do not include dot (.)
+master filename len: master filename
+source filename len: source filename without group name
+source file signature len: source file signature
+**/
+int storage_client_create_link(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *master_filename,\
+ const char *src_filename, const int src_filename_len, \
+ const char *src_file_sig, const int src_file_sig_len, \
+ const char *group_name, const char *prefix_name, \
+ const char *file_ext_name, \
+ char *remote_filename, int *filename_len)
+{
+ TrackerHeader *pHeader;
+ int result;
+ char out_buff[sizeof(TrackerHeader) + 4 * FDFS_PROTO_PKG_LEN_SIZE + \
+ FDFS_GROUP_NAME_MAX_LEN + FDFS_FILE_PREFIX_MAX_LEN + \
+ FDFS_FILE_EXT_NAME_MAX_LEN + 256];
+ char in_buff[128];
+ char *p;
+ int group_name_len;
+ int master_filename_len;
+ int64_t in_bytes;
+ char *pInBuff;
+ ConnectionInfo storageServer;
+ bool new_connection;
+
+ *remote_filename = '\0';
+ if (master_filename != NULL)
+ {
+ master_filename_len = strlen(master_filename);
+ }
+ else
+ {
+ master_filename_len = 0;
+ }
+ if (src_filename_len >= 128 || src_file_sig_len > 64 || \
+ master_filename_len >= 128)
+ {
+ return EINVAL;
+ }
+
+ if ((result=storage_get_update_connection(pTrackerServer, \
+ &pStorageServer, group_name, src_filename, \
+ &storageServer, &new_connection)) != 0)
+ {
+ return result;
+ }
+
+ do
+ {
+ memset(out_buff, 0, sizeof(out_buff));
+ p = out_buff + sizeof(TrackerHeader);
+ long2buff(master_filename_len, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+ long2buff(src_filename_len, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+ long2buff(src_file_sig_len, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ group_name_len = strlen(group_name);
+ if (group_name_len > FDFS_GROUP_NAME_MAX_LEN)
+ {
+ group_name_len = FDFS_GROUP_NAME_MAX_LEN;
+ }
+ memcpy(p, group_name, group_name_len);
+ p += FDFS_GROUP_NAME_MAX_LEN;
+
+ if (prefix_name != NULL)
+ {
+ int prefix_len;
+
+ prefix_len = strlen(prefix_name);
+ if (prefix_len > FDFS_FILE_PREFIX_MAX_LEN)
+ {
+ prefix_len = FDFS_FILE_PREFIX_MAX_LEN;
+ }
+ if (prefix_len > 0)
+ {
+ memcpy(p, prefix_name, prefix_len);
+ }
+ }
+ p += FDFS_FILE_PREFIX_MAX_LEN;
+
+ if (file_ext_name != NULL)
+ {
+ int file_ext_len;
+
+ file_ext_len = strlen(file_ext_name);
+ if (file_ext_len > FDFS_FILE_EXT_NAME_MAX_LEN)
+ {
+ file_ext_len = FDFS_FILE_EXT_NAME_MAX_LEN;
+ }
+ if (file_ext_len > 0)
+ {
+ memcpy(p, file_ext_name, file_ext_len);
+ }
+ }
+ p += FDFS_FILE_EXT_NAME_MAX_LEN;
+
+ if (master_filename_len > 0)
+ {
+ memcpy(p, master_filename, master_filename_len);
+ p += master_filename_len;
+ }
+ memcpy(p, src_filename, src_filename_len);
+ p += src_filename_len;
+ memcpy(p, src_file_sig, src_file_sig_len);
+ p += src_file_sig_len;
+
+ pHeader = (TrackerHeader *)out_buff;
+ long2buff(p - out_buff - sizeof(TrackerHeader), pHeader->pkg_len);
+ pHeader->cmd = STORAGE_PROTO_CMD_CREATE_LINK;
+ if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
+ p - out_buff, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+
+ pInBuff = in_buff;
+ if ((result=fdfs_recv_response(pStorageServer, \
+ &pInBuff, sizeof(in_buff), &in_bytes)) != 0)
+ {
+ break;
+ }
+
+ if (in_bytes <= FDFS_GROUP_NAME_MAX_LEN)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "storage server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid, " \
+ "should > %d", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ in_bytes, FDFS_GROUP_NAME_MAX_LEN);
+ result = EINVAL;
+ break;
+ }
+
+ *(in_buff + in_bytes) = '\0';
+ *filename_len = in_bytes - FDFS_GROUP_NAME_MAX_LEN;
+ memcpy(remote_filename, in_buff + FDFS_GROUP_NAME_MAX_LEN, \
+ (*filename_len) + 1);
+
+ } while (0);
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(pStorageServer, result != 0);
+ }
+
+ return result;
+}
+
+int tracker_query_storage_list1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int nMaxServerCount, \
+ int *server_count, const char *file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+ return tracker_query_storage_list(pTrackerServer, \
+ pStorageServer, nMaxServerCount, \
+ server_count, group_name, filename);
+}
+
+int storage_upload_slave_by_filename(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const char *master_filename, const char *prefix_name, \
+ const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *group_name, char *remote_filename)
+{
+ struct stat stat_buf;
+
+ if (master_filename == NULL || *master_filename == '\0' || \
+ prefix_name == NULL || group_name == NULL || *group_name == '\0')
+ {
+ return EINVAL;
+ }
+
+ if (stat(local_filename, &stat_buf) != 0)
+ {
+ *group_name = '\0';
+ *remote_filename = '\0';
+ return errno != 0 ? errno : EPERM;
+ }
+
+ if (!S_ISREG(stat_buf.st_mode))
+ {
+ *group_name = '\0';
+ *remote_filename = '\0';
+ return EINVAL;
+ }
+
+ if (file_ext_name == NULL)
+ {
+ file_ext_name = fdfs_get_file_ext_name(local_filename);
+ }
+
+ return storage_do_upload_file(pTrackerServer, pStorageServer, \
+ 0, STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, \
+ FDFS_UPLOAD_BY_FILE, local_filename, \
+ NULL, stat_buf.st_size, master_filename, prefix_name, \
+ file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename);
+}
+
+int storage_upload_slave_by_callback(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, \
+ const int64_t file_size, const char *master_filename, \
+ const char *prefix_name, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *group_name, char *remote_filename)
+{
+ if (master_filename == NULL || *master_filename == '\0' || \
+ prefix_name == NULL || *prefix_name == '\0' || \
+ group_name == NULL || *group_name == '\0')
+ {
+ return EINVAL;
+ }
+
+ return storage_do_upload_file(pTrackerServer, pStorageServer, \
+ 0, STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, \
+ FDFS_UPLOAD_BY_CALLBACK, (char *)callback, arg, \
+ file_size, master_filename, prefix_name, \
+ file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename);
+}
+
+int storage_upload_slave_by_filebuff(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_size, const char *master_filename, \
+ const char *prefix_name, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *group_name, char *remote_filename)
+{
+ if (master_filename == NULL || *master_filename == '\0' || \
+ prefix_name == NULL || *prefix_name == '\0' || \
+ group_name == NULL || *group_name == '\0')
+ {
+ return EINVAL;
+ }
+
+ return storage_do_upload_file(pTrackerServer, pStorageServer, \
+ 0, STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, \
+ FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
+ file_size, master_filename, prefix_name, \
+ file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename);
+}
+
+int storage_upload_slave_by_filename1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const char *master_file_id, const char *prefix_name, \
+ const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *file_id)
+{
+ int result;
+ char new_group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ char remote_filename[128];
+
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(master_file_id)
+
+ strcpy(new_group_name, group_name);
+ result = storage_upload_slave_by_filename(pTrackerServer, \
+ pStorageServer, local_filename, filename, \
+ prefix_name, file_ext_name, \
+ meta_list, meta_count, \
+ new_group_name, remote_filename);
+ if (result == 0)
+ {
+ sprintf(file_id, "%s%c%s", new_group_name, \
+ FDFS_FILE_ID_SEPERATOR, remote_filename);
+ }
+ else
+ {
+ *file_id = '\0';
+ }
+
+ return result;
+}
+
+int storage_upload_slave_by_filebuff1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_size, const char *master_file_id, \
+ const char *prefix_name, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *file_id)
+{
+ int result;
+ char new_group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ char remote_filename[128];
+
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(master_file_id)
+
+ strcpy(new_group_name, group_name);
+ result = storage_upload_slave_by_filebuff(pTrackerServer, \
+ pStorageServer, file_buff, file_size, \
+ filename, prefix_name, file_ext_name, \
+ meta_list, meta_count, \
+ new_group_name, remote_filename);
+ if (result == 0)
+ {
+ sprintf(file_id, "%s%c%s", new_group_name, \
+ FDFS_FILE_ID_SEPERATOR, remote_filename);
+ }
+ else
+ {
+ *file_id = '\0';
+ }
+
+ return result;
+}
+
+int storage_upload_slave_by_callback1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, \
+ const int64_t file_size, const char *master_file_id, \
+ const char *prefix_name, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *file_id)
+{
+ int result;
+ char new_group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ char remote_filename[128];
+
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(master_file_id)
+
+ strcpy(new_group_name, group_name);
+ result = storage_upload_slave_by_callback(pTrackerServer, \
+ pStorageServer, callback, arg, file_size, \
+ filename, prefix_name, file_ext_name, \
+ meta_list, meta_count, \
+ new_group_name, remote_filename);
+ if (result == 0)
+ {
+ sprintf(file_id, "%s%c%s", new_group_name, \
+ FDFS_FILE_ID_SEPERATOR, remote_filename);
+ }
+ else
+ {
+ *file_id = '\0';
+ }
+
+ return result;
+}
+
+/**
+STORAGE_PROTO_CMD_APPEND_FILE:
+8 bytes: appender filename length
+8 bytes: file size
+master filename bytes: appender filename
+file size bytes: file content
+**/
+int storage_do_append_file(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int upload_type, \
+ const char *file_buff, void *arg, const int64_t file_size, \
+ const char *group_name, const char *appender_filename)
+{
+ TrackerHeader *pHeader;
+ int result;
+ char out_buff[512];
+ char *p;
+ int64_t in_bytes;
+ int64_t total_send_bytes;
+ ConnectionInfo storageServer;
+ bool new_connection;
+ int appender_filename_len;
+
+ appender_filename_len = strlen(appender_filename);
+
+ if ((result=storage_get_update_connection(pTrackerServer, \
+ &pStorageServer, group_name, appender_filename, \
+ &storageServer, &new_connection)) != 0)
+ {
+ return result;
+ }
+
+ /*
+ //printf("upload to storage %s:%d\n", \
+ pStorageServer->ip_addr, pStorageServer->port);
+ */
+
+ do
+ {
+ pHeader = (TrackerHeader *)out_buff;
+ p = out_buff + sizeof(TrackerHeader);
+ long2buff(appender_filename_len, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ long2buff(file_size, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ memcpy(p, appender_filename, appender_filename_len);
+ p += appender_filename_len;
+
+ long2buff((p - out_buff) + file_size - sizeof(TrackerHeader), \
+ pHeader->pkg_len);
+ pHeader->cmd = STORAGE_PROTO_CMD_APPEND_FILE;
+ pHeader->status = 0;
+
+ if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
+ p - out_buff, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ if ((result=tcpsendfile(pStorageServer->sock, file_buff, \
+ file_size, g_fdfs_network_timeout, \
+ &total_send_bytes)) != 0)
+ {
+ break;
+ }
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ if ((result=tcpsenddata_nb(pStorageServer->sock, \
+ (char *)file_buff, file_size, \
+ g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+ }
+ else //FDFS_UPLOAD_BY_CALLBACK
+ {
+ UploadCallback callback;
+ callback = (UploadCallback)file_buff;
+ if ((result=callback(arg, file_size, pStorageServer->sock))!=0)
+ {
+ break;
+ }
+ }
+
+ if ((result=fdfs_recv_header(pStorageServer, &in_bytes)) != 0)
+ {
+ break;
+ }
+
+ if (in_bytes != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "storage server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid, " \
+ "should == 0", __LINE__, pStorageServer->ip_addr, \
+ pStorageServer->port, in_bytes);
+ result = EINVAL;
+ break;
+ }
+
+ } while (0);
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(pStorageServer, result != 0);
+ }
+
+ return result;
+}
+
+/**
+STORAGE_PROTO_CMD_APPEND_FILE:
+8 bytes: appender filename length
+8 bytes: file offset
+8 bytes: file size
+master filename bytes: appender filename
+file size bytes: file content
+**/
+int storage_do_modify_file(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int upload_type, \
+ const char *file_buff, void *arg, const int64_t file_offset, \
+ const int64_t file_size, const char *group_name, \
+ const char *appender_filename)
+{
+ TrackerHeader *pHeader;
+ int result;
+ char out_buff[512];
+ char *p;
+ int64_t in_bytes;
+ int64_t total_send_bytes;
+ ConnectionInfo storageServer;
+ bool new_connection;
+ int appender_filename_len;
+
+ appender_filename_len = strlen(appender_filename);
+ if ((result=storage_get_update_connection(pTrackerServer, \
+ &pStorageServer, group_name, appender_filename, \
+ &storageServer, &new_connection)) != 0)
+ {
+ return result;
+ }
+
+ /*
+ //printf("upload to storage %s:%d\n", \
+ pStorageServer->ip_addr, pStorageServer->port);
+ */
+
+ do
+ {
+ pHeader = (TrackerHeader *)out_buff;
+ p = out_buff + sizeof(TrackerHeader);
+ long2buff(appender_filename_len, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ long2buff(file_offset, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ long2buff(file_size, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ memcpy(p, appender_filename, appender_filename_len);
+ p += appender_filename_len;
+
+ long2buff((p - out_buff) + file_size - sizeof(TrackerHeader), \
+ pHeader->pkg_len);
+ pHeader->cmd = STORAGE_PROTO_CMD_MODIFY_FILE;
+ pHeader->status = 0;
+
+ if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
+ p - out_buff, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+
+ if (upload_type == FDFS_UPLOAD_BY_FILE)
+ {
+ if ((result=tcpsendfile(pStorageServer->sock, file_buff, \
+ file_size, g_fdfs_network_timeout, \
+ &total_send_bytes)) != 0)
+ {
+ break;
+ }
+ }
+ else if (upload_type == FDFS_UPLOAD_BY_BUFF)
+ {
+ if ((result=tcpsenddata_nb(pStorageServer->sock, \
+ (char *)file_buff, file_size, \
+ g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+ }
+ else //FDFS_UPLOAD_BY_CALLBACK
+ {
+ UploadCallback callback;
+ callback = (UploadCallback)file_buff;
+ if ((result=callback(arg, file_size, pStorageServer->sock))!=0)
+ {
+ break;
+ }
+ }
+
+ if ((result=fdfs_recv_header(pStorageServer, &in_bytes)) != 0)
+ {
+ break;
+ }
+
+ if (in_bytes != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "storage server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid, " \
+ "should == 0", __LINE__, pStorageServer->ip_addr, \
+ pStorageServer->port, in_bytes);
+ result = EINVAL;
+ break;
+ }
+
+ } while (0);
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(pStorageServer, result != 0);
+ }
+
+ return result;
+}
+
+int storage_append_by_filename(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const char *group_name, const char *appender_filename)
+{
+ struct stat stat_buf;
+
+ if (appender_filename == NULL || *appender_filename == '\0' \
+ || group_name == NULL || *group_name == '\0')
+ {
+ return EINVAL;
+ }
+
+ if (stat(local_filename, &stat_buf) != 0)
+ {
+ return errno != 0 ? errno : EPERM;
+ }
+
+ if (!S_ISREG(stat_buf.st_mode))
+ {
+ return EINVAL;
+ }
+ return storage_do_append_file(pTrackerServer, pStorageServer, \
+ FDFS_UPLOAD_BY_FILE, local_filename, \
+ NULL, stat_buf.st_size, group_name, appender_filename);
+}
+
+int storage_append_by_callback(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, const int64_t file_size, \
+ const char *group_name, const char *appender_filename)
+{
+ if (appender_filename == NULL || *appender_filename == '\0' \
+ || group_name == NULL || *group_name == '\0')
+ {
+ return EINVAL;
+ }
+
+ return storage_do_append_file(pTrackerServer, pStorageServer, \
+ FDFS_UPLOAD_BY_CALLBACK, (char *)callback, arg, \
+ file_size, group_name, appender_filename);
+}
+
+int storage_append_by_filebuff(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_size, const char *group_name, \
+ const char *appender_filename)
+{
+ if (appender_filename == NULL || *appender_filename == '\0' \
+ || group_name == NULL || *group_name == '\0')
+ {
+ return EINVAL;
+ }
+
+ return storage_do_append_file(pTrackerServer, pStorageServer, \
+ FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
+ file_size, group_name, appender_filename);
+}
+
+int storage_append_by_filename1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const char *appender_file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(appender_file_id)
+
+ return storage_append_by_filename(pTrackerServer, \
+ pStorageServer, local_filename, group_name, filename);
+}
+
+int storage_append_by_filebuff1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_size, const char *appender_file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(appender_file_id)
+
+ return storage_append_by_filebuff(pTrackerServer, \
+ pStorageServer, file_buff, file_size, \
+ group_name, filename);
+}
+
+int storage_append_by_callback1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, \
+ const int64_t file_size, const char *appender_file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(appender_file_id)
+
+ return storage_append_by_callback(pTrackerServer, \
+ pStorageServer, callback, arg, file_size, \
+ group_name, filename);
+}
+
+int storage_modify_by_filename(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const int64_t file_offset, const char *group_name, \
+ const char *appender_filename)
+{
+ struct stat stat_buf;
+
+ if (appender_filename == NULL || *appender_filename == '\0' \
+ || group_name == NULL || *group_name == '\0')
+ {
+ return EINVAL;
+ }
+
+ if (stat(local_filename, &stat_buf) != 0)
+ {
+ return errno != 0 ? errno : EPERM;
+ }
+
+ if (!S_ISREG(stat_buf.st_mode))
+ {
+ return EINVAL;
+ }
+ return storage_do_modify_file(pTrackerServer, pStorageServer, \
+ FDFS_UPLOAD_BY_FILE, local_filename, \
+ NULL, file_offset, stat_buf.st_size, \
+ group_name, appender_filename);
+}
+
+int storage_modify_by_callback(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, const int64_t file_offset,\
+ const int64_t file_size, const char *group_name, \
+ const char *appender_filename)
+{
+ if (appender_filename == NULL || *appender_filename == '\0' \
+ || group_name == NULL || *group_name == '\0')
+ {
+ return EINVAL;
+ }
+
+ return storage_do_modify_file(pTrackerServer, pStorageServer, \
+ FDFS_UPLOAD_BY_CALLBACK, (char *)callback, arg, \
+ file_offset, file_size, group_name, appender_filename);
+}
+
+int storage_modify_by_filebuff(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_offset, const int64_t file_size, \
+ const char *group_name, const char *appender_filename)
+{
+ if (appender_filename == NULL || *appender_filename == '\0' \
+ || group_name == NULL || *group_name == '\0')
+ {
+ return EINVAL;
+ }
+
+ return storage_do_modify_file(pTrackerServer, pStorageServer, \
+ FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
+ file_offset, file_size, group_name, appender_filename);
+}
+
+int storage_modify_by_filename1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const int64_t file_offset, const char *appender_file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(appender_file_id)
+
+ return storage_modify_by_filename(pTrackerServer, \
+ pStorageServer, local_filename, file_offset, \
+ group_name, filename);
+}
+
+int storage_modify_by_filebuff1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_offset, const int64_t file_size, \
+ const char *appender_file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(appender_file_id)
+
+ return storage_modify_by_filebuff(pTrackerServer, \
+ pStorageServer, file_buff, file_offset, file_size, \
+ group_name, filename);
+}
+
+int storage_modify_by_callback1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, \
+ const int64_t file_offset, const int64_t file_size, \
+ const char *appender_file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(appender_file_id)
+
+ return storage_modify_by_callback(pTrackerServer, \
+ pStorageServer, callback, arg, file_offset, file_size, \
+ group_name, filename);
+}
+
+int fdfs_get_file_info_ex1(const char *file_id, const bool get_from_server, \
+ FDFSFileInfo *pFileInfo)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+
+ return fdfs_get_file_info_ex(group_name, filename, get_from_server, \
+ pFileInfo);
+}
+
+int fdfs_get_file_info_ex(const char *group_name, const char *remote_filename, \
+ const bool get_from_server, FDFSFileInfo *pFileInfo)
+{
+ struct in_addr ip_addr;
+ int filename_len;
+ int buff_len;
+ int result;
+ char buff[64];
+
+ memset(pFileInfo, 0, sizeof(FDFSFileInfo));
+ if (!the_base64_context_inited)
+ {
+ the_base64_context_inited = 1;
+ base64_init_ex(&the_base64_context, 0, '-', '_', '.');
+ }
+
+ filename_len = strlen(remote_filename);
+ if (filename_len < FDFS_NORMAL_LOGIC_FILENAME_LENGTH)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "filename is too short, length: %d < %d", \
+ __LINE__, filename_len, \
+ FDFS_NORMAL_LOGIC_FILENAME_LENGTH);
+ return EINVAL;
+ }
+
+ memset(buff, 0, sizeof(buff));
+ base64_decode_auto(&the_base64_context, (char *)remote_filename + \
+ FDFS_LOGIC_FILE_PATH_LEN, FDFS_FILENAME_BASE64_LENGTH, \
+ buff, &buff_len);
+
+ memset(&ip_addr, 0, sizeof(ip_addr));
+ ip_addr.s_addr = ntohl(buff2int(buff));
+ if (fdfs_get_server_id_type(ip_addr.s_addr) == FDFS_ID_TYPE_SERVER_ID)
+ {
+ pFileInfo->source_id = ip_addr.s_addr;
+ if (g_storage_ids_by_id != NULL && g_storage_id_count > 0)
+ {
+ char id[16];
+ FDFSStorageIdInfo *pStorageId;
+
+ sprintf(id, "%d", pFileInfo->source_id);
+ pStorageId = fdfs_get_storage_by_id(id);
+ if (pStorageId != NULL)
+ {
+ strcpy(pFileInfo->source_ip_addr, \
+ pStorageId->ip_addr);
+ }
+ else
+ {
+ *(pFileInfo->source_ip_addr) = '\0';
+ }
+ }
+ else
+ {
+ *(pFileInfo->source_ip_addr) = '\0';
+ }
+ }
+ else
+ {
+ pFileInfo->source_id = 0;
+ inet_ntop(AF_INET, &ip_addr, pFileInfo->source_ip_addr, \
+ IP_ADDRESS_SIZE);
+ }
+
+ pFileInfo->create_timestamp = buff2int(buff + sizeof(int));
+ pFileInfo->file_size = buff2long(buff + sizeof(int) * 2);
+
+ if (IS_SLAVE_FILE(filename_len, pFileInfo->file_size) || \
+ IS_APPENDER_FILE(pFileInfo->file_size) || \
+ (*(pFileInfo->source_ip_addr) == '\0' && get_from_server))
+ { //slave file or appender file
+ if (get_from_server)
+ {
+ ConnectionInfo *conn;
+ ConnectionInfo trackerServer;
+
+ conn = tracker_get_connection_r(&trackerServer, &result);
+ if (result != 0)
+ {
+ return result;
+ }
+
+ result = storage_query_file_info(conn, \
+ NULL, group_name, remote_filename, pFileInfo);
+ tracker_disconnect_server_ex(conn, result != 0 && \
+ result != ENOENT);
+
+ return result;
+ }
+ else
+ {
+ pFileInfo->file_size = -1;
+ return 0;
+ }
+ }
+ else //master file (normal file)
+ {
+ if ((pFileInfo->file_size >> 63) != 0)
+ {
+ pFileInfo->file_size &= 0xFFFFFFFF; //low 32 bits is file size
+ }
+ else if (IS_TRUNK_FILE(pFileInfo->file_size))
+ {
+ pFileInfo->file_size = FDFS_TRUNK_FILE_TRUE_SIZE( \
+ pFileInfo->file_size);
+ }
+
+ pFileInfo->crc32 = buff2int(buff+sizeof(int)*4);
+ }
+
+ return 0;
+}
+
+int storage_file_exist(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *remote_filename)
+{
+ FDFSFileInfo file_info;
+ return storage_query_file_info_ex(pTrackerServer, \
+ pStorageServer, group_name, remote_filename, \
+ &file_info, true);
+}
+
+int storage_file_exist1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id)
+{
+ FDFS_SPLIT_GROUP_NAME_AND_FILENAME(file_id)
+ return storage_file_exist(pTrackerServer, pStorageServer, \
+ group_name, filename);
+}
+
+int storage_truncate_file(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer,
+ const char *group_name, const char *appender_filename, \
+ const int64_t truncated_file_size)
+{
+ TrackerHeader *pHeader;
+ int result;
+ char out_buff[512];
+ char *p;
+ int64_t in_bytes;
+ ConnectionInfo storageServer;
+ bool new_connection;
+ int appender_filename_len;
+
+ appender_filename_len = strlen(appender_filename);
+ if ((result=storage_get_update_connection(pTrackerServer, \
+ &pStorageServer, group_name, appender_filename, \
+ &storageServer, &new_connection)) != 0)
+ {
+ return result;
+ }
+
+ /*
+ //printf("upload to storage %s:%d\n", \
+ pStorageServer->ip_addr, pStorageServer->port);
+ */
+
+ do
+ {
+ pHeader = (TrackerHeader *)out_buff;
+ p = out_buff + sizeof(TrackerHeader);
+ long2buff(appender_filename_len, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ long2buff(truncated_file_size, p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+
+ memcpy(p, appender_filename, appender_filename_len);
+ p += appender_filename_len;
+
+ long2buff((p - out_buff) - sizeof(TrackerHeader), \
+ pHeader->pkg_len);
+ pHeader->cmd = STORAGE_PROTO_CMD_TRUNCATE_FILE;
+ pHeader->status = 0;
+
+ if ((result=tcpsenddata_nb(pStorageServer->sock, out_buff, \
+ p - out_buff, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to storage server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pStorageServer->ip_addr, pStorageServer->port, \
+ result, STRERROR(result));
+ break;
+ }
+
+ if ((result=fdfs_recv_header(pStorageServer, &in_bytes)) != 0)
+ {
+ break;
+ }
+
+ if (in_bytes != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "storage server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid, " \
+ "should == 0", __LINE__, pStorageServer->ip_addr, \
+ pStorageServer->port, in_bytes);
+ result = EINVAL;
+ break;
+ }
+ } while (0);
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(pStorageServer, result != 0);
+ }
+
+ return result;
+}
+
diff --git a/client/storage_client.h b/client/storage_client.h
new file mode 100644
index 0000000..12489a9
--- /dev/null
+++ b/client/storage_client.h
@@ -0,0 +1,571 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#ifndef STORAGE_CLIENT_H
+#define STORAGE_CLIENT_H
+
+#include "tracker_types.h"
+#include "client_func.h"
+
+#define FDFS_DOWNLOAD_TO_BUFF 1
+#define FDFS_DOWNLOAD_TO_FILE 2
+#define FDFS_DOWNLOAD_TO_CALLBACK 3
+
+#define FDFS_UPLOAD_BY_BUFF 1
+#define FDFS_UPLOAD_BY_FILE 2
+#define FDFS_UPLOAD_BY_CALLBACK 3
+
+#define FDFS_FILE_ID_SEPERATOR '/'
+#define FDFS_FILE_ID_SEPERATE_STR "/"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define storage_upload_by_filename(pTrackerServer, \
+ pStorageServer, store_path_index, local_filename, \
+ file_ext_name, meta_list, meta_count, group_name, \
+ remote_filename) \
+ storage_upload_by_filename_ex(pTrackerServer, \
+ pStorageServer, store_path_index, \
+ STORAGE_PROTO_CMD_UPLOAD_FILE, local_filename, \
+ file_ext_name, meta_list, meta_count, group_name, \
+ remote_filename)
+
+#define storage_upload_appender_by_filename(pTrackerServer, \
+ pStorageServer, store_path_index, local_filename, \
+ file_ext_name, meta_list, meta_count, group_name, \
+ remote_filename) \
+ storage_upload_by_filename_ex(pTrackerServer, \
+ pStorageServer, store_path_index, \
+ STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, local_filename, \
+ file_ext_name, meta_list, meta_count, group_name, \
+ remote_filename)
+
+/**
+* upload file to storage server (by file name)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* store_path_index: the index of path on the storage server
+* local_filename: local filename to upload
+* cmd: the protocol command
+* file_ext_name: file ext name, not include dot(.),
+* if be NULL will abstract ext name from the local filename
+* meta_list: meta info array
+* meta_count: meta item count
+* group_name: if not empty, specify the group name.
+ return the group name to store the file
+* remote_filename: return the new created filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_upload_by_filename_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, const char *local_filename, \
+ const char *file_ext_name, const FDFSMetaData *meta_list, \
+ const int meta_count, char *group_name, char *remote_filename);
+
+/**
+* upload file to storage server (by file buff)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* store_path_index: the index of path on the storage server
+* file_buff: file content/buff
+* file_size: file size (bytes)
+* file_ext_name: file ext name, not include dot(.), can be NULL
+* meta_list: meta info array
+* meta_count: meta item count
+* group_name: if not empty, specify the group name.
+ return the group name to store the file
+* remote_filename: return the new created filename
+* return: 0 success, !=0 fail, return the error code
+**/
+#define storage_upload_by_filebuff(pTrackerServer, pStorageServer, \
+ store_path_index, file_buff, \
+ file_size, file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename) \
+ storage_do_upload_file(pTrackerServer, pStorageServer, \
+ store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
+ FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
+ file_size, NULL, NULL, file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename)
+
+#define storage_upload_appender_by_filebuff(pTrackerServer, pStorageServer, \
+ store_path_index, file_buff, \
+ file_size, file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename) \
+ storage_do_upload_file(pTrackerServer, pStorageServer, \
+ store_path_index, STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, \
+ FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
+ file_size, NULL, NULL, file_ext_name, meta_list, meta_count, \
+ group_name, remote_filename)
+
+/**
+* Upload file callback function prototype
+* params:
+* arg: callback extra arguement
+* sock: connected storage socket for sending file content
+* return: 0 success, !=0 fail, should return the error code
+**/
+typedef int (*UploadCallback) (void *arg, const int64_t file_size, int sock);
+
+/**
+* upload file to storage server (by callback)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* store_path_index: the index of path on the storage server
+* callback: callback function to send file content to storage server
+* arg: callback extra arguement
+* file_size: the file size
+* file_ext_name: file ext name, not include dot(.), can be NULL
+* meta_list: meta info array
+* meta_count: meta item count
+* group_name: if not empty, specify the group name.
+ return the group name to store the file
+* remote_filename: return the new created filename
+* return: 0 success, !=0 fail, return the error code
+**/
+#define storage_upload_by_callback(pTrackerServer, pStorageServer, \
+ store_path_index, callback, arg, file_size, file_ext_name, \
+ meta_list, meta_count, group_name, remote_filename) \
+ storage_upload_by_callback_ex(pTrackerServer, pStorageServer, \
+ store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
+ callback, arg, file_size, file_ext_name, meta_list, \
+ meta_count, group_name, remote_filename)
+
+#define storage_upload_appender_by_callback(pTrackerServer, pStorageServer, \
+ store_path_index, callback, arg, file_size, file_ext_name, \
+ meta_list, meta_count, group_name, remote_filename) \
+ storage_upload_by_callback_ex(pTrackerServer, pStorageServer, \
+ store_path_index, STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, \
+ callback, arg, file_size, file_ext_name, meta_list, \
+ meta_count, group_name, remote_filename)
+
+int storage_upload_by_callback_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, UploadCallback callback, void *arg, \
+ const int64_t file_size, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *group_name, char *remote_filename);
+
+int storage_do_upload_file(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, const int upload_type, const char *file_buff, \
+ void *arg, const int64_t file_size, const char *master_filename, \
+ const char *prefix_name, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *group_name, char *remote_filename);
+
+/**
+* delete file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* group_name: the group name of storage server
+* filename: filename on storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_delete_file(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *filename);
+
+/**
+* download file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* group_name: the group name of storage server
+* remote_filename: filename on storage server
+* file_buff: return file content/buff, must be freed
+* file_size: return file size (bytes)
+* return: 0 success, !=0 fail, return the error code
+**/
+#define storage_download_file(pTrackerServer, pStorageServer, group_name, \
+ remote_filename, file_buff, file_size) \
+ storage_do_download_file_ex(pTrackerServer, pStorageServer, \
+ FDFS_DOWNLOAD_TO_BUFF, group_name, remote_filename, \
+ 0, 0, file_buff, NULL, file_size)
+
+#define storage_download_file_to_buff(pTrackerServer, pStorageServer, \
+ group_name, remote_filename, file_buff, file_size) \
+ storage_do_download_file_ex(pTrackerServer, pStorageServer, \
+ FDFS_DOWNLOAD_TO_BUFF, group_name, remote_filename, \
+ 0, 0, file_buff, NULL, file_size)
+
+#define storage_do_download_file(pTrackerServer, pStorageServer, \
+ download_type, group_name, remote_filename, \
+ file_buff, arg, file_size) \
+ storage_do_download_file_ex(pTrackerServer, pStorageServer, \
+ download_type, group_name, remote_filename, \
+ 0, 0, file_buff, arg, file_size);
+
+/**
+* download file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* download_type: FDFS_DOWNLOAD_TO_BUFF or FDFS_DOWNLOAD_TO_FILE
+* or FDFS_DOWNLOAD_TO_CALLBACK
+* group_name: the group name of storage server
+* remote_filename: filename on storage server
+* file_offset: the start offset to download
+* download_bytes: download bytes, 0 means from start offset to the file end
+* file_buff: return file content/buff, must be freed
+* arg: additional argument for callback(valid only when download_tyee
+* is FDFS_DOWNLOAD_TO_CALLBACK), can be NULL
+* file_size: return file size (bytes)
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_do_download_file_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const int download_type, \
+ const char *group_name, const char *remote_filename, \
+ const int64_t file_offset, const int64_t download_bytes, \
+ char **file_buff, void *arg, int64_t *file_size);
+
+/**
+* download file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* group_name: the group name of storage server
+* remote_filename: filename on storage server
+* local_filename: local filename to write
+* file_size: return file size (bytes)
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_download_file_to_file(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *remote_filename, \
+ const char *local_filename, int64_t *file_size);
+
+/**
+* Download file callback function prototype
+* params:
+* arg: callback extra arguement
+* file_size: file size
+* data: temp buff, should not keep persistently
+* current_size: current data size
+* return: 0 success, !=0 fail, should return the error code
+**/
+typedef int (*DownloadCallback) (void *arg, const int64_t file_size, \
+ const char *data, const int current_size);
+
+/**
+* download file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* group_name: the group name of storage server
+* remote_filename: filename on storage server
+* file_offset: the start offset to download
+* download_bytes: download bytes, 0 means from start offset to the file end
+* callback: callback function
+* arg: callback extra arguement
+* file_size: return file size (bytes)
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_download_file_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *remote_filename, \
+ const int64_t file_offset, const int64_t download_bytes, \
+ DownloadCallback callback, void *arg, int64_t *file_size);
+
+/**
+* set metadata items to storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* group_name: the group name of storage server
+* filename: filename on storage server
+* meta_list: meta item array
+* meta_count: meta item count
+* op_flag:
+* # STORAGE_SET_METADATA_FLAG_OVERWRITE('O'): overwrite all old
+* metadata items
+* # STORAGE_SET_METADATA_FLAG_MERGE ('M'): merge, insert when
+* the metadata item not exist, otherwise update it
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_set_metadata(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *filename, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ const char op_flag);
+
+/**
+* get all metadata items from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* group_name: the group name of storage server
+* filename: filename on storage server
+* meta_list: return meta info array, must be freed
+* meta_count: return meta item count
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_get_metadata(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *filename, \
+ FDFSMetaData **meta_list, \
+ int *meta_count);
+
+/**
+* upload slave file to storage server (by file name)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* local_filename: local filename to upload
+* master_filename: the mater filename to generate the slave file id
+* prefix_name: the prefix name to generate the slave file id
+* file_ext_name: file ext name, not include dot(.),
+* if be NULL will abstract ext name from the local filename
+* meta_list: meta info array
+* meta_count: meta item count
+* group_name: specify the group name.
+ return the group name to store the file
+* remote_filename: return the new created filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_upload_slave_by_filename(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const char *master_filename, const char *prefix_name, \
+ const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *group_name, char *remote_filename);
+
+/**
+* upload slave file to storage server (by file buff)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_buff: file content/buff
+* file_size: file size (bytes)
+* master_filename: the mater filename to generate the slave file id
+* prefix_name: the prefix name to generate the slave file id
+* file_ext_name: file ext name, not include dot(.), can be NULL
+* meta_list: meta info array
+* meta_count: meta item count
+* group_name: specify the group name.
+ return the group name to store the file
+* remote_filename: return the new created filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_upload_slave_by_filebuff(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_size, const char *master_filename, \
+ const char *prefix_name, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *group_name, char *remote_filename);
+
+/**
+* upload slave file to storage server (by callback)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* callback: callback function to send file content to storage server
+* arg: callback extra arguement
+* file_size: the file size
+* master_filename: the mater filename to generate the slave file id
+* prefix_name: the prefix name to generate the slave file id
+* file_ext_name: file ext name, not include dot(.), can be NULL
+* meta_list: meta info array
+* meta_count: meta item count
+* group_name: specify the group name.
+ return the group name to store the file
+* remote_filename: return the new created filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_upload_slave_by_callback(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, \
+ const int64_t file_size, const char *master_filename, \
+ const char *prefix_name, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *group_name, char *remote_filename);
+
+
+/**
+* append file to storage server (by local filename)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* local_filename: local filename to upload
+* group_name: the group name
+* appender_filename: the appender filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_append_by_filename(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const char *group_name, const char *appender_filename);
+
+
+/**
+* append file to storage server (by callback)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* callback: callback function to send file content to storage server
+* arg: callback extra arguement
+* file_size: the file size
+* group_name: the group name
+* appender_filename: the appender filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_append_by_callback(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, const int64_t file_size, \
+ const char *group_name, const char *appender_filename);
+
+
+/**
+* append file to storage server (by file buff)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_buff: file content/buff
+* file_size: file size (bytes)
+* group_name: the group name
+* appender_filename: the appender filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_append_by_filebuff(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_size, const char *group_name, \
+ const char *appender_filename);
+
+
+/**
+* modify file to storage server (by local filename)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* local_filename: local filename to upload
+* file_offset: the start offset to modify appender file
+* group_name: the group name
+* appender_filename: the appender filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_modify_by_filename(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const int64_t file_offset, const char *group_name, \
+ const char *appender_filename);
+
+
+/**
+* modify file to storage server (by callback)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* callback: callback function to send file content to storage server
+* arg: callback extra arguement
+* file_offset: the start offset to modify appender file
+* file_size: the file size
+* group_name: the group name
+* appender_filename: the appender filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_modify_by_callback(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, \
+ const int64_t file_offset, const int64_t file_size, \
+ const char *group_name, const char *appender_filename);
+
+
+/**
+* modify file to storage server (by file buff)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_buff: file content/buff
+* file_offset: the start offset to modify appender file
+* file_size: file size (bytes)
+* group_name: the group name
+* appender_filename: the appender filename
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_modify_by_filebuff(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_offset, const int64_t file_size, \
+ const char *group_name, const char *appender_filename);
+
+
+/**
+* truncate file to sepecify size
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* group_name: the group name
+* appender_filename: the appender filename
+* truncated_file_size: truncated file size
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_truncate_file(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer,
+ const char *group_name, const char *appender_filename, \
+ const int64_t truncated_file_size);
+
+
+#define storage_query_file_info(pTrackerServer, pStorageServer, \
+ group_name, filename, pFileInfo) \
+ storage_query_file_info_ex(pTrackerServer, pStorageServer, \
+ group_name, filename, pFileInfo, false)
+
+/**
+* query file info
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* group_name: the group name of storage server
+* filename: filename on storage server
+* pFileInfo: return the file info (file size and create timestamp)
+* bSilence: when this file not exist, do not log error on storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_query_file_info_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *filename, \
+ FDFSFileInfo *pFileInfo, const bool bSilence);
+
+
+#define fdfs_get_file_info(group_name, remote_filename, pFileInfo) \
+ fdfs_get_file_info_ex(group_name, remote_filename, true, pFileInfo)
+
+/**
+* check if file exist
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* group_name: the group name of storage server
+* remote_filename: filename on storage server
+* return: 0 file exist, !=0 not exist, return the error code
+**/
+int storage_file_exist(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *group_name, const char *remote_filename);
+/**
+* get file info from the filename return by storage server
+* params:
+* group_name: the group name of storage server
+* remote_filename: filename on storage server
+* get_from_server: if get slave file info from storage server
+* pFileInfo: return the file info
+* return: 0 success, !=0 fail, return the error code
+**/
+int fdfs_get_file_info_ex(const char *group_name, const char *remote_filename, \
+ const bool get_from_server, FDFSFileInfo *pFileInfo);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/client/storage_client1.h b/client/storage_client1.h
new file mode 100644
index 0000000..63b5e4c
--- /dev/null
+++ b/client/storage_client1.h
@@ -0,0 +1,532 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#ifndef STORAGE_CLIENT1_H
+#define STORAGE_CLIENT1_H
+
+#include "tracker_types.h"
+#include "storage_client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+* upload file to storage server (by file name)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* store_path_index: the index of path on the storage server
+* local_filename: local filename to upload
+* file_ext_name: file ext name, not include dot(.),
+* if be NULL will abstract ext name from the local filename
+* meta_list: meta info array
+* meta_count: meta item count
+* group_name: specify the group name to upload file to, can be NULL or emtpy
+* file_id: return the new created file id (including group name and filename)
+* return: 0 success, !=0 fail, return the error code
+**/
+#define storage_upload_by_filename1(pTrackerServer, pStorageServer, \
+ store_path_index, local_filename, file_ext_name, \
+ meta_list, meta_count, group_name, file_id) \
+ storage_upload_by_filename1_ex(pTrackerServer, pStorageServer, \
+ store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
+ local_filename, file_ext_name, meta_list, meta_count, \
+ group_name, file_id)
+
+#define storage_upload_appender_by_filename1(pTrackerServer, pStorageServer, \
+ store_path_index, local_filename, file_ext_name, \
+ meta_list, meta_count, group_name, file_id) \
+ storage_upload_by_filename1_ex(pTrackerServer, pStorageServer, \
+ store_path_index, STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, \
+ local_filename, file_ext_name, meta_list, meta_count, \
+ group_name, file_id)
+
+int storage_upload_by_filename1_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, const char *local_filename, \
+ const char *file_ext_name, const FDFSMetaData *meta_list, \
+ const int meta_count, const char *group_name, char *file_id);
+
+/**
+* upload file to storage server (by file buff)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* store_path_index: the index of path on the storage server
+* file_buff: file content/buff
+* file_size: file size (bytes)
+* file_ext_name: file ext name, not include dot(.), can be NULL
+* meta_list: meta info array
+* meta_count: meta item count
+* group_name: specify the group name to upload file to, can be NULL or emtpy
+* file_id: return the new created file id (including group name and filename)
+* return: 0 success, !=0 fail, return the error code
+**/
+#define storage_upload_by_filebuff1(pTrackerServer, pStorageServer, \
+ store_path_index, file_buff, file_size, file_ext_name, \
+ meta_list, meta_count, group_name, file_id) \
+ storage_do_upload_file1(pTrackerServer, pStorageServer, \
+ store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
+ FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
+ file_size, file_ext_name, meta_list, meta_count, \
+ group_name, file_id)
+
+#define storage_upload_appender_by_filebuff1(pTrackerServer, pStorageServer, \
+ store_path_index, file_buff, file_size, file_ext_name, \
+ meta_list, meta_count, group_name, file_id) \
+ storage_do_upload_file1(pTrackerServer, pStorageServer, \
+ store_path_index, STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, \
+ FDFS_UPLOAD_BY_BUFF, file_buff, NULL, \
+ file_size, file_ext_name, meta_list, meta_count, \
+ group_name, file_id)
+
+int storage_do_upload_file1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, const int upload_type, \
+ const char *file_buff, void *arg, const int64_t file_size, \
+ const char *file_ext_name, const FDFSMetaData *meta_list, \
+ const int meta_count, const char *group_name, char *file_id);
+
+/**
+* upload file to storage server (by callback)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* store_path_index: the index of path on the storage server
+* file_size: the file size
+* file_ext_name: file ext name, not include dot(.), can be NULL
+* callback: callback function to send file content to storage server
+* arg: callback extra arguement
+* meta_list: meta info array
+* meta_count: meta item count
+* group_name: specify the group name to upload file to, can be NULL or emtpy
+* file_id: return the new created file id (including group name and filename)
+* return: 0 success, !=0 fail, return the error code
+**/
+#define storage_upload_by_callback1(pTrackerServer, pStorageServer, \
+ store_path_index, callback, arg, \
+ file_size, file_ext_name, meta_list, meta_count, \
+ group_name, file_id) \
+ storage_upload_by_callback1_ex(pTrackerServer, pStorageServer, \
+ store_path_index, STORAGE_PROTO_CMD_UPLOAD_FILE, \
+ callback, arg, file_size, file_ext_name, meta_list, \
+ meta_count, group_name, file_id)
+
+#define storage_upload_appender_by_callback1(pTrackerServer, pStorageServer, \
+ store_path_index, callback, arg, \
+ file_size, file_ext_name, meta_list, meta_count, \
+ group_name, file_id) \
+ storage_upload_by_callback1_ex(pTrackerServer, pStorageServer, \
+ store_path_index, STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, \
+ callback, arg, file_size, file_ext_name, meta_list, \
+ meta_count, group_name, file_id)
+
+int storage_upload_by_callback1_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int store_path_index, \
+ const char cmd, UploadCallback callback, void *arg, \
+ const int64_t file_size, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ const char *group_name, char *file_id);
+
+/**
+* delete file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_id: the file id to deleted (including group name and filename)
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_delete_file1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id);
+
+/**
+* delete file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* appender_file_id: the appender file id
+* truncated_file_size: the truncated file size
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_truncate_file1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer,
+ const char *appender_file_id, \
+ const int64_t truncated_file_size);
+
+/**
+* set metadata items to storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_id: the file id (including group name and filename)
+* meta_list: meta item array
+* meta_count: meta item count
+* op_flag:
+* # STORAGE_SET_METADATA_FLAG_OVERWRITE('O'): overwrite all old
+* metadata items
+* # STORAGE_SET_METADATA_FLAG_MERGE ('M'): merge, insert when
+* the metadata item not exist, otherwise update it
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_set_metadata1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ const char op_flag);
+
+/**
+* download file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_id: the file id (including group name and filename)
+* file_buff: return file content/buff, must be freed
+* file_size: return file size (bytes)
+* return: 0 success, !=0 fail, return the error code
+**/
+#define storage_download_file1(pTrackerServer, pStorageServer, file_id, \
+ file_buff, file_size) \
+ storage_do_download_file1_ex(pTrackerServer, pStorageServer, \
+ FDFS_DOWNLOAD_TO_BUFF, file_id, 0, 0, \
+ file_buff, NULL, file_size)
+
+#define storage_download_file_to_buff1(pTrackerServer, pStorageServer, \
+ file_id, file_buff, file_size) \
+ storage_do_download_file1_ex(pTrackerServer, pStorageServer, \
+ FDFS_DOWNLOAD_TO_BUFF, file_id, 0, 0, \
+ file_buff, NULL, file_size)
+
+#define storage_do_download_file1(pTrackerServer, pStorageServer, \
+ download_type, file_id, file_buff, file_size) \
+ storage_do_download_file1_ex(pTrackerServer, pStorageServer, \
+ download_type, file_id, \
+ 0, 0, file_buff, NULL, file_size)
+
+/**
+* download file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_id: the file id (including group name and filename)
+* file_offset: the start offset to download
+* download_bytes: download bytes, 0 means from start offset to the file end
+* file_buff: return file content/buff, must be freed
+* file_size: return file size (bytes)
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_do_download_file1_ex(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const int download_type, const char *file_id, \
+ const int64_t file_offset, const int64_t download_bytes, \
+ char **file_buff, void *arg, int64_t *file_size);
+
+/**
+* download file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_id: the file id (including group name and filename)
+* local_filename: local filename to write
+* file_size: return file size (bytes)
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_download_file_to_file1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id, \
+ const char *local_filename, int64_t *file_size);
+
+/**
+* get all metadata items from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_id: the file id (including group name and filename)
+* meta_list: return meta info array, must be freed
+* meta_count: return meta item count
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_get_metadata1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id, \
+ FDFSMetaData **meta_list, int *meta_count);
+
+
+/**
+* download file from storage server
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_id: the file id (including group name and filename)
+* file_offset: the start offset to download
+* download_bytes: download bytes, 0 means from start offset to the file end
+* callback: callback function
+* arg: callback extra arguement
+* file_size: return file size (bytes)
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_download_file_ex1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id, \
+ const int64_t file_offset, const int64_t download_bytes, \
+ DownloadCallback callback, void *arg, int64_t *file_size);
+
+/**
+* query storage server to download file
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: return storage server
+* file_id: the file id (including group name and filename)
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_query_storage_fetch1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id);
+
+/**
+* query storage server to update (delete file and set metadata)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: return storage server
+* file_id: the file id (including group name and filename)
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_query_storage_update1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id);
+
+/**
+* query storage server list to fetch file
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: return storage server
+* nMaxServerCount: max storage server count
+* server_count: return storage server count
+* group_name: the group name of storage server
+* filename: filename on storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_query_storage_list1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int nMaxServerCount, \
+ int *server_count, const char *file_id);
+
+/**
+* upload slave file to storage server (by file name)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* local_filename: local filename to upload
+* master_file_id: the mater file id to generate the slave file id
+* prefix_name: the prefix name to generate the file id
+* file_ext_name: file ext name, not include dot(.),
+* if be NULL will abstract ext name from the local filename
+* meta_list: meta info array
+* meta_count: meta item count
+* file_id: return the slave file id
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_upload_slave_by_filename1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const char *master_file_id, const char *prefix_name, \
+ const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *file_id);
+
+/**
+* upload slave file to storage server (by file buff)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_buff: file content/buff
+* file_size: file size (bytes)
+* master_file_id: the mater file id to generate the slave file id
+* prefix_name: the prefix name to generate the file id
+* file_ext_name: file ext name, not include dot(.), can be NULL
+* meta_list: meta info array
+* meta_count: meta item count
+* file_id: return the slave file id
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_upload_slave_by_filebuff1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_size, const char *master_file_id, \
+ const char *prefix_name, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *file_id);
+
+/**
+* upload slave file to storage server (by callback)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* callback: callback function to send file content to storage server
+* arg: callback extra arguement
+* file_size: the file size
+* master_file_id: the mater file id to generate the slave file id
+* prefix_name: the prefix name to generate the file id
+* file_ext_name: file ext name, not include dot(.), can be NULL
+* meta_list: meta info array
+* meta_count: meta item count
+* file_id: return the slave file id
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_upload_slave_by_callback1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, \
+ const int64_t file_size, const char *master_file_id, \
+ const char *prefix_name, const char *file_ext_name, \
+ const FDFSMetaData *meta_list, const int meta_count, \
+ char *file_id);
+
+/**
+* append file to storage server (by filename)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* local_filename: local filename to upload
+* appender_file_id: the appender file id
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_append_by_filename1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const char *appender_file_id);
+
+
+/**
+* append file to storage server (by file buff)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_buff: file content/buff
+* file_size: file size (bytes)
+* appender_file_id: the appender file id
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_append_by_filebuff1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_size, const char *appender_file_id);
+
+
+/**
+* append file to storage server (by callback)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* callback: callback function to send file content to storage server
+* arg: callback extra arguement
+* file_size: the file size
+* appender_file_id: the appender file id
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_append_by_callback1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, \
+ const int64_t file_size, const char *appender_file_id);
+
+/**
+* modify file to storage server (by local filename)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* local_filename: local filename to upload
+* file_offset: the start offset to modify appender file
+* appender_file_id: the appender file id
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_modify_by_filename1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *local_filename,\
+ const int64_t file_offset, const char *appender_file_id);
+
+
+/**
+* modify file to storage server (by callback)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* callback: callback function to send file content to storage server
+* arg: callback extra arguement
+* file_offset: the start offset to modify appender file
+* file_size: the file size
+* appender_file_id: the appender file id
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_modify_by_callback1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ UploadCallback callback, void *arg, \
+ const int64_t file_offset, const int64_t file_size, \
+ const char *appender_file_id);
+
+
+/**
+* modify file to storage server (by file buff)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_buff: file content/buff
+* file_offset: the start offset to modify appender file
+* file_size: file size (bytes)
+* appender_file_id: the appender file id
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_modify_by_filebuff1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_buff, \
+ const int64_t file_offset, const int64_t file_size, \
+ const char *appender_file_id);
+
+
+#define storage_query_file_info1(pTrackerServer, pStorageServer, file_id, \
+ pFileInfo) \
+ storage_query_file_info_ex1(pTrackerServer, pStorageServer, file_id, \
+ pFileInfo, false)
+
+/**
+* query file info
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_id: the file id
+* pFileInfo: return the file info (file size and create timestamp)
+* bSilence: when this file not exist, do not log error on storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+int storage_query_file_info_ex1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const char *file_id, \
+ FDFSFileInfo *pFileInfo, const bool bSilence);
+
+#define fdfs_get_file_info1(file_id, pFileInfo) \
+ fdfs_get_file_info_ex1(file_id, true, pFileInfo)
+
+/**
+* get file info from the filename return by storage server
+* params:
+* file_id: the file id return by storage server
+* get_from_server: if get slave file info from storage server
+* pFileInfo: return the file info
+* return: 0 success, !=0 fail, return the error code
+**/
+int fdfs_get_file_info_ex1(const char *file_id, const bool get_from_server, \
+ FDFSFileInfo *pFileInfo);
+
+/**
+* check if file exist
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: storage server
+* file_id: the file id return by storage server
+* return: 0 file exist, !=0 not exist, return the error code
+**/
+int storage_file_exist1(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, \
+ const char *file_id);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/client/test/Makefile b/client/test/Makefile
new file mode 100644
index 0000000..423d643
--- /dev/null
+++ b/client/test/Makefile
@@ -0,0 +1,23 @@
+.SUFFIXES: .c .o
+
+COMPILE = $(CC) -Wall -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -g -O -DDEBUG_FLAG -DOS_LINUX -DIOEVENT_USE_EPOLL
+INC_PATH = -I/usr/local/include/fastcommon -I/usr/local/include/fastdfs
+LIB_PATH = -L/usr/local/lib -lfastcommon -lfdfsclient -lpthread -ldl -rdynamic
+TARGET_PATH = $(TARGET_PATH)
+
+ALL_OBJS =
+
+ALL_PRGS = fdfs_monitor fdfs_test fdfs_test1
+
+all: $(ALL_OBJS) $(ALL_PRGS)
+.o:
+ $(COMPILE) -o $@ $< $(SHARED_OBJS) $(LIB_PATH) $(INC_PATH)
+.c:
+ $(COMPILE) -o $@ $< $(ALL_OBJS) $(LIB_PATH) $(INC_PATH)
+.c.o:
+ $(COMPILE) -c -o $@ $< $(INC_PATH)
+install:
+ cp -f $(ALL_PRGS) $(TARGET_PATH)
+clean:
+ rm -f $(ALL_OBJS) $(ALL_PRGS)
+
diff --git a/client/test/Makefile.in b/client/test/Makefile.in
new file mode 100644
index 0000000..831e7b0
--- /dev/null
+++ b/client/test/Makefile.in
@@ -0,0 +1,23 @@
+.SUFFIXES: .c .o
+
+COMPILE = $(CC) $(CFLAGS)
+INC_PATH = -I/usr/local/include/fastcommon -I/usr/local/include/fastdfs
+LIB_PATH = -L/usr/local/lib -lfastcommon -lfdfsclient $(LIBS)
+TARGET_PATH = $(TARGET_PATH)
+
+ALL_OBJS =
+
+ALL_PRGS = fdfs_monitor fdfs_test fdfs_test1
+
+all: $(ALL_OBJS) $(ALL_PRGS)
+.o:
+ $(COMPILE) -o $@ $< $(SHARED_OBJS) $(LIB_PATH) $(INC_PATH)
+.c:
+ $(COMPILE) -o $@ $< $(ALL_OBJS) $(LIB_PATH) $(INC_PATH)
+.c.o:
+ $(COMPILE) -c -o $@ $< $(INC_PATH)
+install:
+ cp -f $(ALL_PRGS) $(TARGET_PATH)
+clean:
+ rm -f $(ALL_OBJS) $(ALL_PRGS)
+
diff --git a/client/test/fdfs_monitor.c b/client/test/fdfs_monitor.c
new file mode 120000
index 0000000..9f42ff4
--- /dev/null
+++ b/client/test/fdfs_monitor.c
@@ -0,0 +1 @@
+../fdfs_monitor.c
\ No newline at end of file
diff --git a/client/test/fdfs_test.c b/client/test/fdfs_test.c
new file mode 120000
index 0000000..c3a3f16
--- /dev/null
+++ b/client/test/fdfs_test.c
@@ -0,0 +1 @@
+../fdfs_test.c
\ No newline at end of file
diff --git a/client/test/fdfs_test1.c b/client/test/fdfs_test1.c
new file mode 120000
index 0000000..c87e07d
--- /dev/null
+++ b/client/test/fdfs_test1.c
@@ -0,0 +1 @@
+../fdfs_test1.c
\ No newline at end of file
diff --git a/client/tracker_client.c b/client/tracker_client.c
new file mode 100644
index 0000000..55449fc
--- /dev/null
+++ b/client/tracker_client.c
@@ -0,0 +1,1529 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "fdfs_define.h"
+#include "logger.h"
+#include "shared_func.h"
+#include "fdfs_global.h"
+#include "sockopt.h"
+#include "tracker_types.h"
+#include "tracker_proto.h"
+#include "tracker_client.h"
+#include "client_global.h"
+
+int tracker_get_all_connections_ex(TrackerServerGroup *pTrackerGroup)
+{
+ ConnectionInfo *pServer;
+ ConnectionInfo *pEnd;
+ int success_count;
+
+ success_count = 0;
+ pEnd = pTrackerGroup->servers + pTrackerGroup->server_count;
+ for (pServer=pTrackerGroup->servers; pServersock >= 0)
+ {
+ success_count++;
+ }
+ else if (conn_pool_connect_server(pServer, \
+ g_fdfs_connect_timeout) == 0)
+ {
+ fdfs_active_test(pServer);
+ success_count++;
+ }
+ }
+
+ return success_count > 0 ? 0 : ENOTCONN;
+}
+
+void tracker_close_all_connections_ex(TrackerServerGroup *pTrackerGroup)
+{
+ ConnectionInfo *pServer;
+ ConnectionInfo *pEnd;
+
+ pEnd = pTrackerGroup->servers + pTrackerGroup->server_count;
+ for (pServer=pTrackerGroup->servers; pServerserver_index;
+ if (server_index >= pTrackerGroup->server_count)
+ {
+ server_index = 0;
+ }
+
+ do
+ {
+ pCurrentServer = pTrackerGroup->servers + server_index;
+ if ((conn=tracker_connect_server(pCurrentServer, &result)) != NULL)
+ {
+ break;
+ }
+
+ pEnd = pTrackerGroup->servers + pTrackerGroup->server_count;
+ for (pServer=pCurrentServer+1; pServerserver_index = pServer - \
+ pTrackerGroup->servers;
+ break;
+ }
+ }
+
+ if (conn != NULL)
+ {
+ break;
+ }
+
+ for (pServer=pTrackerGroup->servers; pServerserver_index = pServer - \
+ pTrackerGroup->servers;
+ break;
+ }
+ }
+ } while (0);
+
+ pTrackerGroup->server_index++;
+ if (pTrackerGroup->server_index >= pTrackerGroup->server_count)
+ {
+ pTrackerGroup->server_index = 0;
+ }
+
+ return conn;
+}
+
+ConnectionInfo *tracker_get_connection_no_pool(TrackerServerGroup *pTrackerGroup)
+{
+ ConnectionInfo *pCurrentServer;
+ ConnectionInfo *pServer;
+ ConnectionInfo *pEnd;
+ ConnectionInfo *conn;
+ int server_index;
+ int result;
+
+ server_index = pTrackerGroup->server_index;
+ if (server_index >= pTrackerGroup->server_count)
+ {
+ server_index = 0;
+ }
+
+ conn = NULL;
+ do
+ {
+ pCurrentServer = pTrackerGroup->servers + server_index;
+ if ((result=tracker_connect_server_no_pool(pCurrentServer)) == 0)
+ {
+ conn = pCurrentServer;
+ break;
+ }
+
+ pEnd = pTrackerGroup->servers + pTrackerGroup->server_count;
+ for (pServer=pCurrentServer+1; pServerserver_index = pServer - \
+ pTrackerGroup->servers;
+ break;
+ }
+ }
+
+ if (conn != NULL)
+ {
+ break;
+ }
+
+ for (pServer=pTrackerGroup->servers; pServerserver_index = pServer - \
+ pTrackerGroup->servers;
+ break;
+ }
+ }
+ } while (0);
+
+ pTrackerGroup->server_index++;
+ if (pTrackerGroup->server_index >= pTrackerGroup->server_count)
+ {
+ pTrackerGroup->server_index = 0;
+ }
+
+ return conn;
+}
+
+ConnectionInfo *tracker_get_connection_r_ex(TrackerServerGroup *pTrackerGroup, \
+ ConnectionInfo *pTrackerServer, int *err_no)
+{
+ ConnectionInfo *conn;
+ ConnectionInfo *pCurrentServer;
+ ConnectionInfo *pServer;
+ ConnectionInfo *pEnd;
+ int server_index;
+
+ server_index = pTrackerGroup->server_index;
+ if (server_index >= pTrackerGroup->server_count)
+ {
+ server_index = 0;
+ }
+
+ do
+ {
+ pCurrentServer = pTrackerGroup->servers + server_index;
+ memcpy(pTrackerServer, pCurrentServer, sizeof(ConnectionInfo));
+ pTrackerServer->sock = -1;
+ if ((conn=tracker_connect_server(pTrackerServer, err_no)) != NULL)
+ {
+ break;
+ }
+
+ pEnd = pTrackerGroup->servers + pTrackerGroup->server_count;
+ for (pServer=pCurrentServer+1; pServersock = -1;
+ if ((conn=tracker_connect_server(pTrackerServer, err_no)) != NULL)
+ {
+ pTrackerGroup->server_index = pServer - \
+ pTrackerGroup->servers;
+ break;
+ }
+ }
+
+ if (conn != NULL)
+ {
+ break;
+ }
+
+ for (pServer=pTrackerGroup->servers; pServersock = -1;
+ if ((conn=tracker_connect_server(pTrackerServer, err_no)) != NULL)
+ {
+ pTrackerGroup->server_index = pServer - \
+ pTrackerGroup->servers;
+ break;
+ }
+ }
+ } while (0);
+
+ pTrackerGroup->server_index++;
+ if (pTrackerGroup->server_index >= pTrackerGroup->server_count)
+ {
+ pTrackerGroup->server_index = 0;
+ }
+
+ return conn;
+}
+
+int tracker_list_servers(ConnectionInfo *pTrackerServer, \
+ const char *szGroupName, const char *szStorageId, \
+ FDFSStorageInfo *storage_infos, const int max_storages, \
+ int *storage_count)
+{
+ char out_buff[sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + \
+ IP_ADDRESS_SIZE];
+ bool new_connection;
+ TrackerHeader *pHeader;
+ ConnectionInfo *conn;
+ int result;
+ int name_len;
+ int id_len;
+ TrackerStorageStat stats[FDFS_MAX_GROUPS];
+ char *pInBuff;
+ TrackerStorageStat *pSrc;
+ TrackerStorageStat *pEnd;
+ FDFSStorageStat *pStorageStat;
+ FDFSStorageInfo *pDest;
+ FDFSStorageStatBuff *pStatBuff;
+ int64_t in_bytes;
+
+ CHECK_CONNECTION(pTrackerServer, conn, result, new_connection);
+
+ memset(out_buff, 0, sizeof(out_buff));
+ pHeader = (TrackerHeader *)out_buff;
+ name_len = strlen(szGroupName);
+ if (name_len > FDFS_GROUP_NAME_MAX_LEN)
+ {
+ name_len = FDFS_GROUP_NAME_MAX_LEN;
+ }
+ memcpy(out_buff + sizeof(TrackerHeader), szGroupName, name_len);
+
+ if (szStorageId == NULL)
+ {
+ id_len = 0;
+ }
+ else
+ {
+ id_len = strlen(szStorageId);
+ if (id_len >= FDFS_STORAGE_ID_MAX_SIZE)
+ {
+ id_len = FDFS_STORAGE_ID_MAX_SIZE - 1;
+ }
+
+ memcpy(out_buff+sizeof(TrackerHeader)+FDFS_GROUP_NAME_MAX_LEN,\
+ szStorageId, id_len);
+ }
+
+ long2buff(FDFS_GROUP_NAME_MAX_LEN + id_len, pHeader->pkg_len);
+ pHeader->cmd = TRACKER_PROTO_CMD_SERVER_LIST_STORAGE;
+ if ((result=tcpsenddata_nb(conn->sock, out_buff, \
+ sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + id_len, \
+ g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ pInBuff = (char *)stats;
+ result = fdfs_recv_response(conn, &pInBuff, \
+ sizeof(stats), &in_bytes);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(conn, result != 0);
+ }
+
+ if (result != 0)
+ {
+ *storage_count = 0;
+ return result;
+ }
+
+ if (in_bytes % sizeof(TrackerStorageStat) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid", \
+ __LINE__, pTrackerServer->ip_addr, \
+ pTrackerServer->port, in_bytes);
+ *storage_count = 0;
+ return EINVAL;
+ }
+
+ *storage_count = in_bytes / sizeof(TrackerStorageStat);
+ if (*storage_count > max_storages)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d insufficent space, " \
+ "max storage count: %d, expect count: %d", \
+ __LINE__, pTrackerServer->ip_addr, \
+ pTrackerServer->port, max_storages, *storage_count);
+ *storage_count = 0;
+ return ENOSPC;
+ }
+
+ memset(storage_infos, 0, sizeof(FDFSStorageInfo) * max_storages);
+ pDest = storage_infos;
+ pEnd = stats + (*storage_count);
+ for (pSrc=stats; pSrcstat_buff);
+ pStorageStat = &(pDest->stat);
+
+ pDest->status = pSrc->status;
+ memcpy(pDest->id, pSrc->id, FDFS_STORAGE_ID_MAX_SIZE - 1);
+ memcpy(pDest->ip_addr, pSrc->ip_addr, IP_ADDRESS_SIZE - 1);
+ memcpy(pDest->src_id, pSrc->src_id, \
+ FDFS_STORAGE_ID_MAX_SIZE - 1);
+ strcpy(pDest->domain_name, pSrc->domain_name);
+ strcpy(pDest->version, pSrc->version);
+ pDest->join_time = buff2long(pSrc->sz_join_time);
+ pDest->up_time = buff2long(pSrc->sz_up_time);
+ pDest->total_mb = buff2long(pSrc->sz_total_mb);
+ pDest->free_mb = buff2long(pSrc->sz_free_mb);
+ pDest->upload_priority = buff2long(pSrc->sz_upload_priority);
+ pDest->store_path_count = buff2long(pSrc->sz_store_path_count);
+ pDest->subdir_count_per_path = buff2long( \
+ pSrc->sz_subdir_count_per_path);
+ pDest->storage_port = buff2long(pSrc->sz_storage_port);
+ pDest->storage_http_port = buff2long(pSrc->sz_storage_http_port);
+ pDest->current_write_path = buff2long( \
+ pSrc->sz_current_write_path);
+
+ pStorageStat->total_upload_count = buff2long( \
+ pStatBuff->sz_total_upload_count);
+ pStorageStat->success_upload_count = buff2long( \
+ pStatBuff->sz_success_upload_count);
+ pStorageStat->total_append_count = buff2long( \
+ pStatBuff->sz_total_append_count);
+ pStorageStat->success_append_count = buff2long( \
+ pStatBuff->sz_success_append_count);
+ pStorageStat->total_modify_count = buff2long( \
+ pStatBuff->sz_total_modify_count);
+ pStorageStat->success_modify_count = buff2long( \
+ pStatBuff->sz_success_modify_count);
+ pStorageStat->total_truncate_count = buff2long( \
+ pStatBuff->sz_total_truncate_count);
+ pStorageStat->success_truncate_count = buff2long( \
+ pStatBuff->sz_success_truncate_count);
+ pStorageStat->total_set_meta_count = buff2long( \
+ pStatBuff->sz_total_set_meta_count);
+ pStorageStat->success_set_meta_count = buff2long( \
+ pStatBuff->sz_success_set_meta_count);
+ pStorageStat->total_delete_count = buff2long( \
+ pStatBuff->sz_total_delete_count);
+ pStorageStat->success_delete_count = buff2long( \
+ pStatBuff->sz_success_delete_count);
+ pStorageStat->total_download_count = buff2long( \
+ pStatBuff->sz_total_download_count);
+ pStorageStat->success_download_count = buff2long( \
+ pStatBuff->sz_success_download_count);
+ pStorageStat->total_get_meta_count = buff2long( \
+ pStatBuff->sz_total_get_meta_count);
+ pStorageStat->success_get_meta_count = buff2long( \
+ pStatBuff->sz_success_get_meta_count);
+ pStorageStat->last_source_update = buff2long( \
+ pStatBuff->sz_last_source_update);
+ pStorageStat->last_sync_update = buff2long( \
+ pStatBuff->sz_last_sync_update);
+ pStorageStat->last_synced_timestamp = buff2long( \
+ pStatBuff->sz_last_synced_timestamp);
+ pStorageStat->total_create_link_count = buff2long( \
+ pStatBuff->sz_total_create_link_count);
+ pStorageStat->success_create_link_count = buff2long( \
+ pStatBuff->sz_success_create_link_count);
+ pStorageStat->total_delete_link_count = buff2long( \
+ pStatBuff->sz_total_delete_link_count);
+ pStorageStat->success_delete_link_count = buff2long( \
+ pStatBuff->sz_success_delete_link_count);
+ pStorageStat->total_upload_bytes = buff2long( \
+ pStatBuff->sz_total_upload_bytes);
+ pStorageStat->success_upload_bytes = buff2long( \
+ pStatBuff->sz_success_upload_bytes);
+ pStorageStat->total_append_bytes = buff2long( \
+ pStatBuff->sz_total_append_bytes);
+ pStorageStat->success_append_bytes = buff2long( \
+ pStatBuff->sz_success_append_bytes);
+ pStorageStat->total_modify_bytes = buff2long( \
+ pStatBuff->sz_total_modify_bytes);
+ pStorageStat->success_modify_bytes = buff2long( \
+ pStatBuff->sz_success_modify_bytes);
+ pStorageStat->total_download_bytes = buff2long( \
+ pStatBuff->sz_total_download_bytes);
+ pStorageStat->success_download_bytes = buff2long( \
+ pStatBuff->sz_success_download_bytes);
+ pStorageStat->total_sync_in_bytes = buff2long( \
+ pStatBuff->sz_total_sync_in_bytes);
+ pStorageStat->success_sync_in_bytes = buff2long( \
+ pStatBuff->sz_success_sync_in_bytes);
+ pStorageStat->total_sync_out_bytes = buff2long( \
+ pStatBuff->sz_total_sync_out_bytes);
+ pStorageStat->success_sync_out_bytes = buff2long( \
+ pStatBuff->sz_success_sync_out_bytes);
+ pStorageStat->total_file_open_count = buff2long( \
+ pStatBuff->sz_total_file_open_count);
+ pStorageStat->success_file_open_count = buff2long( \
+ pStatBuff->sz_success_file_open_count);
+ pStorageStat->total_file_read_count = buff2long( \
+ pStatBuff->sz_total_file_read_count);
+ pStorageStat->success_file_read_count = buff2long( \
+ pStatBuff->sz_success_file_read_count);
+ pStorageStat->total_file_write_count = buff2long( \
+ pStatBuff->sz_total_file_write_count);
+ pStorageStat->success_file_write_count = buff2long( \
+ pStatBuff->sz_success_file_write_count);
+ pStorageStat->last_heart_beat_time = buff2long( \
+ pStatBuff->sz_last_heart_beat_time);
+ pDest->if_trunk_server = pSrc->if_trunk_server;
+ pDest++;
+ }
+
+ return 0;
+}
+
+int tracker_list_one_group(ConnectionInfo *pTrackerServer, \
+ const char *group_name, FDFSGroupStat *pDest)
+{
+ TrackerHeader *pHeader;
+ ConnectionInfo *conn;
+ bool new_connection;
+ char out_buff[sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN];
+ TrackerGroupStat src;
+ char *pInBuff;
+ int result;
+ int64_t in_bytes;
+
+ CHECK_CONNECTION(pTrackerServer, conn, result, new_connection);
+
+ memset(out_buff, 0, sizeof(out_buff));
+ pHeader = (TrackerHeader *)out_buff;
+ snprintf(out_buff + sizeof(TrackerHeader), sizeof(out_buff) - \
+ sizeof(TrackerHeader), "%s", group_name);
+ pHeader->cmd = TRACKER_PROTO_CMD_SERVER_LIST_ONE_GROUP;
+ long2buff(FDFS_GROUP_NAME_MAX_LEN, pHeader->pkg_len);
+ if ((result=tcpsenddata_nb(conn->sock, out_buff, \
+ sizeof(out_buff), g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ pInBuff = (char *)&src;
+ result = fdfs_recv_response(conn, \
+ &pInBuff, sizeof(TrackerGroupStat), &in_bytes);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(conn, result != 0);
+ }
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ if (in_bytes != sizeof(TrackerGroupStat))
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid", \
+ __LINE__, pTrackerServer->ip_addr, \
+ pTrackerServer->port, in_bytes);
+ return EINVAL;
+ }
+
+ memset(pDest, 0, sizeof(FDFSGroupStat));
+ memcpy(pDest->group_name, src.group_name, FDFS_GROUP_NAME_MAX_LEN);
+ pDest->total_mb = buff2long(src.sz_total_mb);
+ pDest->free_mb = buff2long(src.sz_free_mb);
+ pDest->trunk_free_mb = buff2long(src.sz_trunk_free_mb);
+ pDest->count= buff2long(src.sz_count);
+ pDest->storage_port= buff2long(src.sz_storage_port);
+ pDest->storage_http_port= buff2long(src.sz_storage_http_port);
+ pDest->active_count = buff2long(src.sz_active_count);
+ pDest->current_write_server = buff2long(src.sz_current_write_server);
+ pDest->store_path_count = buff2long(src.sz_store_path_count);
+ pDest->subdir_count_per_path = buff2long(src.sz_subdir_count_per_path);
+ pDest->current_trunk_file_id = buff2long(src.sz_current_trunk_file_id);
+
+ return 0;
+}
+
+int tracker_list_groups(ConnectionInfo *pTrackerServer, \
+ FDFSGroupStat *group_stats, const int max_groups, \
+ int *group_count)
+{
+ bool new_connection;
+ TrackerHeader header;
+ TrackerGroupStat stats[FDFS_MAX_GROUPS];
+ char *pInBuff;
+ ConnectionInfo *conn;
+ TrackerGroupStat *pSrc;
+ TrackerGroupStat *pEnd;
+ FDFSGroupStat *pDest;
+ int result;
+ int64_t in_bytes;
+
+ CHECK_CONNECTION(pTrackerServer, conn, result, new_connection);
+
+ memset(&header, 0, sizeof(header));
+ header.cmd = TRACKER_PROTO_CMD_SERVER_LIST_ALL_GROUPS;
+ header.status = 0;
+ if ((result=tcpsenddata_nb(conn->sock, &header, \
+ sizeof(header), g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ pInBuff = (char *)stats;
+ result = fdfs_recv_response(conn, \
+ &pInBuff, sizeof(stats), &in_bytes);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(conn, result != 0);
+ }
+
+ if (result != 0)
+ {
+ *group_count = 0;
+ return result;
+ }
+
+ if (in_bytes % sizeof(TrackerGroupStat) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid", \
+ __LINE__, pTrackerServer->ip_addr, \
+ pTrackerServer->port, in_bytes);
+ *group_count = 0;
+ return EINVAL;
+ }
+
+ *group_count = in_bytes / sizeof(TrackerGroupStat);
+ if (*group_count > max_groups)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d insufficent space, " \
+ "max group count: %d, expect count: %d", \
+ __LINE__, pTrackerServer->ip_addr, \
+ pTrackerServer->port, max_groups, *group_count);
+ *group_count = 0;
+ return ENOSPC;
+ }
+
+ memset(group_stats, 0, sizeof(FDFSGroupStat) * max_groups);
+ pDest = group_stats;
+ pEnd = stats + (*group_count);
+ for (pSrc=stats; pSrcgroup_name, pSrc->group_name, \
+ FDFS_GROUP_NAME_MAX_LEN);
+ pDest->total_mb = buff2long(pSrc->sz_total_mb);
+ pDest->free_mb = buff2long(pSrc->sz_free_mb);
+ pDest->trunk_free_mb = buff2long(pSrc->sz_trunk_free_mb);
+ pDest->count= buff2long(pSrc->sz_count);
+ pDest->storage_port= buff2long(pSrc->sz_storage_port);
+ pDest->storage_http_port= buff2long(pSrc->sz_storage_http_port);
+ pDest->active_count = buff2long(pSrc->sz_active_count);
+ pDest->current_write_server = buff2long( \
+ pSrc->sz_current_write_server);
+ pDest->store_path_count = buff2long( \
+ pSrc->sz_store_path_count);
+ pDest->subdir_count_per_path = buff2long( \
+ pSrc->sz_subdir_count_per_path);
+ pDest->current_trunk_file_id = buff2long( \
+ pSrc->sz_current_trunk_file_id);
+
+ pDest++;
+ }
+
+ return 0;
+}
+
+int tracker_do_query_storage(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const byte cmd, \
+ const char *group_name, const char *filename)
+{
+ TrackerHeader *pHeader;
+ ConnectionInfo *conn;
+ bool new_connection;
+ char out_buff[sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + 128];
+ char in_buff[sizeof(TrackerHeader) + TRACKER_QUERY_STORAGE_FETCH_BODY_LEN];
+ char *pInBuff;
+ int64_t in_bytes;
+ int result;
+ int filename_len;
+
+ CHECK_CONNECTION(pTrackerServer, conn, result, new_connection);
+
+ memset(pStorageServer, 0, sizeof(ConnectionInfo));
+ pStorageServer->sock = -1;
+
+ memset(out_buff, 0, sizeof(out_buff));
+ pHeader = (TrackerHeader *)out_buff;
+ snprintf(out_buff + sizeof(TrackerHeader), sizeof(out_buff) - \
+ sizeof(TrackerHeader), "%s", group_name);
+ filename_len = snprintf(out_buff + sizeof(TrackerHeader) + \
+ FDFS_GROUP_NAME_MAX_LEN, \
+ sizeof(out_buff) - sizeof(TrackerHeader) - \
+ FDFS_GROUP_NAME_MAX_LEN, "%s", filename);
+
+ long2buff(FDFS_GROUP_NAME_MAX_LEN + filename_len, pHeader->pkg_len);
+ pHeader->cmd = cmd;
+ if ((result=tcpsenddata_nb(conn->sock, out_buff, \
+ sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN +
+ filename_len, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ pInBuff = in_buff;
+ result = fdfs_recv_response(conn, \
+ &pInBuff, sizeof(in_buff), &in_bytes);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(conn, result != 0);
+ }
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ if (in_bytes != TRACKER_QUERY_STORAGE_FETCH_BODY_LEN)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid, " \
+ "expect length: %d", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, in_bytes, \
+ TRACKER_QUERY_STORAGE_FETCH_BODY_LEN);
+ return EINVAL;
+ }
+
+ memcpy(pStorageServer->ip_addr, in_buff + \
+ FDFS_GROUP_NAME_MAX_LEN, IP_ADDRESS_SIZE-1);
+ pStorageServer->port = (int)buff2long(in_buff + \
+ FDFS_GROUP_NAME_MAX_LEN + IP_ADDRESS_SIZE - 1);
+ return 0;
+}
+
+int tracker_query_storage_list(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int nMaxServerCount, \
+ int *server_count, char *group_name, const char *filename)
+{
+ TrackerHeader *pHeader;
+ ConnectionInfo *pServer;
+ ConnectionInfo *pServerEnd;
+ ConnectionInfo *conn;
+ bool new_connection;
+ char out_buff[sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + 128];
+ char in_buff[sizeof(TrackerHeader) + \
+ TRACKER_QUERY_STORAGE_FETCH_BODY_LEN + \
+ FDFS_MAX_SERVERS_EACH_GROUP * IP_ADDRESS_SIZE];
+ char *pInBuff;
+ int64_t in_bytes;
+ int result;
+ int filename_len;
+
+ CHECK_CONNECTION(pTrackerServer, conn, result, new_connection);
+
+ memset(out_buff, 0, sizeof(out_buff));
+ pHeader = (TrackerHeader *)out_buff;
+ snprintf(out_buff + sizeof(TrackerHeader), sizeof(out_buff) - \
+ sizeof(TrackerHeader), "%s", group_name);
+ filename_len = snprintf(out_buff + sizeof(TrackerHeader) + \
+ FDFS_GROUP_NAME_MAX_LEN, \
+ sizeof(out_buff) - sizeof(TrackerHeader) - \
+ FDFS_GROUP_NAME_MAX_LEN, "%s", filename);
+
+ long2buff(FDFS_GROUP_NAME_MAX_LEN + filename_len, pHeader->pkg_len);
+ pHeader->cmd = TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL;
+ if ((result=tcpsenddata_nb(conn->sock, out_buff, \
+ sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN +
+ filename_len, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ pInBuff = in_buff;
+ result = fdfs_recv_response(conn, \
+ &pInBuff, sizeof(in_buff), &in_bytes);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(conn, result != 0);
+ }
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ if ((in_bytes - TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) % \
+ (IP_ADDRESS_SIZE - 1) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid", \
+ __LINE__, pTrackerServer->ip_addr, \
+ pTrackerServer->port, in_bytes);
+ return EINVAL;
+ }
+
+ *server_count = 1 + (in_bytes - TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) /
+ (IP_ADDRESS_SIZE - 1);
+ if (nMaxServerCount < *server_count)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response storage server " \
+ "count: %d, exceeds max server count: %d!", __LINE__, \
+ pTrackerServer->ip_addr, pTrackerServer->port, \
+ *server_count, nMaxServerCount);
+ return ENOSPC;
+ }
+
+ memset(pStorageServer, 0, nMaxServerCount * sizeof(ConnectionInfo));
+ pStorageServer->sock = -1;
+
+ memcpy(group_name, pInBuff, FDFS_GROUP_NAME_MAX_LEN);
+ *(group_name + FDFS_GROUP_NAME_MAX_LEN) = '\0';
+ pInBuff += FDFS_GROUP_NAME_MAX_LEN;
+ memcpy(pStorageServer->ip_addr, pInBuff, IP_ADDRESS_SIZE - 1);
+ pInBuff += IP_ADDRESS_SIZE - 1;
+ pStorageServer->port = (int)buff2long(pInBuff);
+ pInBuff += FDFS_PROTO_PKG_LEN_SIZE;
+
+ pServerEnd = pStorageServer + (*server_count);
+ for (pServer=pStorageServer+1; pServersock = -1;
+ pServer->port = pStorageServer->port;
+ memcpy(pServer->ip_addr, pInBuff, IP_ADDRESS_SIZE - 1);
+ pInBuff += IP_ADDRESS_SIZE - 1;
+ }
+
+ return 0;
+}
+
+int tracker_query_storage_store_without_group(ConnectionInfo *pTrackerServer,
+ ConnectionInfo *pStorageServer, char *group_name,
+ int *store_path_index)
+{
+ TrackerHeader header;
+ char in_buff[sizeof(TrackerHeader) + \
+ TRACKER_QUERY_STORAGE_STORE_BODY_LEN];
+ bool new_connection;
+ ConnectionInfo *conn;
+ char *pInBuff;
+ int64_t in_bytes;
+ int result;
+
+ CHECK_CONNECTION(pTrackerServer, conn, result, new_connection);
+
+ memset(pStorageServer, 0, sizeof(ConnectionInfo));
+ pStorageServer->sock = -1;
+
+ memset(&header, 0, sizeof(header));
+ header.cmd = TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE;
+ if ((result=tcpsenddata_nb(conn->sock, &header, \
+ sizeof(header), g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ pInBuff = in_buff;
+ result = fdfs_recv_response(conn, \
+ &pInBuff, sizeof(in_buff), &in_bytes);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(conn, result != 0);
+ }
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ if (in_bytes != TRACKER_QUERY_STORAGE_STORE_BODY_LEN)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid, " \
+ "expect length: %d", __LINE__, \
+ pTrackerServer->ip_addr, pTrackerServer->port, \
+ in_bytes, TRACKER_QUERY_STORAGE_STORE_BODY_LEN);
+ return EINVAL;
+ }
+
+ memcpy(group_name, in_buff, FDFS_GROUP_NAME_MAX_LEN);
+ *(group_name + FDFS_GROUP_NAME_MAX_LEN) = '\0';
+ memcpy(pStorageServer->ip_addr, in_buff + \
+ FDFS_GROUP_NAME_MAX_LEN, IP_ADDRESS_SIZE-1);
+ pStorageServer->port = (int)buff2long(in_buff + \
+ FDFS_GROUP_NAME_MAX_LEN + IP_ADDRESS_SIZE - 1);
+ *store_path_index = *(in_buff + FDFS_GROUP_NAME_MAX_LEN + \
+ IP_ADDRESS_SIZE - 1 + FDFS_PROTO_PKG_LEN_SIZE);
+
+ return 0;
+}
+
+int tracker_query_storage_store_with_group(ConnectionInfo *pTrackerServer, \
+ const char *group_name, ConnectionInfo *pStorageServer, \
+ int *store_path_index)
+{
+ TrackerHeader *pHeader;
+ ConnectionInfo *conn;
+ bool new_connection;
+ char out_buff[sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN];
+ char in_buff[sizeof(TrackerHeader) + \
+ TRACKER_QUERY_STORAGE_STORE_BODY_LEN];
+ char *pInBuff;
+ int64_t in_bytes;
+ int result;
+
+ CHECK_CONNECTION(pTrackerServer, conn, result, new_connection);
+
+ memset(pStorageServer, 0, sizeof(ConnectionInfo));
+ pStorageServer->sock = -1;
+
+ pHeader = (TrackerHeader *)out_buff;
+ memset(out_buff, 0, sizeof(out_buff));
+ snprintf(out_buff + sizeof(TrackerHeader), sizeof(out_buff) - \
+ sizeof(TrackerHeader), "%s", group_name);
+
+ long2buff(FDFS_GROUP_NAME_MAX_LEN, pHeader->pkg_len);
+ pHeader->cmd = TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE;
+ if ((result=tcpsenddata_nb(conn->sock, out_buff, \
+ sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN, \
+ g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ pInBuff = in_buff;
+ result = fdfs_recv_response(conn, \
+ &pInBuff, sizeof(in_buff), &in_bytes);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(conn, result != 0);
+ }
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ if (in_bytes != TRACKER_QUERY_STORAGE_STORE_BODY_LEN)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid, " \
+ "expect length: %d", __LINE__, \
+ pTrackerServer->ip_addr, pTrackerServer->port, \
+ in_bytes, TRACKER_QUERY_STORAGE_STORE_BODY_LEN);
+ return EINVAL;
+ }
+
+ memcpy(pStorageServer->ip_addr, in_buff + \
+ FDFS_GROUP_NAME_MAX_LEN, IP_ADDRESS_SIZE-1);
+ pStorageServer->port = (int)buff2long(in_buff + \
+ FDFS_GROUP_NAME_MAX_LEN + IP_ADDRESS_SIZE - 1);
+ *store_path_index = *(in_buff + FDFS_GROUP_NAME_MAX_LEN + \
+ IP_ADDRESS_SIZE - 1 + FDFS_PROTO_PKG_LEN_SIZE);
+
+ return 0;
+}
+
+int tracker_query_storage_store_list_with_group( \
+ ConnectionInfo *pTrackerServer, const char *group_name, \
+ ConnectionInfo *storageServers, const int nMaxServerCount, \
+ int *storage_count, int *store_path_index)
+{
+ ConnectionInfo *pStorageServer;
+ ConnectionInfo *pServerEnd;
+ TrackerHeader *pHeader;
+ ConnectionInfo *conn;
+ bool new_connection;
+ char out_buff[sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN];
+ char in_buff[sizeof(TrackerHeader) + FDFS_MAX_SERVERS_EACH_GROUP * \
+ TRACKER_QUERY_STORAGE_STORE_BODY_LEN];
+ char returned_group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
+ char *pInBuff;
+ char *p;
+ int64_t in_bytes;
+ int out_len;
+ int ipPortsLen;
+ int result;
+
+ *storage_count = 0;
+ CHECK_CONNECTION(pTrackerServer, conn, result, new_connection);
+
+ pHeader = (TrackerHeader *)out_buff;
+ memset(out_buff, 0, sizeof(out_buff));
+
+ if (group_name == NULL || *group_name == '\0')
+ {
+ pHeader->cmd = TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL;
+ out_len = 0;
+ }
+ else
+ {
+ pHeader->cmd = TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL;
+ snprintf(out_buff + sizeof(TrackerHeader), sizeof(out_buff) - \
+ sizeof(TrackerHeader), "%s", group_name);
+ out_len = FDFS_GROUP_NAME_MAX_LEN;
+ }
+
+ long2buff(out_len, pHeader->pkg_len);
+ if ((result=tcpsenddata_nb(conn->sock, out_buff, \
+ sizeof(TrackerHeader) + out_len, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ pInBuff = in_buff;
+ result = fdfs_recv_response(conn, \
+ &pInBuff, sizeof(in_buff), &in_bytes);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(conn, result != 0);
+ }
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ if (in_bytes < TRACKER_QUERY_STORAGE_STORE_BODY_LEN)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid, " \
+ "expect length >= %d", __LINE__, \
+ pTrackerServer->ip_addr, pTrackerServer->port, \
+ in_bytes, TRACKER_QUERY_STORAGE_STORE_BODY_LEN);
+ return EINVAL;
+ }
+
+#define RECORD_LENGTH (IP_ADDRESS_SIZE - 1 + FDFS_PROTO_PKG_LEN_SIZE)
+
+ ipPortsLen = in_bytes - (FDFS_GROUP_NAME_MAX_LEN + 1);
+ if (ipPortsLen % RECORD_LENGTH != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid", \
+ __LINE__, pTrackerServer->ip_addr, \
+ pTrackerServer->port, in_bytes);
+ return EINVAL;
+ }
+
+ *storage_count = ipPortsLen / RECORD_LENGTH;
+ if (nMaxServerCount < *storage_count)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response storage server " \
+ "count: %d, exceeds max server count: %d!", \
+ __LINE__, pTrackerServer->ip_addr, \
+ pTrackerServer->port, *storage_count, nMaxServerCount);
+ return ENOSPC;
+ }
+
+ memset(storageServers, 0, sizeof(ConnectionInfo) * nMaxServerCount);
+
+ memcpy(returned_group_name, in_buff, FDFS_GROUP_NAME_MAX_LEN);
+ p = in_buff + FDFS_GROUP_NAME_MAX_LEN;
+ *(returned_group_name + FDFS_GROUP_NAME_MAX_LEN) = '\0';
+
+ pServerEnd = storageServers + (*storage_count);
+ for (pStorageServer=storageServers; pStorageServersock = -1;
+ memcpy(pStorageServer->ip_addr, p, IP_ADDRESS_SIZE - 1);
+ p += IP_ADDRESS_SIZE - 1;
+
+ pStorageServer->port = (int)buff2long(p);
+ p += FDFS_PROTO_PKG_LEN_SIZE;
+ }
+
+ *store_path_index = *p;
+
+ return 0;
+}
+
+int tracker_delete_storage(TrackerServerGroup *pTrackerGroup, \
+ const char *group_name, const char *storage_id)
+{
+ ConnectionInfo *conn;
+ TrackerHeader *pHeader;
+ ConnectionInfo tracker_server;
+ ConnectionInfo *pServer;
+ ConnectionInfo *pEnd;
+ FDFSStorageInfo storage_infos[1];
+ char out_buff[sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + \
+ FDFS_STORAGE_ID_MAX_SIZE];
+ char in_buff[1];
+ char *pInBuff;
+ int64_t in_bytes;
+ int result;
+ int storage_id_len;
+ int storage_count;
+ int enoent_count;
+
+ enoent_count = 0;
+ pEnd = pTrackerGroup->servers + pTrackerGroup->server_count;
+ for (pServer=pTrackerGroup->servers; pServerserver_count)
+ {
+ return ENOENT;
+ }
+
+ memset(out_buff, 0, sizeof(out_buff));
+ pHeader = (TrackerHeader *)out_buff;
+ snprintf(out_buff + sizeof(TrackerHeader), sizeof(out_buff) - \
+ sizeof(TrackerHeader), "%s", group_name);
+ storage_id_len = snprintf(out_buff + sizeof(TrackerHeader) + \
+ FDFS_GROUP_NAME_MAX_LEN, \
+ sizeof(out_buff) - sizeof(TrackerHeader) - \
+ FDFS_GROUP_NAME_MAX_LEN, "%s", storage_id);
+
+ long2buff(FDFS_GROUP_NAME_MAX_LEN + storage_id_len, pHeader->pkg_len);
+ pHeader->cmd = TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE;
+
+ enoent_count = 0;
+ result = 0;
+ for (pServer=pTrackerGroup->servers; pServersock, out_buff, \
+ sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN +
+ storage_id_len, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ tracker_server.ip_addr, tracker_server.port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ pInBuff = in_buff;
+ result = fdfs_recv_response(conn, &pInBuff, 0, &in_bytes);
+ }
+
+ tracker_disconnect_server_ex(conn, result != 0 && result != ENOENT);
+ if (result != 0)
+ {
+ if (result == ENOENT)
+ {
+ enoent_count++;
+ }
+ else if (result == EALREADY)
+ {
+ }
+ else
+ {
+ return result;
+ }
+ }
+ }
+
+ if (enoent_count == pTrackerGroup->server_count)
+ {
+ return ENOENT;
+ }
+
+ return result == ENOENT ? 0 : result;
+}
+
+int tracker_set_trunk_server(TrackerServerGroup *pTrackerGroup, \
+ const char *group_name, const char *storage_id, \
+ char *new_trunk_server_id)
+{
+ TrackerHeader *pHeader;
+ ConnectionInfo *conn;
+ ConnectionInfo *pServer;
+ ConnectionInfo *pEnd;
+ ConnectionInfo tracker_server;
+ char out_buff[sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + \
+ FDFS_STORAGE_ID_MAX_SIZE];
+ char in_buff[FDFS_STORAGE_ID_MAX_SIZE];
+ char *pInBuff;
+ int64_t in_bytes;
+ int result;
+ int storage_id_len;
+
+ *new_trunk_server_id = '\0';
+ memset(out_buff, 0, sizeof(out_buff));
+ memset(in_buff, 0, sizeof(in_buff));
+ pHeader = (TrackerHeader *)out_buff;
+ snprintf(out_buff + sizeof(TrackerHeader), sizeof(out_buff) - \
+ sizeof(TrackerHeader), "%s", group_name);
+ if (storage_id == NULL)
+ {
+ storage_id_len = 0;
+ }
+ else
+ {
+ storage_id_len = snprintf(out_buff + sizeof(TrackerHeader) + \
+ FDFS_GROUP_NAME_MAX_LEN, \
+ sizeof(out_buff) - sizeof(TrackerHeader) - \
+ FDFS_GROUP_NAME_MAX_LEN, "%s", storage_id);
+ }
+
+ long2buff(FDFS_GROUP_NAME_MAX_LEN + storage_id_len, pHeader->pkg_len);
+ pHeader->cmd = TRACKER_PROTO_CMD_SERVER_SET_TRUNK_SERVER;
+
+ result = 0;
+ pEnd = pTrackerGroup->servers + pTrackerGroup->server_count;
+ for (pServer=pTrackerGroup->servers; pServersock, out_buff, \
+ sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN +
+ storage_id_len, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ tracker_server.ip_addr, tracker_server.port, \
+ result, STRERROR(result));
+
+ tracker_disconnect_server_ex(conn, true);
+ continue;
+ }
+
+ pInBuff = in_buff;
+ result = fdfs_recv_response(conn, &pInBuff, \
+ sizeof(in_buff) - 1, &in_bytes);
+
+ tracker_disconnect_server_ex(conn, result != 0);
+ if (result == 0)
+ {
+ strcpy(new_trunk_server_id, in_buff);
+ return 0;
+ }
+
+ if (result == EOPNOTSUPP)
+ {
+ continue;
+ }
+ if (result == EALREADY)
+ {
+ if (storage_id_len > 0)
+ {
+ strcpy(new_trunk_server_id, storage_id);
+ }
+ return result;
+ }
+ else
+ {
+ return result;
+ }
+ }
+
+ return result;
+}
+
+int tracker_get_storage_status(ConnectionInfo *pTrackerServer, \
+ const char *group_name, const char *ip_addr, \
+ FDFSStorageBrief *pDestBuff)
+{
+ TrackerHeader *pHeader;
+ ConnectionInfo *conn;
+ bool new_connection;
+ char out_buff[sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + \
+ IP_ADDRESS_SIZE];
+ char *pInBuff;
+ char *p;
+ int result;
+ int ip_len;
+ int64_t in_bytes;
+
+ CHECK_CONNECTION(pTrackerServer, conn, result, new_connection);
+
+ if (ip_addr == NULL)
+ {
+ ip_len = 0;
+ }
+ else
+ {
+ ip_len = strlen(ip_addr);
+ }
+
+ memset(out_buff, 0, sizeof(out_buff));
+ pHeader = (TrackerHeader *)out_buff;
+ p = out_buff + sizeof(TrackerHeader);
+ snprintf(p, sizeof(out_buff) - sizeof(TrackerHeader), "%s", group_name);
+ p += FDFS_GROUP_NAME_MAX_LEN;
+ if (ip_len > 0)
+ {
+ memcpy(p, ip_addr, ip_len);
+ p += ip_len;
+ }
+ pHeader->cmd = TRACKER_PROTO_CMD_STORAGE_GET_STATUS;
+ long2buff(FDFS_GROUP_NAME_MAX_LEN + ip_len, pHeader->pkg_len);
+ if ((result=tcpsenddata_nb(conn->sock, out_buff, \
+ p - out_buff, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ pInBuff = (char *)pDestBuff;
+ result = fdfs_recv_response(conn, \
+ &pInBuff, sizeof(FDFSStorageBrief), &in_bytes);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(conn, result != 0);
+ }
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ if (in_bytes != sizeof(FDFSStorageBrief))
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid", \
+ __LINE__, pTrackerServer->ip_addr, \
+ pTrackerServer->port, in_bytes);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int tracker_get_storage_id(ConnectionInfo *pTrackerServer, \
+ const char *group_name, const char *ip_addr, \
+ char *storage_id)
+{
+ TrackerHeader *pHeader;
+ ConnectionInfo *conn;
+ bool new_connection;
+ char out_buff[sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + \
+ IP_ADDRESS_SIZE];
+ char *p;
+ int result;
+ int ip_len;
+ int64_t in_bytes;
+
+ if (storage_id == NULL)
+ {
+ return EINVAL;
+ }
+
+ CHECK_CONNECTION(pTrackerServer, conn, result, new_connection);
+
+ if (ip_addr == NULL)
+ {
+ ip_len = 0;
+ }
+ else
+ {
+ ip_len = strlen(ip_addr);
+ }
+
+ memset(out_buff, 0, sizeof(out_buff));
+ pHeader = (TrackerHeader *)out_buff;
+ p = out_buff + sizeof(TrackerHeader);
+ snprintf(p, sizeof(out_buff) - sizeof(TrackerHeader), "%s", group_name);
+ p += FDFS_GROUP_NAME_MAX_LEN;
+ if (ip_len > 0)
+ {
+ memcpy(p, ip_addr, ip_len);
+ p += ip_len;
+ }
+ pHeader->cmd = TRACKER_PROTO_CMD_STORAGE_GET_SERVER_ID;
+ long2buff(FDFS_GROUP_NAME_MAX_LEN + ip_len, pHeader->pkg_len);
+ if ((result=tcpsenddata_nb(conn->sock, out_buff, \
+ p - out_buff, g_fdfs_network_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "send data to tracker server %s:%d fail, " \
+ "errno: %d, error info: %s", __LINE__, \
+ pTrackerServer->ip_addr, \
+ pTrackerServer->port, \
+ result, STRERROR(result));
+ }
+ else
+ {
+ result = fdfs_recv_response(conn, \
+ &storage_id, FDFS_STORAGE_ID_MAX_SIZE, &in_bytes);
+ }
+
+ if (new_connection)
+ {
+ tracker_disconnect_server_ex(conn, result != 0);
+ }
+
+ if (result != 0)
+ {
+ return result;
+ }
+
+ if (in_bytes == 0 || in_bytes >= FDFS_STORAGE_ID_MAX_SIZE)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "tracker server %s:%d response data " \
+ "length: "INT64_PRINTF_FORMAT" is invalid", \
+ __LINE__, pTrackerServer->ip_addr, \
+ pTrackerServer->port, in_bytes);
+ return EINVAL;
+ }
+
+ *(storage_id + in_bytes) = '\0';
+ return 0;
+}
+
+int tracker_get_storage_max_status(TrackerServerGroup *pTrackerGroup, \
+ const char *group_name, const char *ip_addr, \
+ char *storage_id, int *status)
+{
+ ConnectionInfo *conn;
+ ConnectionInfo tracker_server;
+ ConnectionInfo *pServer;
+ ConnectionInfo *pEnd;
+ FDFSStorageBrief storage_brief;
+ int result;
+
+ memset(&storage_brief, 0, sizeof(FDFSStorageBrief));
+ storage_brief.status = -1;
+
+ *storage_id = '\0';
+ *status = -1;
+ pEnd = pTrackerGroup->servers + pTrackerGroup->server_count;
+ for (pServer=pTrackerGroup->servers; pServer *status)
+ {
+ *status = storage_brief.status;
+ }
+ }
+
+ if (*status == -1)
+ {
+ return ENOENT;
+ }
+
+ return 0;
+}
+
diff --git a/client/tracker_client.h b/client/tracker_client.h
new file mode 100644
index 0000000..fb11a10
--- /dev/null
+++ b/client/tracker_client.h
@@ -0,0 +1,353 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#ifndef TRACKER_CLIENT_H
+#define TRACKER_CLIENT_H
+
+#include "tracker_types.h"
+#include "tracker_proto.h"
+#include "client_global.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ bool if_trunk_server;
+ char status;
+ char id[FDFS_STORAGE_ID_MAX_SIZE];
+ char ip_addr[IP_ADDRESS_SIZE];
+ char src_id[FDFS_STORAGE_ID_MAX_SIZE]; //src storage id
+ char domain_name[FDFS_DOMAIN_NAME_MAX_SIZE]; //http domain name
+ char version[FDFS_VERSION_SIZE];
+ int total_mb; //total disk storage in MB
+ int free_mb; //free disk storage in MB
+ int upload_priority; //upload priority
+ time_t join_time; //storage join timestamp (create timestamp)
+ time_t up_time; //storage service started timestamp
+ int store_path_count; //store base path count of each storage server
+ int subdir_count_per_path;
+ int storage_port;
+ int storage_http_port; //storage http server port
+ int current_write_path; //current write path index
+ FDFSStorageStat stat;
+} FDFSStorageInfo;
+
+
+#define CHECK_CONNECTION(pTrackerServer, conn, result, new_connection) \
+ do { \
+ if (pTrackerServer->sock < 0) \
+ { \
+ if ((conn=tracker_connect_server( \
+ pTrackerServer, &result)) != NULL) \
+ { \
+ return result; \
+ } \
+ new_connection = true; \
+ } \
+ else \
+ { \
+ conn = pTrackerServer; \
+ new_connection = false; \
+ } \
+ } while (0)
+
+
+#define tracker_get_connection() \
+ tracker_get_connection_ex((&g_tracker_group))
+
+/**
+* get a connection to tracker server
+* params:
+* pTrackerGroup: the tracker group
+* return: != NULL for success, NULL for fail
+**/
+ConnectionInfo *tracker_get_connection_ex(TrackerServerGroup *pTrackerGroup);
+
+
+#define tracker_get_connection_r(pTrackerServer, err_no) \
+ tracker_get_connection_r_ex((&g_tracker_group), pTrackerServer, err_no)
+
+/**
+* get a connection to tracker server
+* params:
+* pTrackerGroup: the tracker group
+* pTrackerServer: tracker server
+* return: 0 success, !=0 fail
+**/
+ConnectionInfo *tracker_get_connection_r_ex(TrackerServerGroup *pTrackerGroup, \
+ ConnectionInfo *pTrackerServer, int *err_no);
+
+#define tracker_get_all_connections() \
+ tracker_get_all_connections_ex((&g_tracker_group))
+
+
+/**
+* get a connection to tracker server without connection pool
+* params:
+* pTrackerGroup: the tracker group
+* return: != NULL for success, NULL for fail
+**/
+ConnectionInfo *tracker_get_connection_no_pool( \
+ TrackerServerGroup *pTrackerGroup);
+
+/**
+* connect to all tracker servers
+* params:
+* pTrackerGroup: the tracker group
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_get_all_connections_ex(TrackerServerGroup *pTrackerGroup);
+
+#define tracker_close_all_connections() \
+ tracker_close_all_connections_ex((&g_tracker_group))
+
+/**
+* close all connections to tracker servers
+* params:
+* pTrackerGroup: the tracker group
+* return:
+**/
+void tracker_close_all_connections_ex(TrackerServerGroup *pTrackerGroup);
+
+/**
+* list one group
+* params:
+* pTrackerServer: tracker server
+* group_name: the group name
+* pDest: return the group info
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_list_one_group(ConnectionInfo *pTrackerServer, \
+ const char *group_name, FDFSGroupStat *pDest);
+
+
+/**
+* list all groups
+* params:
+* pTrackerServer: tracker server
+* group_stats: return group info array
+* max_groups: max group count(group array capacity)
+* group_count: return group count
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_list_groups(ConnectionInfo *pTrackerServer, \
+ FDFSGroupStat *group_stats, const int max_groups, \
+ int *group_count);
+
+/**
+* list all servers of the specified group
+* params:
+* pTrackerServer: tracker server
+* szGroupName: group name to query
+* szStorageId: the storage id to query, can be NULL or empty
+* storage_infos: return storage info array
+* max_storages: max storage count(storage array capacity)
+* storage_count: return storage count
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_list_servers(ConnectionInfo *pTrackerServer, \
+ const char *szGroupName, const char *szStorageId, \
+ FDFSStorageInfo *storage_infos, const int max_storages, \
+ int *storage_count);
+
+#define tracker_query_storage_store(pTrackerServer, pStorageServer, \
+ group_name, store_path_index) \
+ tracker_query_storage_store_without_group(pTrackerServer, \
+ pStorageServer, group_name, store_path_index)
+/**
+* query storage server to upload file
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: return storage server
+* store_path_index: return the index of path on the storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_query_storage_store_without_group(ConnectionInfo *pTrackerServer,
+ ConnectionInfo *pStorageServer, char *group_name,
+ int *store_path_index);
+
+/**
+* query storage servers/list to upload file
+* params:
+* pTrackerServer: tracker server
+* storageServers: store the storage server list
+* nMaxServerCount: max storage server count
+* storage_count: return the storage server count
+* store_path_index: return the index of path on the storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+#define tracker_query_storage_store_list_without_group( \
+ pTrackerServer, storageServers, nMaxServerCount, \
+ storage_count, group_name, store_path_index) \
+ tracker_query_storage_store_list_with_group( \
+ pTrackerServer, NULL, storageServers, nMaxServerCount, \
+ storage_count, store_path_index)
+
+/**
+* query storage server to upload file
+* params:
+* pTrackerServer: tracker server
+* group_name: the group name to upload file to
+* pStorageServer: return storage server
+* store_path_index: return the index of path on the storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_query_storage_store_with_group(ConnectionInfo *pTrackerServer, \
+ const char *group_name, ConnectionInfo *pStorageServer, \
+ int *store_path_index);
+
+/**
+* query storage servers/list to upload file
+* params:
+* pTrackerServer: tracker server
+* group_name: the group name to upload file to
+* storageServers: store the storage server list
+* nMaxServerCount: max storage server count
+* storage_count: return the storage server count
+* store_path_index: return the index of path on the storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_query_storage_store_list_with_group( \
+ ConnectionInfo *pTrackerServer, const char *group_name, \
+ ConnectionInfo *storageServers, const int nMaxServerCount, \
+ int *storage_count, int *store_path_index);
+
+/**
+* query storage server to update (delete file or set meta data)
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: return storage server
+* group_name: the group name of storage server
+* filename: filename on storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+#define tracker_query_storage_update(pTrackerServer, \
+ pStorageServer, group_name, filename) \
+ tracker_do_query_storage(pTrackerServer, \
+ pStorageServer, TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE,\
+ group_name, filename)
+
+/**
+* query storage server to download file
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: return storage server
+* group_name: the group name of storage server
+* filename: filename on storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+#define tracker_query_storage_fetch(pTrackerServer, \
+ pStorageServer, group_name, filename) \
+ tracker_do_query_storage(pTrackerServer, \
+ pStorageServer, TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE,\
+ group_name, filename)
+
+/**
+* query storage server to fetch or update
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: return storage server
+* cmd : command, TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE or
+* TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE
+* group_name: the group name of storage server
+* filename: filename on storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_do_query_storage(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const byte cmd, \
+ const char *group_name, const char *filename);
+
+/**
+* query storage server list to fetch file
+* params:
+* pTrackerServer: tracker server
+* pStorageServer: return storage server
+* nMaxServerCount: max storage server count
+* server_count: return storage server count
+* group_name: the group name of storage server
+* filename: filename on storage server
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_query_storage_list(ConnectionInfo *pTrackerServer, \
+ ConnectionInfo *pStorageServer, const int nMaxServerCount, \
+ int *server_count, char *group_name, const char *filename);
+
+/**
+* delete a storage server from cluster
+* params:
+* pTrackerGroup: the tracker group
+* group_name: the group name which the storage server belongs to
+* storage_id: the storage server id
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_delete_storage(TrackerServerGroup *pTrackerGroup, \
+ const char *group_name, const char *storage_id);
+
+
+/**
+* set trunk server of the specified group
+* params:
+* pTrackerGroup: the tracker group
+* group_name: the group name which the storage server belongs to
+* storage_id: the storage server id, can be NULL or empty
+* new_trunk_server_id: the new trunk server id
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_set_trunk_server(TrackerServerGroup *pTrackerGroup, \
+ const char *group_name, const char *storage_id, \
+ char *new_trunk_server_id);
+
+
+/**
+* get storage server status from the tracker server
+* params:
+* pTrackerServer: tracker server
+* group_name: the group name which the storage server belongs to
+* ip_addr: the ip addr of the storage server
+* pDestBuff: return the storage server brief info
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_get_storage_status(ConnectionInfo *pTrackerServer, \
+ const char *group_name, const char *ip_addr, \
+ FDFSStorageBrief *pDestBuff);
+
+
+/**
+* get storage server id from the tracker server
+* params:
+* pTrackerServer: tracker server
+* group_name: the group name which the storage server belongs to
+* ip_addr: the ip addr of the storage server
+* storage_id: return the storage server id
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_get_storage_id(ConnectionInfo *pTrackerServer, \
+ const char *group_name, const char *ip_addr, \
+ char *storage_id);
+
+/**
+* get storage server highest level status from all tracker servers
+* params:
+* pTrackerGroup: the tracker group
+* group_name: the group name which the storage server belongs to
+* ip_addr: the ip addr of the storage server
+* storage_id: return the storage server id
+* status: return the highest level status
+* return: 0 success, !=0 fail, return the error code
+**/
+int tracker_get_storage_max_status(TrackerServerGroup *pTrackerGroup, \
+ const char *group_name, const char *ip_addr, \
+ char *storage_id, int *status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/common/Makefile b/common/Makefile
new file mode 100644
index 0000000..c7ffc92
--- /dev/null
+++ b/common/Makefile
@@ -0,0 +1,28 @@
+.SUFFIXES: .c .o
+
+COMPILE = $(CC) -Wall -O2 -D_FILE_OFFSET_BITS=64 -DOS_LINUX
+#COMPILE = $(CC) -Wall -g -D_FILE_OFFSET_BITS=64 -DOS_LINUX -D__DEBUG__
+INC_PATH = -I/usr/local/include
+LIB_PATH = -L/usr/local/lib
+TARGET_PATH = /usr/local/bin
+
+COMMON_LIB =
+SHARED_OBJS = hash.o chain.o shared_func.o ini_file_reader.o \
+ logger.o sockopt.o fdfs_global.o base64.o sched_thread.o \
+ mime_file_parser.o fdfs_http_shared.o
+
+ALL_OBJS = $(SHARED_OBJS)
+
+ALL_PRGS =
+
+all: $(ALL_OBJS) $(ALL_PRGS)
+.o:
+ $(COMPILE) -o $@ $< $(SHARED_OBJS) $(COMMON_LIB) $(LIB_PATH) $(INC_PATH)
+.c:
+ $(COMPILE) -o $@ $< $(ALL_OBJS) $(COMMON_LIB) $(LIB_PATH) $(INC_PATH)
+.c.o:
+ $(COMPILE) -c -o $@ $< $(INC_PATH)
+install:
+ cp -f $(ALL_PRGS) $(TARGET_PATH)
+clean:
+ rm -f $(ALL_OBJS) $(ALL_PRGS)
diff --git a/common/_os_bits.h b/common/_os_bits.h
new file mode 100644
index 0000000..81e1626
--- /dev/null
+++ b/common/_os_bits.h
@@ -0,0 +1,7 @@
+#ifndef _OS_BITS_H
+#define _OS_BITS_H
+
+#define OS_BITS 64
+#define OFF_BITS 64
+
+#endif
diff --git a/common/avl_tree.c b/common/avl_tree.c
new file mode 100644
index 0000000..65c7e36
--- /dev/null
+++ b/common/avl_tree.c
@@ -0,0 +1,789 @@
+#include "avl_tree.h"
+
+int avl_tree_init(AVLTreeInfo *tree, FreeDataFunc free_data_func, \
+ CompareFunc compare_func)
+{
+ tree->root = NULL;
+ tree->free_data_func = free_data_func;
+ tree->compare_func = compare_func;
+ return 0;
+}
+
+static void avl_tree_destroy_loop(FreeDataFunc free_data_func, \
+ AVLTreeNode *pCurrentNode)
+{
+ if (pCurrentNode->left != NULL)
+ {
+ avl_tree_destroy_loop(free_data_func, pCurrentNode->left);
+ }
+ if (pCurrentNode->right != NULL)
+ {
+ avl_tree_destroy_loop(free_data_func, pCurrentNode->right);
+ }
+
+ if (free_data_func != NULL)
+ {
+ free_data_func(pCurrentNode->data);
+ }
+ free(pCurrentNode);
+}
+
+void avl_tree_destroy(AVLTreeInfo *tree)
+{
+ if (tree == NULL)
+ {
+ return;
+ }
+
+ if (tree->root != NULL)
+ {
+ avl_tree_destroy_loop(tree->free_data_func, tree->root);
+ tree->root = NULL;
+ }
+}
+
+static AVLTreeNode *createTreeNode(AVLTreeNode *pParentNode, void *target_data)
+{
+ AVLTreeNode *pNewNode;
+ pNewNode = (AVLTreeNode *)malloc(sizeof(AVLTreeNode));
+ if (pNewNode == NULL)
+ {
+ /*
+ fprintf(stderr, "file: "__FILE__", line: %d, " \
+ "malloc %d bytes fail!\n", __LINE__, \
+ (int)sizeof(AVLTreeNode));
+ */
+ return NULL;
+ }
+
+ pNewNode->left = pNewNode->right = NULL;
+ pNewNode->data = target_data;
+ pNewNode->balance = 0;
+
+ return pNewNode;
+}
+
+static void avlRotateLeft(AVLTreeNode *pRotateNode, AVLTreeNode **ppRaiseNode)
+{
+ *ppRaiseNode = pRotateNode->right;
+ pRotateNode->right = (*ppRaiseNode)->left;
+ (*ppRaiseNode)->left = pRotateNode;
+}
+
+static void avlRotateRight(AVLTreeNode *pRotateNode, AVLTreeNode **ppRaiseNode)
+{
+ *ppRaiseNode = pRotateNode->left;
+ pRotateNode->left = (*ppRaiseNode)->right;
+ (*ppRaiseNode)->right = pRotateNode;
+}
+
+static void avlLeftBalanceWhenInsert(AVLTreeNode **pTreeNode, int *taller)
+{
+ AVLTreeNode *leftsub;
+ AVLTreeNode *rightsub;
+
+ leftsub = (*pTreeNode)->left;
+ switch (leftsub->balance)
+ {
+ case -1 :
+ (*pTreeNode)->balance = leftsub->balance = 0;
+ avlRotateRight (*pTreeNode, pTreeNode);
+ *taller = 0;
+ break;
+ case 0 :
+ break;
+ case 1 :
+ rightsub = leftsub->right;
+ switch ( rightsub->balance )
+ {
+ case -1:
+ (*pTreeNode)->balance = 1;
+ leftsub->balance = 0;
+ break;
+ case 0 :
+ (*pTreeNode)->balance = leftsub->balance = 0;
+ break;
+ case 1 :
+ (*pTreeNode)->balance = 0;
+ leftsub->balance = -1;
+ break;
+ }
+
+ rightsub->balance = 0;
+ avlRotateLeft( leftsub, &((*pTreeNode)->left));
+ avlRotateRight(*pTreeNode, pTreeNode);
+ *taller = 0;
+ }
+}
+
+static void avlRightBalanceWhenInsert(AVLTreeNode **pTreeNode, int *taller)
+{
+ AVLTreeNode *rightsub;
+ AVLTreeNode *leftsub;
+
+ rightsub = (*pTreeNode)->right;
+ switch (rightsub->balance)
+ {
+ case 1:
+ (*pTreeNode)->balance = rightsub->balance = 0;
+ avlRotateLeft(*pTreeNode, pTreeNode);
+ *taller = 0;
+ break;
+ case 0:
+ break;
+ case -1:
+ leftsub = rightsub->left;
+ switch (leftsub->balance)
+ {
+ case 1 :
+ (*pTreeNode)->balance = -1;
+ rightsub->balance = 0;
+ break;
+ case 0 :
+ (*pTreeNode)->balance = rightsub->balance = 0;
+ break;
+ case -1 :
+ (*pTreeNode)->balance = 0;
+ rightsub->balance = 1;
+ break;
+ }
+
+ leftsub->balance = 0;
+ avlRotateRight(rightsub, &((*pTreeNode)->right));
+ avlRotateLeft(*pTreeNode, pTreeNode);
+ *taller = 0;
+ }
+}
+
+static int avl_tree_insert_loop(CompareFunc compare_func, AVLTreeNode **pCurrentNode, \
+ void *target_data, int *taller)
+{
+ int nCompRes;
+ int success;
+
+ if (*pCurrentNode == NULL)
+ {
+ *pCurrentNode = createTreeNode(*pCurrentNode, target_data);
+ if (*pCurrentNode != NULL)
+ {
+ *taller = 1;
+ return 1;
+ }
+ else
+ {
+ return -ENOMEM;
+ }
+ }
+
+ nCompRes = compare_func((*pCurrentNode)->data, target_data);
+ if (nCompRes > 0)
+ {
+ success = avl_tree_insert_loop(compare_func, \
+ &((*pCurrentNode)->left), target_data, taller);
+ if (*taller != 0)
+ {
+ switch ((*pCurrentNode)->balance)
+ {
+ case -1:
+ avlLeftBalanceWhenInsert(pCurrentNode, taller);
+ break;
+ case 0:
+ (*pCurrentNode)->balance = -1;
+ break;
+ case 1:
+ (*pCurrentNode)->balance = 0;
+ *taller = 0;
+ break;
+ }
+ }
+ }
+ else if (nCompRes < 0)
+ {
+ success = avl_tree_insert_loop(compare_func, \
+ &((*pCurrentNode)->right), target_data, taller);
+ if (*taller != 0)
+ {
+ switch ((*pCurrentNode)->balance)
+ {
+ case -1:
+ (*pCurrentNode)->balance = 0;
+ *taller = 0;
+ break;
+ case 0:
+ (*pCurrentNode)->balance = 1;
+ break;
+ case 1:
+ avlRightBalanceWhenInsert(pCurrentNode, taller);
+ break;
+ }
+ }
+ }
+ else
+ {
+ return 0;
+ }
+
+ return success;
+}
+
+int avl_tree_insert(AVLTreeInfo *tree, void *data)
+{
+ int taller;
+
+ taller = 0;
+ return avl_tree_insert_loop(tree->compare_func, &(tree->root), \
+ data, &taller);
+}
+
+static int avl_tree_replace_loop(CompareFunc compare_func, \
+ FreeDataFunc free_data_func, AVLTreeNode **pCurrentNode, \
+ void *target_data, int *taller)
+{
+ int nCompRes;
+ int success;
+
+ if (*pCurrentNode == NULL )
+ {
+ *pCurrentNode = createTreeNode(*pCurrentNode, target_data);
+ if (*pCurrentNode != NULL)
+ {
+ *taller = 1;
+ return 1;
+ }
+ else
+ {
+ return -ENOMEM;
+ }
+ }
+
+ nCompRes = compare_func((*pCurrentNode)->data, target_data);
+ if (nCompRes > 0)
+ {
+ success = avl_tree_replace_loop(compare_func, free_data_func,
+ &((*pCurrentNode)->left), target_data, taller);
+ if (*taller != 0)
+ {
+ switch ((*pCurrentNode)->balance)
+ {
+ case -1:
+ avlLeftBalanceWhenInsert(pCurrentNode, taller);
+ break;
+ case 0:
+ (*pCurrentNode)->balance = -1;
+ break;
+ case 1:
+ (*pCurrentNode)->balance = 0;
+ *taller = 0;
+ break;
+ }
+ }
+ }
+ else if (nCompRes < 0)
+ {
+ success = avl_tree_replace_loop(compare_func, free_data_func,
+ &((*pCurrentNode)->right), target_data, taller);
+ if (*taller != 0)
+ {
+ switch ((*pCurrentNode)->balance)
+ {
+ case -1 :
+ (*pCurrentNode)->balance = 0;
+ *taller = 0;
+ break;
+ case 0 :
+ (*pCurrentNode)->balance = 1;
+ break;
+ case 1 :
+ avlRightBalanceWhenInsert(pCurrentNode, taller);
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (free_data_func != NULL)
+ {
+ free_data_func((*pCurrentNode)->data);
+ }
+ (*pCurrentNode)->data = target_data;
+ return 0;
+ }
+
+ return success;
+}
+
+int avl_tree_replace(AVLTreeInfo *tree, void *data)
+{
+ int taller;
+
+ taller = 0;
+ return avl_tree_replace_loop(tree->compare_func, \
+ tree->free_data_func, &(tree->root), data, &taller);
+}
+
+static AVLTreeNode *avl_tree_find_loop(CompareFunc compare_func, \
+ AVLTreeNode *pCurrentNode, void *target_data)
+{
+ int nCompRes;
+ nCompRes = compare_func(pCurrentNode->data, target_data);
+ if (nCompRes > 0)
+ {
+ if (pCurrentNode->left == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ return avl_tree_find_loop(compare_func, \
+ pCurrentNode->left, target_data);
+ }
+ }
+ else if (nCompRes < 0)
+ {
+ if (pCurrentNode->right == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ return avl_tree_find_loop(compare_func, \
+ pCurrentNode->right, target_data);
+ }
+ }
+ else
+ {
+ return pCurrentNode;
+ }
+}
+
+static void *avl_tree_find_ge_loop(CompareFunc compare_func, \
+ AVLTreeNode *pCurrentNode, void *target_data)
+{
+ int nCompRes;
+ void *found;
+
+ nCompRes = compare_func(pCurrentNode->data, target_data);
+ if (nCompRes > 0)
+ {
+ if (pCurrentNode->left == NULL)
+ {
+ return pCurrentNode->data;
+ }
+
+ found = avl_tree_find_ge_loop(compare_func, \
+ pCurrentNode->left, target_data);
+ return found != NULL ? found : pCurrentNode->data;
+ }
+ else if (nCompRes < 0)
+ {
+ if (pCurrentNode->right == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ return avl_tree_find_ge_loop(compare_func, \
+ pCurrentNode->right, target_data);
+ }
+ }
+ else
+ {
+ return pCurrentNode->data;
+ }
+}
+
+void *avl_tree_find(AVLTreeInfo *tree, void *target_data)
+{
+ AVLTreeNode *found;
+
+ if (tree->root == NULL)
+ {
+ return NULL;
+ }
+
+ found = avl_tree_find_loop(tree->compare_func, \
+ tree->root, target_data);
+
+ return found != NULL ? found->data : NULL;
+}
+
+void *avl_tree_find_ge(AVLTreeInfo *tree, void *target_data)
+{
+ void *found;
+
+ if (tree->root == NULL)
+ {
+ found = NULL;
+ }
+ else
+ {
+ found = avl_tree_find_ge_loop(tree->compare_func, \
+ tree->root, target_data);
+ }
+
+ return found;
+}
+
+static void avlLeftBalanceWhenDelete(AVLTreeNode **pTreeNode, int *shorter)
+{
+ AVLTreeNode *leftsub;
+ AVLTreeNode *rightsub;
+
+ leftsub = (*pTreeNode)->left;
+ switch (leftsub->balance)
+ {
+ case -1:
+ (*pTreeNode)->balance = leftsub->balance = 0;
+ avlRotateRight (*pTreeNode, pTreeNode);
+ break;
+ case 0:
+ leftsub->balance = 1;
+ avlRotateRight(*pTreeNode, pTreeNode);
+ *shorter = 0;
+ break;
+ case 1:
+ rightsub = leftsub->right;
+ switch ( rightsub->balance )
+ {
+ case -1:
+ (*pTreeNode)->balance = 1;
+ leftsub->balance = 0;
+ break;
+ case 0 :
+ (*pTreeNode)->balance = leftsub->balance = 0;
+ break;
+ case 1 :
+ (*pTreeNode)->balance = 0;
+ leftsub->balance = -1;
+ break;
+ }
+
+ rightsub->balance = 0;
+ avlRotateLeft( leftsub, &((*pTreeNode)->left));
+ avlRotateRight(*pTreeNode, pTreeNode);
+ break;
+ }
+}
+
+static void avlRightBalanceWhenDelete(AVLTreeNode **pTreeNode, int *shorter)
+{
+ AVLTreeNode *rightsub;
+ AVLTreeNode *leftsub;
+
+ rightsub = (*pTreeNode)->right;
+ switch (rightsub->balance)
+ {
+ case 1:
+ (*pTreeNode)->balance = rightsub->balance = 0;
+ avlRotateLeft(*pTreeNode, pTreeNode);
+ break;
+ case 0:
+ rightsub->balance = -1;
+ avlRotateLeft(*pTreeNode, pTreeNode);
+ *shorter = 0;
+ break;
+ case -1:
+ leftsub = rightsub->left;
+ switch (leftsub->balance)
+ {
+ case 1:
+ (*pTreeNode)->balance = -1;
+ rightsub->balance = 0;
+ break;
+ case 0:
+ (*pTreeNode)->balance = rightsub->balance = 0;
+ break;
+ case -1:
+ (*pTreeNode)->balance = 0;
+ rightsub->balance = 1;
+ break;
+ }
+
+ leftsub->balance = 0;
+ avlRotateRight(rightsub, &((*pTreeNode)->right));
+ avlRotateLeft(*pTreeNode, pTreeNode);
+ break;
+ }
+}
+
+static int avl_tree_delete_loop(AVLTreeInfo *tree, AVLTreeNode **pCurrentNode,\
+ void *target_data, int *shorter, AVLTreeNode *pDeletedDataNode)
+{
+ int nCompRes;
+ bool result;
+ AVLTreeNode *leftsub;
+ AVLTreeNode *rightsub;
+
+ if (pDeletedDataNode != NULL)
+ {
+ if ((*pCurrentNode)->right == NULL)
+ {
+ pDeletedDataNode->data = (*pCurrentNode)->data;
+ leftsub = (*pCurrentNode)->left;
+
+ free(*pCurrentNode);
+ *pCurrentNode = leftsub;
+ *shorter = 1;
+ return 1;
+ }
+
+ nCompRes = -1;
+ }
+ else
+ {
+ nCompRes = tree->compare_func((*pCurrentNode)->data, target_data);
+ }
+
+ if (nCompRes > 0)
+ {
+ if ((*pCurrentNode)->left == NULL)
+ {
+ return 0;
+ }
+
+ result = avl_tree_delete_loop(tree, &((*pCurrentNode)->left), \
+ target_data, shorter, pDeletedDataNode);
+ if (*shorter != 0)
+ {
+ switch ((*pCurrentNode)->balance)
+ {
+ case -1:
+ (*pCurrentNode)->balance = 0;
+ break;
+ case 0:
+ (*pCurrentNode)->balance = 1;
+ *shorter = 0;
+ break;
+ case 1:
+ avlRightBalanceWhenDelete(pCurrentNode, shorter);
+ break;
+ }
+ }
+ return result;
+ }
+ else if (nCompRes < 0)
+ {
+ if ((*pCurrentNode)->right == NULL)
+ {
+ return 0;
+ }
+
+ result = avl_tree_delete_loop(tree, &((*pCurrentNode)->right), \
+ target_data, shorter, pDeletedDataNode);
+ if (*shorter != 0)
+ {
+ switch ((*pCurrentNode)->balance)
+ {
+ case -1:
+ avlLeftBalanceWhenDelete(pCurrentNode, shorter);
+ break;
+ case 0:
+ (*pCurrentNode)->balance = -1;
+ *shorter = 0;
+ break;
+ case 1:
+ (*pCurrentNode)->balance = 0;
+ break;
+ }
+ }
+ return result;
+ }
+ else
+ {
+ if (tree->free_data_func != NULL)
+ {
+ tree->free_data_func((*pCurrentNode)->data);
+ }
+
+ leftsub = (*pCurrentNode)->left;
+ rightsub = (*pCurrentNode)->right;
+ if (leftsub == NULL)
+ {
+ free(*pCurrentNode);
+ *pCurrentNode = rightsub;
+ }
+ else if (rightsub == NULL)
+ {
+ free(*pCurrentNode);
+ *pCurrentNode = leftsub;
+ }
+ else
+ {
+ avl_tree_delete_loop(tree, &((*pCurrentNode)->left), \
+ target_data, shorter, *pCurrentNode);
+
+ if (*shorter != 0)
+ {
+ switch ((*pCurrentNode)->balance)
+ {
+ case -1:
+ (*pCurrentNode)->balance = 0;
+ break;
+ case 0:
+ (*pCurrentNode)->balance = 1;
+ *shorter = 0;
+ break;
+ case 1:
+ avlRightBalanceWhenDelete(pCurrentNode, shorter);
+ break;
+ }
+ }
+ return 1;
+ }
+
+ *shorter = 1;
+ return 1;
+ }
+}
+
+int avl_tree_delete(AVLTreeInfo *tree, void *data)
+{
+ int shorter;
+
+ if (tree->root == NULL)
+ {
+ return 0;
+ }
+
+ shorter = 0;
+ return avl_tree_delete_loop(tree, &(tree->root), data, &shorter, NULL);
+}
+
+static int avl_tree_walk_loop(DataOpFunc data_op_func, \
+ AVLTreeNode *pCurrentNode, void *args)
+{
+ int result;
+
+ if (pCurrentNode->left != NULL)
+ {
+ result = avl_tree_walk_loop(data_op_func, \
+ pCurrentNode->left, args);
+ if (result != 0)
+ {
+ return result;
+ }
+ }
+
+ if ((result=data_op_func(pCurrentNode->data, args)) != 0)
+ {
+ return result;
+ }
+
+ /*
+ if (pCurrentNode->balance >= -1 && pCurrentNode->balance <= 1)
+ {
+ //printf("==%d\n", pCurrentNode->balance);
+ }
+ else
+ {
+ printf("==bad %d!!!!!!!!!!!!\n", pCurrentNode->balance);
+ }
+ */
+
+ if (pCurrentNode->right != NULL)
+ {
+ result = avl_tree_walk_loop(data_op_func, \
+ pCurrentNode->right, args);
+ }
+
+ return result;
+}
+
+int avl_tree_walk(AVLTreeInfo *tree, DataOpFunc data_op_func, void *args)
+{
+ if (tree->root == NULL)
+ {
+ return 0;
+ }
+ else
+ {
+ return avl_tree_walk_loop(data_op_func, tree->root, args);
+ }
+}
+
+static void avl_tree_count_loop(AVLTreeNode *pCurrentNode, int *count)
+{
+ if (pCurrentNode->left != NULL)
+ {
+ avl_tree_count_loop(pCurrentNode->left, count);
+ }
+
+ (*count)++;
+
+ if (pCurrentNode->right != NULL)
+ {
+ avl_tree_count_loop(pCurrentNode->right, count);
+ }
+}
+
+int avl_tree_count(AVLTreeInfo *tree)
+{
+ int count;
+ if (tree->root == NULL)
+ {
+ return 0;
+ }
+
+ count = 0;
+ avl_tree_count_loop(tree->root, &count);
+ return count;
+}
+
+int avl_tree_depth(AVLTreeInfo *tree)
+{
+ int depth;
+ AVLTreeNode *pNode;
+
+ if (tree->root == NULL)
+ {
+ return 0;
+ }
+
+ depth = 0;
+ pNode = tree->root;
+ while (pNode != NULL)
+ {
+ if (pNode->balance == -1)
+ {
+ pNode = pNode->left;
+ }
+ else
+ {
+ pNode = pNode->right;
+ }
+ depth++;
+ }
+
+ return depth;
+}
+
+/*
+static void avl_tree_print_loop(AVLTreeNode *pCurrentNode)
+{
+ printf("%ld left: %ld, right: %ld, balance: %d\n", pCurrentNode->data,
+ pCurrentNode->left != NULL ? pCurrentNode->left->data : 0,
+ pCurrentNode->right != NULL ? pCurrentNode->right->data : 0,
+ pCurrentNode->balance);
+
+ if (pCurrentNode->left != NULL)
+ {
+ avl_tree_print_loop(pCurrentNode->left);
+ }
+
+ if (pCurrentNode->right != NULL)
+ {
+ avl_tree_print_loop(pCurrentNode->right);
+ }
+}
+
+void avl_tree_print(AVLTreeInfo *tree)
+{
+ if (tree->root == NULL)
+ {
+ return;
+ }
+
+ avl_tree_print_loop(tree->root);
+}
+*/
+
diff --git a/common/avl_tree.h b/common/avl_tree.h
new file mode 100644
index 0000000..1a9db45
--- /dev/null
+++ b/common/avl_tree.h
@@ -0,0 +1,47 @@
+
+#ifndef AVL_TREE_H
+#define AVL_TREE_H
+
+#include
+#include
+#include
+#include "common_define.h"
+
+typedef struct tagAVLTreeNode {
+ void *data;
+ struct tagAVLTreeNode *left;
+ struct tagAVLTreeNode *right;
+ byte balance;
+} AVLTreeNode;
+
+typedef int (*DataOpFunc) (void *data, void *args);
+
+typedef struct tagAVLTreeInfo {
+ AVLTreeNode *root;
+ FreeDataFunc free_data_func;
+ CompareFunc compare_func;
+} AVLTreeInfo;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int avl_tree_init(AVLTreeInfo *tree, FreeDataFunc free_data_func, \
+ CompareFunc compare_func);
+void avl_tree_destroy(AVLTreeInfo *tree);
+
+int avl_tree_insert(AVLTreeInfo *tree, void *data);
+int avl_tree_replace(AVLTreeInfo *tree, void *data);
+int avl_tree_delete(AVLTreeInfo *tree, void *data);
+void *avl_tree_find(AVLTreeInfo *tree, void *target_data);
+void *avl_tree_find_ge(AVLTreeInfo *tree, void *target_data);
+int avl_tree_walk(AVLTreeInfo *tree, DataOpFunc data_op_func, void *args);
+int avl_tree_count(AVLTreeInfo *tree);
+int avl_tree_depth(AVLTreeInfo *tree);
+//void avl_tree_print(AVLTreeInfo *tree);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/common/base64.c b/common/base64.c
new file mode 100644
index 0000000..1ff77bf
--- /dev/null
+++ b/common/base64.c
@@ -0,0 +1,393 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "base64.h"
+
+/**
+* Marker value for chars we just ignore, e.g. \n \r high ascii
+*/
+#define BASE64_IGNORE -1
+
+/**
+* Marker for = trailing pad
+*/
+#define BASE64_PAD -2
+
+/**
+* determines how long the lines are that are generated by encode.
+* Ignored by decode.
+* @param length 0 means no newlines inserted. Must be a multiple of 4.
+*/
+void base64_set_line_length(struct base64_context *context, const int length)
+{
+ context->line_length = (length / 4) * 4;
+}
+
+/**
+ * How lines are separated.
+ * Ignored by decode.
+ * @param context->line_separator may be "" but not null.
+ * Usually contains only a combination of chars \n and \r.
+ * Could be any chars not in set A-Z a-z 0-9 + /.
+*/
+void base64_set_line_separator(struct base64_context *context, \
+ const char *pLineSeparator)
+{
+ context->line_sep_len = snprintf(context->line_separator, \
+ sizeof(context->line_separator), "%s", pLineSeparator);
+}
+
+void base64_init_ex(struct base64_context *context, const int nLineLength, \
+ const unsigned char chPlus, const unsigned char chSplash, \
+ const unsigned char chPad)
+{
+ int i;
+
+ memset(context, 0, sizeof(struct base64_context));
+
+ context->line_length = nLineLength;
+ context->line_separator[0] = '\n';
+ context->line_separator[1] = '\0';
+ context->line_sep_len = 1;
+
+ // build translate valueToChar table only once.
+ // 0..25 -> 'A'..'Z'
+ for (i=0; i<=25; i++)
+ {
+ context->valueToChar[i] = (char)('A'+i);
+ }
+ // 26..51 -> 'a'..'z'
+ for (i=0; i<=25; i++ )
+ {
+ context->valueToChar[i+26] = (char)('a'+i);
+ }
+ // 52..61 -> '0'..'9'
+ for (i=0; i<=9; i++ )
+ {
+ context->valueToChar[i+52] = (char)('0'+i);
+ }
+ context->valueToChar[62] = chPlus;
+ context->valueToChar[63] = chSplash;
+
+ memset(context->charToValue, BASE64_IGNORE, sizeof(context->charToValue));
+ for (i=0; i<64; i++ )
+ {
+ context->charToValue[context->valueToChar[i]] = i;
+ }
+
+ context->pad_ch = chPad;
+ context->charToValue[chPad] = BASE64_PAD;
+}
+
+int base64_get_encode_length(struct base64_context *context, const int nSrcLen)
+{
+ // Each group or partial group of 3 bytes becomes four chars
+ // covered quotient
+ int outputLength;
+
+ outputLength = ((nSrcLen + 2) / 3) * 4;
+
+ // account for trailing newlines, on all but the very last line
+ if (context->line_length != 0)
+ {
+ int lines = (outputLength + context->line_length - 1) /
+ context->line_length - 1;
+ if ( lines > 0 )
+ {
+ outputLength += lines * context->line_sep_len;
+ }
+ }
+
+ return outputLength;
+}
+
+ /**
+ * Encode an arbitrary array of bytes as base64 printable ASCII.
+ * It will be broken into lines of 72 chars each. The last line is not
+ * terminated with a line separator.
+ * The output will always have an even multiple of data characters,
+ * exclusive of \n. It is padded out with =.
+ */
+char *base64_encode_ex(struct base64_context *context, const char *src, \
+ const int nSrcLen, char *dest, int *dest_len, const bool bPad)
+{
+ int linePos;
+ int leftover;
+ int combined;
+ char *pDest;
+ int c0, c1, c2, c3;
+ unsigned char *pRaw;
+ unsigned char *pEnd;
+ const char *ppSrcs[2];
+ int lens[2];
+ char szPad[3];
+ int k;
+ int loop;
+
+ if (nSrcLen <= 0)
+ {
+ *dest = '\0';
+ *dest_len = 0;
+ return dest;
+ }
+
+ linePos = 0;
+ lens[0] = (nSrcLen / 3) * 3;
+ lens[1] = 3;
+ leftover = nSrcLen - lens[0];
+ ppSrcs[0] = src;
+ ppSrcs[1] = szPad;
+
+ szPad[0] = szPad[1] = szPad[2] = '\0';
+ switch (leftover)
+ {
+ case 0:
+ default:
+ loop = 1;
+ break;
+ case 1:
+ loop = 2;
+ szPad[0] = src[nSrcLen-1];
+ break;
+ case 2:
+ loop = 2;
+ szPad[0] = src[nSrcLen-2];
+ szPad[1] = src[nSrcLen-1];
+ break;
+ }
+
+ pDest = dest;
+ for (k=0; k context->line_length)
+ {
+ if (context->line_length != 0)
+ {
+ memcpy(pDest, context->line_separator, context->line_sep_len);
+ pDest += context->line_sep_len;
+ }
+ linePos = 4;
+ }
+
+ // get next three bytes in unsigned form lined up,
+ // in big-endian order
+ combined = ((*pRaw) << 16) | ((*(pRaw+1)) << 8) | (*(pRaw+2));
+
+ // break those 24 bits into a 4 groups of 6 bits,
+ // working LSB to MSB.
+ c3 = combined & 0x3f;
+ combined >>= 6;
+ c2 = combined & 0x3f;
+ combined >>= 6;
+ c1 = combined & 0x3f;
+ combined >>= 6;
+ c0 = combined & 0x3f;
+
+ // Translate into the equivalent alpha character
+ // emitting them in big-endian order.
+ *pDest++ = context->valueToChar[c0];
+ *pDest++ = context->valueToChar[c1];
+ *pDest++ = context->valueToChar[c2];
+ *pDest++ = context->valueToChar[c3];
+ }
+ }
+
+ *pDest = '\0';
+ *dest_len = pDest - dest;
+
+ // deal with leftover bytes
+ switch (leftover)
+ {
+ case 0:
+ default:
+ // nothing to do
+ break;
+ case 1:
+ // One leftover byte generates xx==
+ if (bPad)
+ {
+ *(pDest-1) = context->pad_ch;
+ *(pDest-2) = context->pad_ch;
+ }
+ else
+ {
+ *(pDest-2) = '\0';
+ *dest_len -= 2;
+ }
+ break;
+ case 2:
+ // Two leftover bytes generates xxx=
+ if (bPad)
+ {
+ *(pDest-1) = context->pad_ch;
+ }
+ else
+ {
+ *(pDest-1) = '\0';
+ *dest_len -= 1;
+ }
+ break;
+ } // end switch;
+
+ return dest;
+}
+
+char *base64_decode_auto(struct base64_context *context, const char *src, \
+ const int nSrcLen, char *dest, int *dest_len)
+{
+ int nRemain;
+ int nPadLen;
+ int nNewLen;
+ char tmpBuff[256];
+ char *pBuff;
+
+ nRemain = nSrcLen % 4;
+ if (nRemain == 0)
+ {
+ return base64_decode(context, src, nSrcLen, dest, dest_len);
+ }
+
+ nPadLen = 4 - nRemain;
+ nNewLen = nSrcLen + nPadLen;
+ if (nNewLen <= sizeof(tmpBuff))
+ {
+ pBuff = tmpBuff;
+ }
+ else
+ {
+ pBuff = (char *)malloc(nNewLen);
+ if (pBuff == NULL)
+ {
+ fprintf(stderr, "Can't malloc %d bytes\n", \
+ nSrcLen + nPadLen + 1);
+ *dest_len = 0;
+ *dest = '\0';
+ return dest;
+ }
+ }
+
+ memcpy(pBuff, src, nSrcLen);
+ memset(pBuff + nSrcLen, context->pad_ch, nPadLen);
+
+ base64_decode(context, pBuff, nNewLen, dest, dest_len);
+
+ if (pBuff != tmpBuff)
+ {
+ free(pBuff);
+ }
+
+ return dest;
+}
+
+/**
+* decode a well-formed complete base64 string back into an array of bytes.
+* It must have an even multiple of 4 data characters (not counting \n),
+* padded out with = as needed.
+*/
+char *base64_decode(struct base64_context *context, const char *src, \
+ const int nSrcLen, char *dest, int *dest_len)
+{
+ // tracks where we are in a cycle of 4 input chars.
+ int cycle;
+
+ // where we combine 4 groups of 6 bits and take apart as 3 groups of 8.
+ int combined;
+
+ // will be an even multiple of 4 chars, plus some embedded \n
+ int dummies;
+ int value;
+ unsigned char *pSrc;
+ unsigned char *pSrcEnd;
+ char *pDest;
+
+ cycle = 0;
+ combined = 0;
+ dummies = 0;
+ pDest = dest;
+ pSrcEnd = (unsigned char *)src + nSrcLen;
+ for (pSrc=(unsigned char *)src; pSrccharToValue[*pSrc];
+ switch (value)
+ {
+ case BASE64_IGNORE:
+ // e.g. \n, just ignore it.
+ break;
+ case BASE64_PAD:
+ value = 0;
+ dummies++;
+ // fallthrough
+ default:
+ /* regular value character */
+ switch (cycle)
+ {
+ case 0:
+ combined = value;
+ cycle = 1;
+ break;
+ case 1:
+ combined <<= 6;
+ combined |= value;
+ cycle = 2;
+ break;
+ case 2:
+ combined <<= 6;
+ combined |= value;
+ cycle = 3;
+ break;
+ case 3:
+ combined <<= 6;
+ combined |= value;
+ // we have just completed a cycle of 4 chars.
+ // the four 6-bit values are in combined in big-endian order
+ // peel them off 8 bits at a time working lsb to msb
+ // to get our original 3 8-bit bytes back
+
+ *pDest++ = (char)(combined >> 16);
+ *pDest++ = (char)((combined & 0x0000FF00) >> 8);
+ *pDest++ = (char)(combined & 0x000000FF);
+
+ cycle = 0;
+ break;
+ }
+ break;
+ }
+ } // end for
+
+ if (cycle != 0)
+ {
+ *dest = '\0';
+ *dest_len = 0;
+ fprintf(stderr, "Input to decode not an even multiple of " \
+ "4 characters; pad with %c\n", context->pad_ch);
+ return dest;
+ }
+
+ *dest_len = (pDest - dest) - dummies;
+ *(dest + (*dest_len)) = '\0';
+
+ return dest;
+}
+
diff --git a/common/base64.h b/common/base64.h
new file mode 100644
index 0000000..6c85007
--- /dev/null
+++ b/common/base64.h
@@ -0,0 +1,134 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+//base64.h
+
+#ifndef _BASE64_H
+#define _BASE64_H
+
+#include "common_define.h"
+
+struct base64_context
+{
+ char line_separator[16];
+ int line_sep_len;
+
+ /**
+ * max chars per line, excluding line_separator. A multiple of 4.
+ */
+ int line_length;
+
+ /**
+ * letter of the alphabet used to encode binary values 0..63
+ */
+ unsigned char valueToChar[64];
+
+ /**
+ * binary value encoded by a given letter of the alphabet 0..63
+ */
+ int charToValue[256];
+ int pad_ch;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** use stardand base64 charset
+ */
+#define base64_init(context, nLineLength) \
+ base64_init_ex(context, nLineLength, '+', '/', '=')
+
+
+/** stardand base64 encode
+ */
+#define base64_encode(context, src, nSrcLen, dest, dest_len) \
+ base64_encode_ex(context, src, nSrcLen, dest, dest_len, true)
+
+/** base64 init function, before use base64 function, you should call
+ * this function
+ * parameters:
+ * context: the base64 context
+ * nLineLength: length of a line, 0 for never add line seperator
+ * chPlus: specify a char for base64 char plus (+)
+ * chSplash: specify a char for base64 char splash (/)
+ * chPad: specify a char for base64 padding char =
+ * return: none
+ */
+void base64_init_ex(struct base64_context *context, const int nLineLength, \
+ const unsigned char chPlus, const unsigned char chSplash, \
+ const unsigned char chPad);
+
+/** calculate the encoded length of the source buffer
+ * parameters:
+ * context: the base64 context
+ * nSrcLen: the source buffer length
+ * return: the encoded length of the source buffer
+ */
+int base64_get_encode_length(struct base64_context *context, const int nSrcLen);
+
+/** base64 encode buffer
+ * parameters:
+ * context: the base64 context
+ * src: the source buffer
+ * nSrcLen: the source buffer length
+ * dest: the dest buffer
+ * dest_len: return dest buffer length
+ * bPad: if padding
+ * return: the encoded buffer
+ */
+char *base64_encode_ex(struct base64_context *context, const char *src, \
+ const int nSrcLen, char *dest, int *dest_len, const bool bPad);
+
+/** base64 decode buffer, work only with padding source string
+ * parameters:
+ * context: the base64 context
+ * src: the source buffer with padding
+ * nSrcLen: the source buffer length
+ * dest: the dest buffer
+ * dest_len: return dest buffer length
+ * return: the decoded buffer
+ */
+char *base64_decode(struct base64_context *context, const char *src, \
+ const int nSrcLen, char *dest, int *dest_len);
+
+/** base64 decode buffer, can work with no padding source string
+ * parameters:
+ * context: the base64 context
+ * src: the source buffer with padding or no padding
+ * nSrcLen: the source buffer length
+ * dest: the dest buffer
+ * dest_len: return dest buffer length
+ * return: the decoded buffer
+ */
+char *base64_decode_auto(struct base64_context *context, const char *src, \
+ const int nSrcLen, char *dest, int *dest_len);
+
+/** set line separator string, such as \n or \r\n
+ * parameters:
+ * context: the base64 context
+ * pLineSeparator: the line separator string
+ * return: none
+ */
+void base64_set_line_separator(struct base64_context *context, \
+ const char *pLineSeparator);
+
+/** set line length, 0 for never add line separators
+ * parameters:
+ * context: the base64 context
+ * length: the line length
+ * return: none
+ */
+void base64_set_line_length(struct base64_context *context, const int length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/common/chain.c b/common/chain.c
new file mode 100644
index 0000000..a23e5ab
--- /dev/null
+++ b/common/chain.c
@@ -0,0 +1,325 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include
+#include "chain.h"
+//#include "use_mmalloc.h"
+
+void chain_init(ChainList *pList, const int type, FreeDataFunc freeDataFunc, \
+ CompareFunc compareFunc)
+{
+ if (pList == NULL)
+ {
+ return;
+ }
+
+ pList->head = NULL;
+ pList->tail = NULL;
+ pList->type = type;
+ pList->freeDataFunc = freeDataFunc;
+ pList->compareFunc = compareFunc;
+
+ return;
+}
+
+void chain_destroy(ChainList *pList)
+{
+ ChainNode *pNode;
+ ChainNode *pDeleted;
+ if (pList == NULL || pList->head == NULL)
+ {
+ return;
+ }
+
+ pNode = pList->head;
+ while (pNode != NULL)
+ {
+ pDeleted = pNode;
+ pNode = pNode->next;
+
+ freeChainNode(pList, pDeleted);
+ }
+
+ pList->head = pList->tail = NULL;
+}
+
+void freeChainNode(ChainList *pList, ChainNode *pChainNode)
+{
+ if (pList->freeDataFunc != NULL)
+ {
+ pList->freeDataFunc(pChainNode->data);
+ }
+
+ free(pChainNode);
+}
+
+int insertNodePrior(ChainList *pList, void *data)
+{
+ ChainNode *pNode;
+ if (pList == NULL)
+ {
+ return EINVAL;
+ }
+
+ pNode = (ChainNode *)malloc(sizeof(ChainNode));
+ if (pNode == NULL)
+ {
+ return ENOMEM;
+ }
+
+ pNode->data = data;
+ pNode->next = pList->head;
+
+ pList->head = pNode;
+ if (pList->tail == NULL)
+ {
+ pList->tail = pNode;
+ }
+
+ return 0;
+}
+
+int appendNode(ChainList *pList, void *data)
+{
+ ChainNode *pNode;
+ if (pList == NULL)
+ {
+ return EINVAL;
+ }
+
+ pNode = (ChainNode *)malloc(sizeof(ChainNode));
+ if (pNode == NULL)
+ {
+ return ENOMEM;
+ }
+
+ pNode->data = data;
+ pNode->next = NULL;
+
+ if (pList->tail != NULL)
+ {
+ pList->tail->next = pNode;
+ }
+
+ pList->tail = pNode;
+ if (pList->head == NULL)
+ {
+ pList->head = pNode;
+ }
+
+ return 0;
+}
+
+int insertNodeAsc(ChainList *pList, void *data)
+{
+ ChainNode *pNew;
+ ChainNode *pNode;
+ ChainNode *pPrevious;
+ if (pList == NULL || pList->compareFunc == NULL)
+ {
+ return EINVAL;
+ }
+
+ pNew = (ChainNode *)malloc(sizeof(ChainNode));
+ if (pNew == NULL)
+ {
+ return ENOMEM;
+ }
+
+ pNew->data = data;
+
+ pPrevious = NULL;
+ pNode = pList->head;
+ while (pNode != NULL && pList->compareFunc(pNode->data, data) < 0)
+ {
+ pPrevious = pNode;
+ pNode = pNode->next;
+ }
+
+ pNew->next = pNode;
+ if (pPrevious == NULL)
+ {
+ pList->head = pNew;
+ }
+ else
+ {
+ pPrevious->next = pNew;
+ }
+
+ if (pNode == NULL)
+ {
+ pList->tail = pNew;
+ }
+
+ return 0;
+}
+
+int addNode(ChainList *pList, void *data)
+{
+ if (pList->type == CHAIN_TYPE_INSERT)
+ {
+ return insertNodePrior(pList, data);
+ }
+ else if (pList->type == CHAIN_TYPE_APPEND)
+ {
+ return appendNode(pList, data);
+ }
+ else
+ {
+ return insertNodeAsc(pList, data);
+ }
+}
+
+void deleteNodeEx(ChainList *pList, ChainNode *pPreviousNode, \
+ ChainNode *pDeletedNode)
+{
+ if (pDeletedNode == pList->head)
+ {
+ pList->head = pDeletedNode->next;
+ }
+ else
+ {
+ pPreviousNode->next = pDeletedNode->next;
+ }
+
+ if (pDeletedNode == pList->tail)
+ {
+ pList->tail = pPreviousNode;
+ }
+
+ freeChainNode(pList, pDeletedNode);
+}
+
+void deleteToNodePrevious(ChainList *pList, ChainNode *pPreviousNode, \
+ ChainNode *pDeletedNext)
+{
+ ChainNode *pNode;
+ ChainNode *pDeletedNode;
+
+ if (pPreviousNode == NULL)
+ {
+ pNode = pList->head;
+ pList->head = pDeletedNext;
+ }
+ else
+ {
+ pNode = pPreviousNode->next;
+ pPreviousNode->next = pDeletedNext;
+ }
+
+ while (pNode != NULL && pNode != pDeletedNext)
+ {
+ pDeletedNode = pNode;
+ pNode = pNode->next;
+ freeChainNode(pList, pDeletedNode);
+ }
+
+ if (pDeletedNext == NULL)
+ {
+ pList->tail = pPreviousNode;
+ }
+}
+
+void *chain_pop_head(ChainList *pList)
+{
+ ChainNode *pDeletedNode;
+ void *data;
+
+ pDeletedNode = pList->head;
+ if (pDeletedNode == NULL)
+ {
+ return NULL;
+ }
+
+ pList->head = pDeletedNode->next;
+ if (pList->head == NULL)
+ {
+ pList->tail = NULL;
+ }
+
+ data = pDeletedNode->data;
+ free(pDeletedNode);
+
+ return data;
+}
+
+int chain_count(ChainList *pList)
+{
+ ChainNode *pNode;
+ int count;
+
+ count = 0;
+ pNode = pList->head;
+ while (pNode != NULL)
+ {
+ count++;
+ pNode = pNode->next;
+ }
+
+ return count;
+}
+
+int deleteNode(ChainList *pList, void *data, bool bDeleteAll)
+{
+ ChainNode *pNode;
+ ChainNode *pPrevious;
+ ChainNode *pDeleted;
+ int nCount;
+ int nCompareRes;
+
+ if (pList == NULL || pList->compareFunc == NULL)
+ {
+ return -EINVAL;
+ }
+
+ nCount = 0;
+ pPrevious = NULL;
+ pNode = pList->head;
+ while (pNode != NULL)
+ {
+ nCompareRes = pList->compareFunc(pNode->data, data);
+ if (nCompareRes == 0)
+ {
+ pDeleted = pNode;
+ pNode = pNode->next;
+
+ deleteNodeEx(pList, pPrevious, pDeleted);
+ nCount++;
+
+ if (!bDeleteAll)
+ {
+ break;
+ }
+
+ continue;
+ }
+ else if(nCompareRes > 0 && pList->type == CHAIN_TYPE_SORTED)
+ {
+ break;
+ }
+
+ pPrevious = pNode;
+ pNode = pNode->next;
+ }
+
+ return nCount;
+}
+
+int deleteOne(ChainList *pList, void *data)
+{
+ return deleteNode(pList, data, false);
+}
+
+int deleteAll(ChainList *pList, void *data)
+{
+ return deleteNode(pList, data, true);
+}
+
diff --git a/common/chain.h b/common/chain.h
new file mode 100644
index 0000000..9d18a3d
--- /dev/null
+++ b/common/chain.h
@@ -0,0 +1,148 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#ifndef CHAIN_H
+#define CHAIN_H
+
+#include "common_define.h"
+
+#define CHAIN_TYPE_INSERT 0 //insert new node before head
+#define CHAIN_TYPE_APPEND 1 //insert new node after tail
+#define CHAIN_TYPE_SORTED 2 //sorted chain
+
+typedef struct tagChainNode
+{
+ void *data;
+ struct tagChainNode *next;
+} ChainNode;
+
+typedef struct
+{
+ int type;
+ ChainNode *head;
+ ChainNode *tail;
+ FreeDataFunc freeDataFunc;
+ CompareFunc compareFunc;
+} ChainList;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** chain init function
+ * parameters:
+ * pList: the chain list
+ * type: chain type, value is one of following:
+ * CHAIN_TYPE_INSERT: insert new node before head
+ * CHAIN_TYPE_APPEND: insert new node after tail
+ * CHAIN_TYPE_SORTED: sorted chain
+ * freeDataFunc: free data function pointer, can be NULL
+ * compareFunc: compare data function pointer, can be NULL,
+ * must set when type is CHAIN_TYPE_SORTED
+ * return: none
+ */
+void chain_init(ChainList *pList, const int type, FreeDataFunc freeDataFunc, \
+ CompareFunc compareFunc);
+
+/** destroy chain
+ * parameters:
+ * pList: the chain list
+ * return: none
+ */
+void chain_destroy(ChainList *pList);
+
+
+/** get the chain node count
+ * parameters:
+ * pList: the chain list
+ * return: chain node count
+ */
+int chain_count(ChainList *pList);
+
+/** add a new node to the chain
+ * parameters:
+ * pList: the chain list
+ * data: the data to add
+ * return: error no, 0 for success, != 0 fail
+ */
+int addNode(ChainList *pList, void *data);
+
+/** free the chain node
+ * parameters:
+ * pList: the chain list
+ * pChainNode: the chain node to free
+ * return: none
+ */
+void freeChainNode(ChainList *pList, ChainNode *pChainNode);
+
+/** delete the chain node
+ * parameters:
+ * pList: the chain list
+ * pPreviousNode: the previous chain node
+ * pDeletedNode: the chain node to delete
+ * return: none
+ */
+void deleteNodeEx(ChainList *pList, ChainNode *pPreviousNode, \
+ ChainNode *pDeletedNode);
+
+/** delete the chain nodes from pPreviousNode->next to pDeletedNext
+ * (not including pDeletedNext)
+ * parameters:
+ * pList: the chain list
+ * pPreviousNode: the previous chain node, delete from pPreviousNode->next
+ * pDeletedNext: the chain node after the deleted node
+ * return: none
+ */
+void deleteToNodePrevious(ChainList *pList, ChainNode *pPreviousNode, \
+ ChainNode *pDeletedNext);
+
+/** delete the chain node using data compare function
+ * parameters:
+ * pList: the chain list
+ * data: the first node whose data equals this will be deleted
+ * return: delete chain node count, < 0 fail
+ */
+int deleteOne(ChainList *pList, void *data);
+
+/** delete all chain nodes using data compare function
+ * parameters:
+ * pList: the chain list
+ * data: the node whose data equals this will be deleted
+ * return: delete chain node count, < 0 fail
+ */
+int deleteAll(ChainList *pList, void *data);
+
+/** pop up a chain nodes from chain head
+ * parameters:
+ * pList: the chain list
+ * return: the head node, return NULL when the chain is empty
+ */
+void *chain_pop_head(ChainList *pList);
+
+/** add a chain nodes before the chain head
+ * parameters:
+ * pList: the chain list
+ * data: the node to insert
+ * return: error no, 0 for success, != 0 fail
+ */
+int insertNodePrior(ChainList *pList, void *data);
+
+/** add a chain nodes after the chain tail
+ * parameters:
+ * pList: the chain list
+ * data: the node to insert
+ * return: error no, 0 for success, != 0 fail
+ */
+int appendNode(ChainList *pList, void *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/common/common_define.h b/common/common_define.h
new file mode 100644
index 0000000..5cb8128
--- /dev/null
+++ b/common/common_define.h
@@ -0,0 +1,169 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+//common_define.h
+
+#ifndef _COMMON_DEFINE_H_
+#define _COMMON_DEFINE_H_
+
+#include
+#include
+
+#ifdef WIN32
+
+#include
+#include
+typedef UINT in_addr_t;
+#define FILE_SEPERATOR "\\"
+#define THREAD_ENTRANCE_FUNC_DECLARE DWORD WINAPI
+#define THREAD_RETURN_VALUE 0
+typedef DWORD (WINAPI *ThreadEntranceFunc)(LPVOID lpThreadParameter);
+#else
+
+#include
+#include
+#include
+#include
+#include
+#include
+#define FILE_SEPERATOR "/"
+typedef int SOCKET;
+#define closesocket close
+#define INVALID_SOCKET -1
+#define THREAD_ENTRANCE_FUNC_DECLARE void *
+typedef void *LPVOID;
+#define THREAD_RETURN_VALUE NULL
+typedef void * (*ThreadEntranceFunc)(LPVOID lpThreadParameter);
+
+#endif
+
+#ifndef WIN32
+extern int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
+#endif
+
+#ifdef OS_LINUX
+#define PTHREAD_MUTEX_ERRORCHECK PTHREAD_MUTEX_ERRORCHECK_NP
+#endif
+
+#include "_os_bits.h"
+
+#ifdef OS_BITS
+ #if OS_BITS == 64
+ #define INT64_PRINTF_FORMAT "%ld"
+ #else
+ #define INT64_PRINTF_FORMAT "%lld"
+ #endif
+#else
+ #define INT64_PRINTF_FORMAT "%lld"
+#endif
+
+#ifdef OFF_BITS
+ #if OFF_BITS == 64
+ #define OFF_PRINTF_FORMAT INT64_PRINTF_FORMAT
+ #else
+ #define OFF_PRINTF_FORMAT "%d"
+ #endif
+#else
+ #define OFF_PRINTF_FORMAT INT64_PRINTF_FORMAT
+#endif
+
+#ifndef WIN32
+#define USE_SENDFILE
+#endif
+
+#define MAX_PATH_SIZE 256
+#define LOG_FILE_DIR "logs"
+#define CONF_FILE_DIR "conf"
+#define DEFAULT_CONNECT_TIMEOUT 30
+#define DEFAULT_NETWORK_TIMEOUT 30
+#define DEFAULT_MAX_CONNECTONS 256
+#define DEFAULT_WORK_THREADS 4
+#define SYNC_LOG_BUFF_DEF_INTERVAL 10
+#define TIME_NONE -1
+
+#define IP_ADDRESS_SIZE 16
+#define INFINITE_FILE_SIZE (256 * 1024LL * 1024 * 1024 * 1024 * 1024LL)
+
+#ifndef __cplusplus
+#ifndef true
+typedef char bool;
+#define true 1
+#define false 0
+#endif
+#endif
+
+#ifndef byte
+#define byte signed char
+#endif
+
+#ifndef ubyte
+#define ubyte unsigned char
+#endif
+
+#ifndef WIN32
+#ifndef INADDR_NONE
+#define INADDR_NONE ((in_addr_t) 0xffffffff)
+#endif
+#endif
+
+#ifndef ECANCELED
+#define ECANCELED 125
+#endif
+
+#ifndef ENONET
+#define ENONET 64 /* Machine is not on the network */
+#endif
+
+#define IS_UPPER_HEX(ch) ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F'))
+#define STRERROR(no) (strerror(no) != NULL ? strerror(no) : "Unkown error")
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ byte hour;
+ byte minute;
+} TimeInfo;
+
+typedef struct
+{
+ char major;
+ char minor;
+} Version;
+
+typedef struct
+{
+ char *key;
+ char *value;
+} KeyValuePair;
+
+typedef struct
+{
+ char *buff;
+ int alloc_size;
+ int length;
+} BufferInfo;
+
+typedef void (*FreeDataFunc)(void *ptr);
+typedef int (*CompareFunc)(void *p1, void *p2);
+typedef void* (*MallocFunc)(size_t size);
+
+#define TO_UPPERCASE(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 32 : c)
+#define MEM_ALIGN(x) (((x) + 7) & (~7))
+
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/common/connection_pool.c b/common/connection_pool.c
new file mode 100644
index 0000000..e272862
--- /dev/null
+++ b/common/connection_pool.c
@@ -0,0 +1,346 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+#include
+#include
+#include
+#include "logger.h"
+#include "sockopt.h"
+#include "shared_func.h"
+#include "sched_thread.h"
+#include "connection_pool.h"
+
+int conn_pool_init(ConnectionPool *cp, int connect_timeout, \
+ const int max_count_per_entry, const int max_idle_time)
+{
+ int result;
+
+ if ((result=init_pthread_lock(&cp->lock)) != 0)
+ {
+ return result;
+ }
+ cp->connect_timeout = connect_timeout;
+ cp->max_count_per_entry = max_count_per_entry;
+ cp->max_idle_time = max_idle_time;
+
+ return hash_init(&(cp->hash_array), simple_hash, 1024, 0.75);
+}
+
+void conn_pool_destroy(ConnectionPool *cp)
+{
+ pthread_mutex_lock(&cp->lock);
+ hash_destroy(&(cp->hash_array));
+ pthread_mutex_unlock(&cp->lock);
+
+ pthread_mutex_destroy(&cp->lock);
+}
+
+void conn_pool_disconnect_server(ConnectionInfo *pConnection)
+{
+ if (pConnection->sock >= 0)
+ {
+ close(pConnection->sock);
+ pConnection->sock = -1;
+ }
+}
+
+int conn_pool_connect_server(ConnectionInfo *pConnection, \
+ const int connect_timeout)
+{
+ int result;
+
+ if (pConnection->sock >= 0)
+ {
+ close(pConnection->sock);
+ }
+
+ pConnection->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if(pConnection->sock < 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "socket create failed, errno: %d, " \
+ "error info: %s", __LINE__, errno, STRERROR(errno));
+ return errno != 0 ? errno : EPERM;
+ }
+
+ if ((result=tcpsetnonblockopt(pConnection->sock)) != 0)
+ {
+ close(pConnection->sock);
+ pConnection->sock = -1;
+ return result;
+ }
+
+ if ((result=connectserverbyip_nb(pConnection->sock, \
+ pConnection->ip_addr, pConnection->port, \
+ connect_timeout)) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "connect to %s:%d fail, errno: %d, " \
+ "error info: %s", __LINE__, pConnection->ip_addr, \
+ pConnection->port, result, STRERROR(result));
+
+ close(pConnection->sock);
+ pConnection->sock = -1;
+ return result;
+ }
+
+ return 0;
+}
+
+static int conn_pool_get_key(const ConnectionInfo *conn, char *key, int *key_len)
+{
+ struct in_addr sin_addr;
+
+ if (inet_aton(conn->ip_addr, &sin_addr) == 0)
+ {
+ *key_len = 0;
+ return EINVAL;
+ }
+
+ int2buff(sin_addr.s_addr, key);
+ *key_len = 4 + sprintf(key + 4, "%d", conn->port);
+
+ return 0;
+}
+
+ConnectionInfo *conn_pool_get_connection(ConnectionPool *cp,
+ const ConnectionInfo *conn, int *err_no)
+{
+ char key[32];
+ int key_len;
+ int bytes;
+ char *p;
+ ConnectionManager *cm;
+ ConnectionNode *node;
+ ConnectionInfo *ci;
+ time_t current_time;
+
+ *err_no = conn_pool_get_key(conn, key, &key_len);
+ if (*err_no != 0)
+ {
+ return NULL;
+ }
+
+ pthread_mutex_lock(&cp->lock);
+ cm = (ConnectionManager *)hash_find(&cp->hash_array, key, key_len);
+ if (cm == NULL)
+ {
+ cm = (ConnectionManager *)malloc(sizeof(ConnectionManager));
+ if (cm == NULL)
+ {
+ *err_no = errno != 0 ? errno : ENOMEM;
+ logError("file: "__FILE__", line: %d, " \
+ "malloc %d bytes fail, errno: %d, " \
+ "error info: %s", __LINE__, \
+ (int)sizeof(ConnectionManager), \
+ *err_no, STRERROR(*err_no));
+ pthread_mutex_unlock(&cp->lock);
+ return NULL;
+ }
+
+ cm->head = NULL;
+ cm->total_count = 0;
+ cm->free_count = 0;
+ if ((*err_no=init_pthread_lock(&cm->lock)) != 0)
+ {
+ pthread_mutex_unlock(&cp->lock);
+ return NULL;
+ }
+ hash_insert(&cp->hash_array, key, key_len, cm);
+ }
+ pthread_mutex_unlock(&cp->lock);
+
+ current_time = get_current_time();
+ pthread_mutex_lock(&cm->lock);
+ while (1)
+ {
+ if (cm->head == NULL)
+ {
+ if ((cp->max_count_per_entry > 0) &&
+ (cm->total_count >= cp->max_count_per_entry))
+ {
+ *err_no = ENOSPC;
+ logError("file: "__FILE__", line: %d, " \
+ "connections: %d of server %s:%d " \
+ "exceed limit: %d", __LINE__, \
+ cm->total_count, conn->ip_addr, \
+ conn->port, cp->max_count_per_entry);
+ pthread_mutex_unlock(&cm->lock);
+ return NULL;
+ }
+
+ bytes = sizeof(ConnectionInfo) + sizeof(ConnectionNode);
+ p = (char *)malloc(bytes);
+ if (p == NULL)
+ {
+ *err_no = errno != 0 ? errno : ENOMEM;
+ logError("file: "__FILE__", line: %d, " \
+ "malloc %d bytes fail, errno: %d, " \
+ "error info: %s", __LINE__, \
+ bytes, *err_no, STRERROR(*err_no));
+ pthread_mutex_unlock(&cm->lock);
+ return NULL;
+ }
+
+ node = (ConnectionNode *)(p + sizeof(ConnectionInfo));
+ node->conn = (ConnectionInfo *)p;
+ node->manager = cm;
+ node->next = NULL;
+ node->atime = 0;
+
+ cm->total_count++;
+ pthread_mutex_unlock(&cm->lock);
+
+ memcpy(node->conn, conn, sizeof(ConnectionInfo));
+ node->conn->sock = -1;
+ *err_no = conn_pool_connect_server(node->conn, \
+ cp->connect_timeout);
+ if (*err_no != 0)
+ {
+ free(p);
+ return NULL;
+ }
+
+ logDebug("file: "__FILE__", line: %d, " \
+ "server %s:%d, new connection: %d, " \
+ "total_count: %d, free_count: %d", \
+ __LINE__, conn->ip_addr, conn->port, \
+ node->conn->sock, cm->total_count, \
+ cm->free_count);
+ return node->conn;
+ }
+ else
+ {
+ node = cm->head;
+ ci = node->conn;
+ cm->head = node->next;
+ cm->free_count--;
+
+ if (current_time - node->atime > cp->max_idle_time)
+ {
+ cm->total_count--;
+
+ logDebug("file: "__FILE__", line: %d, " \
+ "server %s:%d, connection: %d idle " \
+ "time: %d exceeds max idle time: %d, "\
+ "total_count: %d, free_count: %d", \
+ __LINE__, conn->ip_addr, conn->port, \
+ ci->sock, \
+ (int)(current_time - node->atime), \
+ cp->max_idle_time, cm->total_count, \
+ cm->free_count);
+
+ conn_pool_disconnect_server(ci);
+ free(ci);
+ continue;
+ }
+
+ pthread_mutex_unlock(&cm->lock);
+ logDebug("file: "__FILE__", line: %d, " \
+ "server %s:%d, reuse connection: %d, " \
+ "total_count: %d, free_count: %d",
+ __LINE__, conn->ip_addr, conn->port,
+ ci->sock, cm->total_count, cm->free_count);
+ return ci;
+ }
+ }
+}
+
+int conn_pool_close_connection_ex(ConnectionPool *cp, ConnectionInfo *conn,
+ const bool bForce)
+{
+ char key[32];
+ int result;
+ int key_len;
+ ConnectionManager *cm;
+ ConnectionNode *node;
+
+ result = conn_pool_get_key(conn, key, &key_len);
+ if (result != 0)
+ {
+ return result;
+ }
+
+ pthread_mutex_lock(&cp->lock);
+ cm = (ConnectionManager *)hash_find(&cp->hash_array, key, key_len);
+ pthread_mutex_unlock(&cp->lock);
+ if (cm == NULL)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "hash entry of server %s:%d not exist", __LINE__, \
+ conn->ip_addr, conn->port);
+ return ENOENT;
+ }
+
+ node = (ConnectionNode *)(((char *)conn) + sizeof(ConnectionInfo));
+ if (node->manager != cm)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "manager of server entry %s:%d is invalid!", \
+ __LINE__, conn->ip_addr, conn->port);
+ return EINVAL;
+ }
+
+ pthread_mutex_lock(&cm->lock);
+ if (bForce)
+ {
+ cm->total_count--;
+
+ logDebug("file: "__FILE__", line: %d, " \
+ "server %s:%d, release connection: %d, " \
+ "total_count: %d, free_count: %d",
+ __LINE__, conn->ip_addr, conn->port,
+ conn->sock, cm->total_count, cm->free_count);
+
+ conn_pool_disconnect_server(conn);
+ free(conn);
+ }
+ else
+ {
+ node->atime = get_current_time();
+ node->next = cm->head;
+ cm->head = node;
+ cm->free_count++;
+
+ logDebug("file: "__FILE__", line: %d, " \
+ "server %s:%d, free connection: %d, " \
+ "total_count: %d, free_count: %d",
+ __LINE__, conn->ip_addr, conn->port,
+ conn->sock, cm->total_count, cm->free_count);
+ }
+ pthread_mutex_unlock(&cm->lock);
+
+ return 0;
+}
+
+static int _conn_count_walk(const int index, const HashData *data, void *args)
+{
+ int *count;
+ ConnectionManager *cm;
+ ConnectionNode *node;
+
+ count = (int *)args;
+ cm = (ConnectionManager *)data->value;
+ node = cm->head;
+ while (node != NULL)
+ {
+ (*count)++;
+ node = node->next;
+ }
+
+ return 0;
+}
+
+int conn_pool_get_connection_count(ConnectionPool *cp)
+{
+ int count;
+ count = 0;
+ hash_walk(&cp->hash_array, _conn_count_walk, &count);
+ return count;
+}
+
diff --git a/common/connection_pool.h b/common/connection_pool.h
new file mode 100644
index 0000000..265693f
--- /dev/null
+++ b/common/connection_pool.h
@@ -0,0 +1,86 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+//connection_pool.h
+
+#ifndef _CONNECTION_POOL_H
+#define _CONNECTION_POOL_H
+
+#include
+#include
+#include
+#include
+#include "common_define.h"
+#include "pthread_func.h"
+#include "hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ int sock;
+ int port;
+ char ip_addr[IP_ADDRESS_SIZE];
+} ConnectionInfo;
+
+struct tagConnectionManager;
+
+typedef struct tagConnectionNode {
+ ConnectionInfo *conn;
+ struct tagConnectionManager *manager;
+ struct tagConnectionNode *next;
+ time_t atime; //last access time
+} ConnectionNode;
+
+typedef struct tagConnectionManager {
+ ConnectionNode *head;
+ int total_count; //total connections
+ int free_count; //free connections
+ pthread_mutex_t lock;
+} ConnectionManager;
+
+typedef struct tagConnectionPool {
+ HashArray hash_array; //key is ip:port, value is ConnectionManager
+ pthread_mutex_t lock;
+ int connect_timeout;
+ int max_count_per_entry; //0 means no limit
+
+ /*
+ connections whose the idle time exceeds this time will be closed
+ */
+ int max_idle_time;
+} ConnectionPool;
+
+int conn_pool_init(ConnectionPool *cp, int connect_timeout, \
+ const int max_count_per_entry, const int max_idle_time);
+void conn_pool_destroy(ConnectionPool *cp);
+
+ConnectionInfo *conn_pool_get_connection(ConnectionPool *cp,
+ const ConnectionInfo *conn, int *err_no);
+
+#define conn_pool_close_connection(cp, conn) \
+ conn_pool_close_connection_ex(cp, conn, false)
+
+int conn_pool_close_connection_ex(ConnectionPool *cp, ConnectionInfo *conn,
+ const bool bForce);
+
+void conn_pool_disconnect_server(ConnectionInfo *pConnection);
+
+int conn_pool_connect_server(ConnectionInfo *pConnection, \
+ const int connect_timeout);
+
+int conn_pool_get_connection_count(ConnectionPool *cp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/common/fast_mblock.c b/common/fast_mblock.c
new file mode 100644
index 0000000..488a084
--- /dev/null
+++ b/common/fast_mblock.c
@@ -0,0 +1,225 @@
+//fast_mblock.c
+
+#include
+#include
+#include
+#include "fast_mblock.h"
+#include "logger.h"
+#include "shared_func.h"
+#include "pthread_func.h"
+
+int fast_mblock_init(struct fast_mblock_man *mblock, const int element_size, \
+ const int alloc_elements_once)
+{
+ int result;
+
+ if (element_size <= 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "invalid block size: %d", \
+ __LINE__, element_size);
+ return EINVAL;
+ }
+
+ mblock->element_size = element_size;
+ if (alloc_elements_once > 0)
+ {
+ mblock->alloc_elements_once = alloc_elements_once;
+ }
+ else
+ {
+ int block_size;
+ block_size = MEM_ALIGN(sizeof(struct fast_mblock_node) \
+ + element_size);
+ mblock->alloc_elements_once = (1024 * 1024) / block_size;
+ }
+
+ if ((result=init_pthread_lock(&(mblock->lock))) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "init_pthread_lock fail, errno: %d, error info: %s", \
+ __LINE__, result, STRERROR(result));
+ return result;
+ }
+
+ mblock->malloc_chain_head = NULL;
+ mblock->free_chain_head = NULL;
+
+ return 0;
+}
+
+static int fast_mblock_prealloc(struct fast_mblock_man *mblock)
+{
+ struct fast_mblock_node *pNode;
+ struct fast_mblock_malloc *pMallocNode;
+ char *pNew;
+ char *pTrunkStart;
+ char *p;
+ char *pLast;
+ int block_size;
+ int alloc_size;
+
+ block_size = MEM_ALIGN(sizeof(struct fast_mblock_node) + \
+ mblock->element_size);
+ alloc_size = sizeof(struct fast_mblock_malloc) + block_size * \
+ mblock->alloc_elements_once;
+
+ pNew = (char *)malloc(alloc_size);
+ if (pNew == NULL)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "malloc %d bytes fail, " \
+ "errno: %d, error info: %s", \
+ __LINE__, alloc_size, errno, STRERROR(errno));
+ return errno != 0 ? errno : ENOMEM;
+ }
+ memset(pNew, 0, alloc_size);
+
+ pMallocNode = (struct fast_mblock_malloc *)pNew;
+
+ pTrunkStart = pNew + sizeof(struct fast_mblock_malloc);
+ pLast = pNew + (alloc_size - block_size);
+ for (p=pTrunkStart; pnext = (struct fast_mblock_node *)(p + block_size);
+ }
+
+ ((struct fast_mblock_node *)pLast)->next = NULL;
+ mblock->free_chain_head = (struct fast_mblock_node *)pTrunkStart;
+
+ pMallocNode->next = mblock->malloc_chain_head;
+ mblock->malloc_chain_head = pMallocNode;
+
+ return 0;
+}
+
+void fast_mblock_destroy(struct fast_mblock_man *mblock)
+{
+ struct fast_mblock_malloc *pMallocNode;
+ struct fast_mblock_malloc *pMallocTmp;
+
+ if (mblock->malloc_chain_head == NULL)
+ {
+ return;
+ }
+
+ pMallocNode = mblock->malloc_chain_head;
+ while (pMallocNode != NULL)
+ {
+ pMallocTmp = pMallocNode;
+ pMallocNode = pMallocNode->next;
+
+ free(pMallocTmp);
+ }
+ mblock->malloc_chain_head = NULL;
+ mblock->free_chain_head = NULL;
+
+ pthread_mutex_destroy(&(mblock->lock));
+}
+
+struct fast_mblock_node *fast_mblock_alloc(struct fast_mblock_man *mblock)
+{
+ struct fast_mblock_node *pNode;
+ int result;
+
+ if ((result=pthread_mutex_lock(&(mblock->lock))) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "call pthread_mutex_lock fail, " \
+ "errno: %d, error info: %s", \
+ __LINE__, result, STRERROR(result));
+ return NULL;
+ }
+
+ if (mblock->free_chain_head != NULL)
+ {
+ pNode = mblock->free_chain_head;
+ mblock->free_chain_head = pNode->next;
+ }
+ else
+ {
+ if ((result=fast_mblock_prealloc(mblock)) == 0)
+ {
+ pNode = mblock->free_chain_head;
+ mblock->free_chain_head = pNode->next;
+ }
+ else
+ {
+ pNode = NULL;
+ }
+ }
+
+ if ((result=pthread_mutex_unlock(&(mblock->lock))) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "call pthread_mutex_unlock fail, " \
+ "errno: %d, error info: %s", \
+ __LINE__, result, STRERROR(result));
+ }
+
+ return pNode;
+}
+
+int fast_mblock_free(struct fast_mblock_man *mblock, \
+ struct fast_mblock_node *pNode)
+{
+ int result;
+
+ if ((result=pthread_mutex_lock(&(mblock->lock))) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "call pthread_mutex_lock fail, " \
+ "errno: %d, error info: %s", \
+ __LINE__, result, STRERROR(result));
+ return result;
+ }
+
+ pNode->next = mblock->free_chain_head;
+ mblock->free_chain_head = pNode;
+
+ if ((result=pthread_mutex_unlock(&(mblock->lock))) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "call pthread_mutex_unlock fail, " \
+ "errno: %d, error info: %s", \
+ __LINE__, result, STRERROR(result));
+ }
+
+ return 0;
+}
+
+int fast_mblock_count(struct fast_mblock_man *mblock)
+{
+ struct fast_mblock_node *pNode;
+ int count;
+ int result;
+
+ if ((result=pthread_mutex_lock(&(mblock->lock))) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "call pthread_mutex_lock fail, " \
+ "errno: %d, error info: %s", \
+ __LINE__, result, STRERROR(result));
+ return -1;
+ }
+
+ count = 0;
+ pNode = mblock->free_chain_head;
+ while (pNode != NULL)
+ {
+ pNode = pNode->next;
+ count++;
+ }
+
+ if ((result=pthread_mutex_unlock(&(mblock->lock))) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "call pthread_mutex_unlock fail, " \
+ "errno: %d, error info: %s", \
+ __LINE__, result, STRERROR(result));
+ }
+
+ return count;
+}
+
diff --git a/common/fast_mblock.h b/common/fast_mblock.h
new file mode 100644
index 0000000..496c9c7
--- /dev/null
+++ b/common/fast_mblock.h
@@ -0,0 +1,100 @@
+/**
+* Copyright (C) 2008 Happy Fish / YuQing
+*
+* FastDFS may be copied only under the terms of the GNU General
+* Public License V3, which may be found in the FastDFS source kit.
+* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+**/
+
+//fast_mblock.h
+
+#ifndef _FAST_MBLOCK_H
+#define _FAST_MBLOCK_H
+
+#include
+#include
+#include
+#include
+#include "common_define.h"
+#include "chain.h"
+
+/* free node chain */
+struct fast_mblock_node
+{
+ struct fast_mblock_node *next;
+ char data[0]; //the data buffer
+};
+
+/* malloc chain */
+struct fast_mblock_malloc
+{
+ struct fast_mblock_malloc *next;
+};
+
+struct fast_mblock_man
+{
+ struct fast_mblock_node *free_chain_head; //free node chain
+ struct fast_mblock_malloc *malloc_chain_head; //malloc chain to be freed
+ int element_size; //element size
+ int alloc_elements_once; //alloc elements once
+ pthread_mutex_t lock; //the lock for read / write free node chain
+};
+
+#define fast_mblock_to_node_ptr(data_ptr) \
+ (struct fast_mblock_node *)(data_ptr - ((size_t)(char *) \
+ &((struct fast_mblock_node *)0)->data))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+mblock init
+parameters:
+ mblock: the mblock pointer
+ element_size: element size, such as sizeof(struct xxx)
+ alloc_elements_once: malloc elements once, 0 for malloc 1MB once
+return error no, 0 for success, != 0 fail
+*/
+int fast_mblock_init(struct fast_mblock_man *mblock, const int element_size, \
+ const int alloc_elements_once);
+
+/**
+mblock destroy
+parameters:
+ mblock: the mblock pointer
+*/
+void fast_mblock_destroy(struct fast_mblock_man *mblock);
+
+/**
+alloc a node from the mblock
+parameters:
+ mblock: the mblock pointer
+return the alloced node, return NULL if fail
+*/
+struct fast_mblock_node *fast_mblock_alloc(struct fast_mblock_man *mblock);
+
+/**
+free a node (put a node to the mblock)
+parameters:
+ mblock: the mblock pointer
+ pNode: the node to free
+return the alloced node, return NULL if fail
+*/
+int fast_mblock_free(struct fast_mblock_man *mblock, \
+ struct fast_mblock_node *pNode);
+
+/**
+get node count of the mblock
+parameters:
+ mblock: the mblock pointer
+return the free node count of the mblock, return -1 if fail
+*/
+int fast_mblock_count(struct fast_mblock_man *mblock);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/common/fast_task_queue.c b/common/fast_task_queue.c
new file mode 100644
index 0000000..b55bc46
--- /dev/null
+++ b/common/fast_task_queue.c
@@ -0,0 +1,503 @@
+//fast_task_queue.c
+
+#include
+#include
+#include
+#include "fast_task_queue.h"
+#include "logger.h"
+#include "shared_func.h"
+#include "pthread_func.h"
+
+static struct fast_task_queue g_free_queue;
+
+struct mpool_chain {
+ struct fast_task_info *blocks;
+ struct fast_task_info *last_block; //last block
+ struct mpool_chain *next;
+} *g_mpool = NULL;
+
+#define ALIGNED_TASK_INFO_SIZE MEM_ALIGN(sizeof(struct fast_task_info))
+
+int task_queue_init(struct fast_task_queue *pQueue)
+{
+ int result;
+
+ if ((result=init_pthread_lock(&(pQueue->lock))) != 0)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "init_pthread_lock fail, errno: %d, error info: %s", \
+ __LINE__, result, STRERROR(result));
+ return result;
+ }
+
+ pQueue->head = NULL;
+ pQueue->tail = NULL;
+
+ return 0;
+}
+
+static struct mpool_chain *malloc_mpool(const int block_size, \
+ const int total_alloc_size)
+{
+ struct fast_task_info *pTask;
+ char *p;
+ char *pCharEnd;
+ struct mpool_chain *mpool;
+
+ mpool = (struct mpool_chain *)malloc(sizeof(struct mpool_chain));
+ if (mpool == NULL)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "malloc %d bytes fail, " \
+ "errno: %d, error info: %s", \
+ __LINE__, (int)sizeof(struct mpool_chain), \
+ errno, STRERROR(errno));
+ return NULL;
+ }
+
+ mpool->next = NULL;
+ mpool->blocks = (struct fast_task_info *)malloc(total_alloc_size);
+ if (mpool->blocks == NULL)
+ {
+ logError("file: "__FILE__", line: %d, " \
+ "malloc %d bytes fail, " \
+ "errno: %d, error info: %s", \
+ __LINE__, total_alloc_size, \
+ errno, STRERROR(errno));
+ free(mpool);
+ return NULL;
+ }
+ memset(mpool->blocks, 0, total_alloc_size);
+
+ pCharEnd = ((char *)mpool->blocks) + total_alloc_size;
+ for (p=(char *)mpool->blocks; p