├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSES
├── CC0-1.0.txt
├── GPL-3.0-or-later.txt
└── LGPL-3.0-or-later.txt
├── Makefile.am
├── README.md
├── autogen.sh
├── configure.ac
├── debian
├── changelog
├── control
├── copyright
├── libzbd-dev.install
├── libzbd2.install
├── libzbd2.symbols
├── not-installed
├── rules
├── salsa-ci.yml
├── source
│ └── format
├── tests
│ └── control
├── upstream
│ └── metadata
├── watch
└── zbd-utils.install
├── include
└── libzbd
│ └── zbd.h
├── lib
├── Makefile.am
├── exports
├── libzbd.pc.in
├── zbd.c
├── zbd.h
└── zbd_utils.c
├── libzbd.spec
├── m4
└── dontremove
└── tools
├── Makefile.am
├── cli
├── Makefile.am
├── zbd.8
├── zbd.c
├── zbd.h
└── zbd_dump.c
├── gui
├── Makefile.am
├── gzbd.8
├── gzbd.c
├── gzbd.desktop.in
├── gzbd.h
├── gzbd.png
├── gzbd_if.c
├── gzbd_if_dev.c
└── org.gnome.gzbd.policy.in
└── viewer
├── Makefile.am
├── gzbd-viewer.8
├── gzbd-viewer.desktop.in
├── gzbd-viewer.png
├── gzbd_viewer.c
├── gzbd_viewer.h
├── gzbd_viewer_if.c
└── org.gnome.gzbd-viewer.policy.in
/.gitignore:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: CC0-1.0
2 | #
3 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 |
5 | # Object files
6 | *.o
7 | *.lo
8 | *.obj
9 | *.elf
10 | *.la
11 | *.so
12 | *.so.*
13 | *.out
14 | *.dump
15 |
16 | tools/zbd
17 | tools/gzbd
18 | tools/gzbd-viewer
19 |
20 | # Generated files
21 | *.desktop
22 | *.policy
23 |
24 | # Dependency files
25 | .depfile
26 | .depend
27 |
28 | # RPM packages build files and directories
29 | rpmbuild
30 | *.tar.gz
31 | *.rpm
32 |
33 | # Debian packages build files and directories
34 | debbuild
35 | *.deb
36 |
37 | # Other
38 | Thumbs.db
39 | desktop.ini
40 | .DS_Store
41 | *~
42 |
43 | # Autotools stuff
44 | m4/*.m4
45 | Makefile.in
46 | aclocal.m4
47 | autom4te.cache/
48 | build-aux/
49 | configure
50 | include/config.h.in
51 | Makefile
52 | config.log
53 | config.status
54 | include/config.h
55 | include/stamp-h1
56 | .deps
57 | libtool
58 | lib/libzbd.pc
59 | .libs
60 | .dirstamp
61 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 |
2 | # Contributor Covenant Code of Conduct
3 |
4 | ## Our Pledge
5 |
6 | We as members, contributors, and leaders pledge to make participation in our
7 | community a harassment-free experience for everyone, regardless of age, body
8 | size, visible or invisible disability, ethnicity, sex characteristics, gender
9 | identity and expression, level of experience, education, socio-economic status,
10 | nationality, personal appearance, race, caste, color, religion, or sexual
11 | identity and orientation.
12 |
13 | We pledge to act and interact in ways that contribute to an open, welcoming,
14 | diverse, inclusive, and healthy community.
15 |
16 | ## Our Standards
17 |
18 | Examples of behavior that contributes to a positive environment for our
19 | community include:
20 |
21 | * Demonstrating empathy and kindness toward other people
22 | * Being respectful of differing opinions, viewpoints, and experiences
23 | * Giving and gracefully accepting constructive feedback
24 | * Accepting responsibility and apologizing to those affected by our mistakes,
25 | and learning from the experience
26 | * Focusing on what is best not just for us as individuals, but for the
27 | overall community
28 |
29 | Examples of unacceptable behavior include:
30 |
31 | * The use of sexualized language or imagery, and sexual attention or
32 | advances of any kind
33 | * Trolling, insulting or derogatory comments, and personal or political attacks
34 | * Public or private harassment
35 | * Publishing others' private information, such as a physical or email
36 | address, without their explicit permission
37 | * Other conduct which could reasonably be considered inappropriate in a
38 | professional setting
39 |
40 | ## Enforcement Responsibilities
41 |
42 | Community leaders are responsible for clarifying and enforcing our standards of
43 | acceptable behavior and will take appropriate and fair corrective action in
44 | response to any behavior that they deem inappropriate, threatening, offensive,
45 | or harmful.
46 |
47 | Community leaders have the right and responsibility to remove, edit, or reject
48 | comments, commits, code, wiki edits, issues, and other contributions that are
49 | not aligned to this Code of Conduct, and will communicate reasons for moderation
50 | decisions when appropriate.
51 |
52 | ## Scope
53 |
54 | This Code of Conduct applies within all community spaces, and also applies when
55 | an individual is officially representing the community in public spaces.
56 | Examples of representing our community include using an official e-mail address,
57 | posting via an official social media account, or acting as an appointed
58 | representative at an online or offline event.
59 |
60 | ## Enforcement
61 |
62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
63 | reported to the community leaders responsible for enforcement at
64 | [Open Source Inquiries][contact].
65 | All complaints will be reviewed and investigated promptly and fairly.
66 |
67 | All community leaders are obligated to respect the privacy and security of the
68 | reporter of any incident.
69 |
70 | ## Enforcement Guidelines
71 |
72 | Community leaders will follow these Community Impact Guidelines in determining
73 | the consequences for any action they deem in violation of this Code of Conduct:
74 |
75 | ### 1. Correction
76 |
77 | **Community Impact**: Use of inappropriate language or other behavior deemed
78 | unprofessional or unwelcome in the community.
79 |
80 | **Consequence**: A private, written warning from community leaders, providing
81 | clarity around the nature of the violation and an explanation of why the
82 | behavior was inappropriate. A public apology may be requested.
83 |
84 | ### 2. Warning
85 |
86 | **Community Impact**: A violation through a single incident or series
87 | of actions.
88 |
89 | **Consequence**: A warning with consequences for continued behavior. No
90 | interaction with the people involved, including unsolicited interaction with
91 | those enforcing the Code of Conduct, for a specified period of time. This
92 | includes avoiding interactions in community spaces as well as external channels
93 | like social media. Violating these terms may lead to a temporary or
94 | permanent ban.
95 |
96 | ### 3. Temporary Ban
97 |
98 | **Community Impact**: A serious violation of community standards, including
99 | sustained inappropriate behavior.
100 |
101 | **Consequence**: A temporary ban from any sort of interaction or public
102 | communication with the community for a specified period of time. No public or
103 | private interaction with the people involved, including unsolicited interaction
104 | with those enforcing the Code of Conduct, is allowed during this period.
105 | Violating these terms may lead to a permanent ban.
106 |
107 | ### 4. Permanent Ban
108 |
109 | **Community Impact**: Demonstrating a pattern of violation of community
110 | standards, including sustained inappropriate behavior, harassment of an
111 | individual, or aggression toward or disparagement of classes of individuals.
112 |
113 | **Consequence**: A permanent ban from any sort of public interaction within
114 | the community.
115 |
116 | ## Attribution
117 |
118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119 | version 2.0, available at
120 | [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
121 |
122 | Community Impact Guidelines were inspired by
123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124 |
125 | For answers to common questions about this code of conduct, see the FAQ at
126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available
127 | at [https://www.contributor-covenant.org/translations][translations].
128 |
129 | [homepage]: https://www.contributor-covenant.org
130 | [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
131 | [Mozilla CoC]: https://github.com/mozilla/diversity
132 | [FAQ]: https://www.contributor-covenant.org/faq
133 | [translations]: https://www.contributor-covenant.org/translations
134 | [contact]: https://www.westerndigital.com/contact/contact-open-source
135 |
--------------------------------------------------------------------------------
/LICENSES/CC0-1.0.txt:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
--------------------------------------------------------------------------------
/LICENSES/LGPL-3.0-or-later.txt:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/Makefile.am:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-or-later
2 | #
3 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 |
5 | ACLOCAL_AMFLAGS = -I m4
6 |
7 | SUBDIRS = lib tools
8 |
9 | EXTRA_DIST = autogen.sh \
10 | README.md \
11 | LICENSES/GPL-3.0-or-later.txt \
12 | LICENSES/LGPL-3.0-or-later.txt
13 |
14 | if BUILDING_RPM
15 | rpmdir = $(abs_top_builddir)/rpmbuild
16 |
17 | EXTRA_DIST += libzbd.spec
18 | RPMARCH=`$(RPM) --eval %_target_cpu`
19 |
20 | rpm: dist
21 | @echo "Building RPM packages..."
22 | @mkdir -p $(rpmdir)
23 | $(RPMBUILD) -ta --clean \
24 | -D "_topdir $(rpmdir)" \
25 | -D "pkg_name $(PACKAGE_NAME)" \
26 | -D "pkg_version $(PACKAGE_VERSION)" \
27 | libzbd-$(PACKAGE_VERSION).tar.gz
28 | @mv -f $(rpmdir)/RPMS/$(RPMARCH)/*.rpm $(abs_top_builddir)
29 | @mv -f $(rpmdir)/SRPMS/*.rpm $(abs_top_builddir)
30 | @rm -rf $(rpmdir)
31 | @rm -f libzbd-$(PACKAGE_VERSION).tar.gz
32 | else
33 | rpm:
34 | @echo "Building RPM packages requires rpmbuild and rpm utilities"
35 | exit 1
36 | endif
37 |
38 | CLEANFILES = *.rpm *.tar.gz
39 | DISTCLEANFILES = *.rpm *.tar.gz configure
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Copyright (C) 2020 Western Digital Corporation or its affiliates.
2 |
3 | # libzbd
4 |
5 | *libzbd* is a library providing functions simplifying access to zoned block
6 | device information and the execution of zone management operations.
7 |
8 | An example command line application using *libzbd* is available under the
9 | tools directory. Additionally, graphical user interface applications are also
10 | implemented to visually represent the state and usage of zones of a zoned
11 | block device.
12 |
13 | ## License
14 |
15 | *libzbd* is distributed under the terms of the of the GNU Lesser General Public
16 | License Version 3 or any later verison ((SPDX: *LGPL-3.0-or-later*). A copy of
17 | this license with *libzbd* copyright can be found in the file
18 | [LICENSES/LGPL-3.0-or-later.txt](LICENSES/LGPL-3.0-or-later.txt).
19 |
20 | All example applications under the tools directory are distributed under
21 | the terms of the GNU General Public License version 3, or any later version.
22 | A copy of this license can be found in the file
23 | [LICENSES/GPL-3.0-or-later.txt](LICENSES/GPL-3.0-or-later.txt).
24 |
25 | If *libzbd* license files are missing, please see the LGPL version 3 text
26 | [here](https://www.gnu.org/licenses/lgpl-3.0.html) and the GPL version 3 text
27 | [here](https://www.gnu.org/licenses/gpl-3.0.html).
28 |
29 | All source files in *libzbd* contain the LGPL v3 or GPL v3 license SPDX short
30 | identifiers in place of the full license text.
31 |
32 | ```
33 | SPDX-License-Identifier: LGPL-3.0-or-later
34 | SPDX-License-Identifier: GPL-3.0-or-later
35 | ```
36 |
37 | Some files such as the `.gitignore` file are public domain specified by the
38 | CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. These files are
39 | identified with the following SPDX header.
40 |
41 | ```
42 | SPDX-License-Identifier: CC0-1.0
43 | ```
44 |
45 | See the file [LICENSES/CC0-1.0.txt] for the full text of this license.
46 |
47 | *libzbd* and all its example applications are distributed "as is," without
48 | technical support, and WITHOUT ANY WARRANTY, without even the implied warranty
49 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
50 |
51 | ## Contributions and Bug Reports
52 |
53 | Contributions are accepted as github pull requests or via email (`git
54 | send-email` patches). Any problem may also be reported through github issues
55 | page or by contacting:
56 |
57 | * Damien Le Moal (damien.lemoal@wdc.com)
58 | * Niklas Cassel (niklas.cassel@wdc.com)
59 |
60 | PLEASE DO NOT SUBMIT CONFIDENTIAL INFORMATION OR INFORMATION SPECIFIC TO DRIVES
61 | THAT ARE VENDOR SAMPLES OR NOT PUBLICLY AVAILABLE.
62 |
63 | ## Compilation and installation
64 |
65 | ### Requirements
66 |
67 | *libzbd* requires the following packages for compilation:
68 |
69 | * m4
70 | * autoconf
71 | * automake
72 | * libtool
73 | * GTK3 and GTK3 development headers (when building the *gzbd* and *gzbd-viewer*
74 | graphical applications)
75 |
76 | Since *libzbd* uses Linux(tm) kernel zoned block device interface, compilation
77 | must be done on a system where the kernel header file *blkzoned.h* for zoned
78 | block devices interface is installed under /usr/include/linux/. This implies
79 | that the kernel version must be higher than version 4.10.0.
80 |
81 | If the GTK3 and GTK3 development packages are not installed, compilation of the
82 | *gzbd* and *gzbd-viewer* graphical applications is automatically disabled.
83 |
84 | ### Compilation
85 |
86 | To compile the library and all example applications under the tools directory,
87 | execute the following commands.
88 |
89 | ```
90 | $ sh ./autogen.sh
91 | $ ./configure
92 | $ make
93 | ```
94 |
95 | ### Installation
96 |
97 | To install the library and all example applications compiled under the tools
98 | directory, execute the following command.
99 |
100 | ```
101 | $ sudo make install
102 | ```
103 |
104 | The library files are by default installed under `/usr/lib` (or `/usr/lib64`).
105 | The library header file is installed under `/usr/include/libzbd`.
106 |
107 | The executable files for the example applications are installed under
108 | `/usr/bin`.
109 |
110 | These default installation paths can be changed. Executing the following
111 | command displays the options used to control the installation paths.
112 |
113 | ```
114 | $ ./configure --help
115 | ```
116 |
117 | ### Building RPM Packages
118 |
119 | The *rpm* and *rpmbuild* utilities are necessary to build *libzbd* RPM
120 | packages. Once these utilities are installed, the RPM packages can be built
121 | using the following command.
122 |
123 | ```
124 | $ sh ./autogen.sh
125 | $ ./configure
126 | $ make rpm
127 | ```
128 |
129 | Five RPM packages are built:
130 | * A binary package providing *libzbd* library file, the tools executables,
131 | the tools man pages, documentation and license files.
132 | * A source RPM package
133 | * A *debuginfo* RPM package and a *debugsource* RPM package
134 | * A development package providing the *libzbd* header files
135 |
136 | The source RPM package can be used to build the binary and debug RPM packages
137 | outside of *libzbd* source tree using the following command.
138 |
139 | ```
140 | $ rpmbuild --rebuild libzbd-.src.rpm
141 | ```
142 |
143 | ## Library
144 |
145 | *libzbd* defines a set of functions and data structures simplifying the
146 | management and use of zoned block devices.
147 |
148 | ### Overview
149 |
150 | *libzbd* functions and data structures are defined in the header file
151 | [include/libzbd/zbd.h]. Applications using *libzbd* must include this header
152 | file and compile against *libzbd* using dynamic linking (`libzbd.so` library
153 | file) or statically linking using the `libzbd.a` archive file.
154 |
155 | *libzbd* internal implementation is simple. No internal library state is
156 | maintained at run-time, with the exception of a list of open zoned block device.
157 | Data types and units used by regular file access system calls are reused.
158 |
159 | * Open zoned block device files are identified using a file descriptor similar
160 | to one that the *open()* system call returns.
161 | * Zone information such as zone start position, zone size and zone write pointer
162 | position use Byte unit, the same unit as used by system calls such as
163 | *fseek()*, *read()* or *write()* for file offset position and I/O buffer
164 | sizes.
165 |
166 | *libzbd* provides data structures for describing zones of a zoned block device
167 | (*struct zbd_zone*) and providing information about the device itself
168 | (*struct zbd_info*).
169 |
170 | ### Library Functions
171 |
172 | *libzbd* implements the following functions.
173 |
174 | Function | Description
175 | ----------------------- | -----------------------------------------------
176 | *zbd_device_is_zoned()* | Test if a block device is a zoned block device
177 | *zbd_open()* | Open a zoned block device
178 | *zbd_close()* | Close a zoned block device
179 | *zbd_get_info()* | Get an open zoned block device information
180 | *zbd_report_zones()*
*zbd_list_zones()* | Get zone information of an open device
181 | *zbd_report_nr_zones()* | Get the number of zones of an open device
182 | *zbd_zone_operation()* | Execute a zone management operation
183 | *zbd_reset_zones()* | Reset the write pointer position of a range of zones
184 | *zbd_open_zones()* | Explicitly open a range of zones
185 | *zbd_close_zones()* | Explicitly close a range of open zones
186 | *zbd_finish_zones()* | Finish a range of zones
187 |
188 | The following macro definitions are defined to facilitate manipulation of a
189 | zone descriptor information (*struct zbd_zone*).
190 |
191 | Function | Description
192 | ----------------------- | --------------------------------------------------
193 | *zbd_zone_type()* | Get a zone type (*enum zbd_zone_type*)
194 | *zbd_zone_cnv()* | Test if a zone type is conventional
195 | *zbd_zone_swr()* | Test if a zone type is sequential write required
196 | *zbd_zone_swp()* | Test if a zone type is sequential write preferred
197 | *zbd_zone_seq()* | Test if zone type is sequential (not conventional)
198 | *zbd_zone_cond()* | Get a zone condition (*enum zbd_zone_cond*)
199 | *zbd_zone_not_wp()* | Test if a zone is "not write pointer" (conventional zones)
200 | *zbd_zone_empty()* | Test if a zone is empty
201 | *zbd_zone_imp_open()* | Test if a zone is implicitly open
202 | *zbd_zone_exp_open()* | Test if a zone is explicitly open
203 | *zbd_zone_is_open()* | Test if a zone is open (implicitly or explicitly)
204 | *zbd_zone_closed()* | Test if a zone is closed
205 | *zbd_zone_full()* | Test if a zone is full
206 | *zbd_zone_rdonly()* | Test if a zone is read-only
207 | *zbd_zone_offline()* | Test if a zone is offline
208 | *zbd_zone_start()* | Get a zone start position (bytes)
209 | *zbd_zone_len()* | Get a zone size (bytes)
210 | *zbd_zone_capacity()* | Get a zone capacity (bytes)
211 | *zbd_zone_wp()* | Get a zone write pointer position (bytes)
212 | *zbd_zone_flags()* | Get a zone state flags
213 | *zbd_zone_rwp_recommended()* | Test if a zone indicates reset write pointer recommended
214 | *zbd_zone_non_seq_resources()* | Test if a zone indicates using non-sequential write resources
215 |
216 | The followimg utility functions are also defined.
217 |
218 | Function | Description
219 | ------------------------ | --------------------------------------------------
220 | *zbd_set_log_level()* | Set the logging level of the library functions
221 | *zbd_device_model_str()* | Get a string describing a device zoned model
222 | *zbd_zone_type_str()* | Get a string describing a zone type
223 | *zbd_zone_cond_str()* | Get a string describing a zone condition
224 |
225 | ### Thread Safety
226 |
227 | Since *libzbd* does not maintain any internal state for open zoned block
228 | devices, that is, it does not dynamically maintain the zone state of open zoned
229 | block devices, no synchronization mechanism for multiple threads applications is
230 | implemented. It is the responsibility of the application to ensure that zones of
231 | a device are manipulated correctly with mutual exclusion when needed. This is
232 | in particular necessary for operations like concurrent write to the same zone
233 | by multiple threads or the execution of zone management operations while reading
234 | or writing the target zones.
235 |
236 | ### Functions Documentation
237 |
238 | *libzbd* functions and data types are documented in more details using code
239 | comments in the file [include/libzbd/zbd.h].
240 |
241 | ## Tools
242 |
243 | Under the tools directory, several simple applications are available as
244 | examples. These appliations are as follows.
245 |
246 | * **zbd** This application execute zone commands on a device.
247 |
248 | * **gzbd** provides a graphical user interface showing zone information of a
249 | zoned block device. It also displays the write status (write pointer
250 | position) of zones graphically using color coding (red for written space and
251 | green for unwritten space). Operations on zones can also be executed directly
252 | from the interface (reset zone write pointer, open zone, close zone, etc).
253 |
254 | * **gzbd-viewer** provides a simple graphical user interface showing the write
255 | pointer position and zone state of zones of a zoned block device. Similar
256 | color coding as *gzbd* is used. This application automatically refresh the
257 | device zone information after a configurable timeout elapses. The default
258 | refresh cycle is 2 times per seconds (500 ms).
259 |
--------------------------------------------------------------------------------
/autogen.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # SPDX-License-Identifier: LGPL-3.0-or-later
4 | #
5 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
6 |
7 | exec autoreconf -f -i
8 |
--------------------------------------------------------------------------------
/configure.ac:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-or-later
2 | #
3 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 |
5 | AC_INIT([libzbd], [2.0.4],
6 | [damien.lemoal@wdc.com, niklas.cassel@wdc.com],
7 | [libzbd], [https://github.com/westerndigitalcorporation/libzbd])
8 |
9 | AC_CONFIG_AUX_DIR([build-aux])
10 | AC_CANONICAL_TARGET
11 | AC_CONFIG_MACRO_DIR([m4])
12 | AC_CONFIG_HEADERS([include/config.h])
13 | AC_PREFIX_DEFAULT(/usr)
14 | AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
15 | AM_SILENT_RULES([yes])
16 |
17 | # Change default CFLAGS from "-g -O2" to "-O2" for regular builds.
18 | AC_ARG_ENABLE(debug,
19 | [ --enable-debug Compile with "-g" option],
20 | [DBGCFLAGS="-g"],
21 | [DBGCFLAGS="-O2"])
22 | CFLAGS="$CFLAGS $DBGCFLAGS"
23 |
24 | AC_PROG_CC
25 | AM_PROG_CC_C_O
26 | AC_PROG_INSTALL
27 |
28 | AC_USE_SYSTEM_EXTENSIONS
29 | AC_SYS_LARGEFILE
30 |
31 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
32 | m4_pattern_allow([AM_PROG_AR])
33 | LT_INIT
34 |
35 | # Package version details: ..
36 | PACKAGE_VERSION_MAJOR=$(echo $PACKAGE_VERSION | awk -F. '{print $1}')
37 | PACKAGE_VERSION_MINOR=$(echo $PACKAGE_VERSION | awk -F. '{print $2}')
38 | PACKAGE_VERSION_RELEASE=$(echo $PACKAGE_VERSION | awk -F. '{print $3}')
39 |
40 | # libtool friendly library version format
41 | LIBZBD_VERSION_LT=$PACKAGE_VERSION_MAJOR:$PACKAGE_VERSION_MINOR:$PACKAGE_VERSION_RELEASE
42 | AC_SUBST([LIBZBD_VERSION_LT])
43 |
44 | # Checks for rpm package builds
45 | AC_PATH_PROG([RPMBUILD], [rpmbuild], [notfound])
46 | AC_PATH_PROG([RPM], [rpm], [notfound])
47 | AM_CONDITIONAL([BUILDING_RPM],
48 | [test "x$RPMBUILD" != xnotfound && test "x$RPM" != xnotfound])
49 |
50 | # Checks for header files
51 | AC_CHECK_HEADER(libgen.h, [],
52 | [AC_MSG_ERROR([Couldn't find libgen.h])],
53 | [[#include ]])
54 | AC_CHECK_HEADER(linux/blkzoned.h, [],
55 | [AC_MSG_ERROR([Couldn't find linux/blkzoned.h. Kernel too old ?])],
56 | [[#include ]])
57 |
58 | AC_CHECK_MEMBER([struct blk_zone.capacity],
59 | [AC_DEFINE(HAVE_BLK_ZONE_REP_V2, [1], [report zones includes zone capacity])],
60 | [], [[#include ]])
61 |
62 | # Conditionals
63 |
64 | # Build GUI tools only if GTK3 is installed and can be detected with pkg-config.
65 | AC_ARG_ENABLE([gui],
66 | AS_HELP_STRING([--disable-gui],
67 | [Disable build of GUI tools (gzbd and gzbd-viewer) [default=no]]))
68 | AS_IF([test "x$enable_gui" != "xno"],
69 | [m4_ifdef([PKG_CHECK_MODULES],
70 | [PKG_CHECK_MODULES([GTK], [gtk+-3.0], [HAVE_GTK3=1], [HAVE_GTK3=0])],
71 | [HAVE_GTK3=0])
72 | AM_CONDITIONAL([BUILD_GUI], [test "$HAVE_GTK3" -eq 1])],
73 | [AM_CONDITIONAL([BUILD_GUI], false)])
74 |
75 | AC_CONFIG_FILES([
76 | lib/libzbd.pc
77 | lib/Makefile
78 | tools/Makefile
79 | Makefile
80 | ])
81 |
82 | AC_OUTPUT
83 |
--------------------------------------------------------------------------------
/debian/changelog:
--------------------------------------------------------------------------------
1 | libzbd (2.0.2-2) unstable; urgency=medium
2 |
3 | * Source only upload.
4 |
5 | -- Sudip Mukherjee Tue, 15 Feb 2022 22:02:14 +0000
6 |
7 | libzbd (2.0.2-1) unstable; urgency=medium
8 |
9 | [ Debian Janitor ]
10 | * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository,
11 | Repository-Browse.
12 | * Update standards version to 4.6.0, no changes needed.
13 |
14 | [ Sudip Mukherjee ]
15 | * New upstream version 2.0.2
16 | - Update version for SONAME change.
17 | - Update symbols.
18 | * Update Standards-Version to 4.6.0.1
19 |
20 | -- Sudip Mukherjee Mon, 14 Feb 2022 14:23:27 +0000
21 |
22 | libzbd (1.5.0-1) unstable; urgency=medium
23 |
24 | * Upload to unstable.
25 | * New upstream version 1.5.0
26 | - Update installed library name.
27 | - Update library name in symbols.
28 |
29 | -- Sudip Mukherjee Wed, 18 Aug 2021 23:00:07 +0100
30 |
31 | libzbd (1.4.0-1) experimental; urgency=medium
32 |
33 | * New upstream version 1.4.0
34 | - Update symbols.
35 | - Update copyright.
36 |
37 | -- Sudip Mukherjee Fri, 11 Jun 2021 23:41:24 +0100
38 |
39 | libzbd (1.3.0-1) experimental; urgency=medium
40 |
41 | * New upstream version 1.3.0
42 | - Update copyright.
43 | - Update symbols.
44 |
45 | -- Sudip Mukherjee Fri, 16 Apr 2021 19:50:56 +0100
46 |
47 | libzbd (1.2.0-1) unstable; urgency=medium
48 |
49 | * New upstream version 1.2.0
50 | - Update symbols.
51 | - Update copyright.
52 |
53 | -- Sudip Mukherjee Sat, 16 Jan 2021 22:53:00 +0000
54 |
55 | libzbd (1.1.0-2) unstable; urgency=medium
56 |
57 | * Source only upload.
58 |
59 | -- Sudip Mukherjee Sun, 10 Jan 2021 18:23:39 +0000
60 |
61 | libzbd (1.1.0-1) unstable; urgency=medium
62 |
63 | * Initial release (Closes: #977914)
64 |
65 | -- Sudip Mukherjee Wed, 23 Dec 2020 23:39:53 +0000
66 |
--------------------------------------------------------------------------------
/debian/control:
--------------------------------------------------------------------------------
1 | Source: libzbd
2 | Priority: optional
3 | Maintainer: Sudip Mukherjee
4 | Build-Depends: debhelper-compat (= 13), autoconf-archive, libgtk-3-dev
5 | Standards-Version: 4.6.0.1
6 | Section: libs
7 | Homepage: https://zonedstorage.io/projects/libzbd/
8 | Vcs-Browser: https://salsa.debian.org/sudip/libzbd
9 | Vcs-Git: https://salsa.debian.org/sudip/libzbd.git
10 |
11 | Package: libzbd-dev
12 | Section: libdevel
13 | Architecture: linux-any
14 | Multi-Arch: same
15 | Depends: libzbd2 (= ${binary:Version}), ${misc:Depends}
16 | Description: Library to manipulate zoned block devices (development files)
17 | libzbd uses the kernel provided zoned block device interface based on the
18 | ioctl() system call. It provides functions for discovering and managing the
19 | state of zones of zoned block devices. Read and write accesses to the devices
20 | can be done using standard standard I/O system calls.
21 | .
22 | This package is needed to compile programs against libzbd.
23 |
24 | Package: libzbd2
25 | Architecture: linux-any
26 | Multi-Arch: same
27 | Depends: ${shlibs:Depends}, ${misc:Depends}
28 | Description: Library to manipulate zoned block devices (shared library)
29 | libzbd uses the kernel provided zoned block device interface based on the
30 | ioctl() system call. It provides functions for discovering and managing the
31 | state of zones of zoned block devices. Read and write accesses to the devices
32 | can be done using standard standard I/O system calls.
33 | .
34 | This package contains the shared library.
35 |
36 | Package: zbd-utils
37 | Section: utils
38 | Architecture: linux-any
39 | Depends: ${shlibs:Depends}, ${misc:Depends}
40 | Description: Utilities to manipulate zoned block devices
41 | zbd is a command line utility to report, open, close, reset and finish zones
42 | of a device.
43 | .
44 | gzbd is similar to zbd but using a graphical user interface.
45 | .
46 | gzbd-viewer is a graphical user interface showing the condition and state of
47 | zones of a zoned block device.
48 |
--------------------------------------------------------------------------------
/debian/copyright:
--------------------------------------------------------------------------------
1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2 | Upstream-Name: libzbd
3 | Upstream-Contact: Damien Le Moal
5 | Source: https://github.com/westerndigitalcorporation/libzbd
6 |
7 | Files: *
8 | Copyright: 2020, 2021, Western Digital Corporation or its affiliates
9 | License: GPL-3.0-or-later
10 |
11 | Files: Makefile.am
12 | autogen.sh
13 | configure.ac
14 | lib/exports
15 | include/libzbd/zbd.h
16 | lib/Makefile.am
17 | lib/zbd.c
18 | lib/zbd.h
19 | lib/zbd_utils.c
20 | lib/libzbd.pc.in
21 | libzbd.spec
22 | Copyright: 2020, Western Digital Corporation or its affiliates
23 | License: LGPL-3.0-or-later
24 |
25 | Files: m4/dontremove
26 | .gitignore
27 | Copyright: 2020, Western Digital Corporation or its affiliates
28 | License: CC0-1.0
29 |
30 | Files: debian/*
31 | Copyright: 2020, Sudip Mukherjee
32 | License: GPL-3.0-or-later
33 |
34 | License: GPL-3.0-or-later
35 | This package is free software; you can redistribute it and/or modify
36 | it under the terms of the GNU General Public License as published by
37 | the Free Software Foundation; either version 3 of the License, or
38 | (at your option) any later version.
39 | .
40 | This package is distributed in the hope that it will be useful,
41 | but WITHOUT ANY WARRANTY; without even the implied warranty of
42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43 | GNU General Public License for more details.
44 | .
45 | You should have received a copy of the GNU General Public License
46 | along with this program. If not, see
47 | .
48 | On Debian systems, the complete text of the GNU General
49 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
50 |
51 | License: LGPL-3.0-or-later
52 | This package is free software; you can redistribute it and/or
53 | modify it under the terms of the GNU Lesser General Public
54 | License as published by the Free Software Foundation; either
55 | version 3 of the License, or (at your option) any later version.
56 | .
57 | This package is distributed in the hope that it will be useful,
58 | but WITHOUT ANY WARRANTY; without even the implied warranty of
59 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
60 | Lesser General Public License for more details.
61 | .
62 | You should have received a copy of the GNU General Public License
63 | along with this program. If not, see .
64 | .
65 | On Debian systems, the complete text of the GNU Lesser General
66 | Public License can be found in "/usr/share/common-licenses/LGPL-3"
67 |
68 | License: CC0-1.0
69 | On Debian systems, the full text of the CC0 1.0 Universal
70 | License can be found in the file
71 | "/usr/share/common-licenses/CC0-1.0"
72 |
--------------------------------------------------------------------------------
/debian/libzbd-dev.install:
--------------------------------------------------------------------------------
1 | usr/include/libzbd
2 | usr/lib/*/libzbd.a
3 | usr/lib/*/libzbd.so
4 | usr/lib/*/pkgconfig
5 |
--------------------------------------------------------------------------------
/debian/libzbd2.install:
--------------------------------------------------------------------------------
1 | usr/lib/*/libzbd.so.*
2 |
--------------------------------------------------------------------------------
/debian/libzbd2.symbols:
--------------------------------------------------------------------------------
1 | libzbd.so.2 libzbd2 #MINVER#
2 | ZBD_GLOBAL@ZBD_GLOBAL 1.1.0
3 | zbd_close@ZBD_GLOBAL 1.1.0
4 | zbd_device_is_zoned@ZBD_GLOBAL 1.1.0
5 | zbd_device_model_str@ZBD_GLOBAL 1.1.0
6 | zbd_get_info@ZBD_GLOBAL 2.0.2
7 | zbd_list_zones@ZBD_GLOBAL 1.1.0
8 | zbd_open@ZBD_GLOBAL 1.1.0
9 | zbd_report_zones@ZBD_GLOBAL 1.1.0
10 | zbd_set_log_level@ZBD_GLOBAL 1.1.0
11 | zbd_zone_cond_str@ZBD_GLOBAL 1.1.0
12 | zbd_zone_type_str@ZBD_GLOBAL 1.1.0
13 | zbd_zones_operation@ZBD_GLOBAL 1.1.0
14 |
--------------------------------------------------------------------------------
/debian/not-installed:
--------------------------------------------------------------------------------
1 | usr/lib/*/libzbd.la
2 |
--------------------------------------------------------------------------------
/debian/rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 |
3 | %:
4 | dh $@
5 |
--------------------------------------------------------------------------------
/debian/salsa-ci.yml:
--------------------------------------------------------------------------------
1 | # For more information on what jobs are run see:
2 | # https://salsa.debian.org/salsa-ci-team/pipeline
3 | #
4 | # To enable the jobs, go to your repository (at salsa.debian.org)
5 | # and click over Settings > CI/CD > Expand (in General pipelines).
6 | # In "Custom CI config path" write debian/salsa-ci.yml and click
7 | # in "Save Changes". The CI tests will run after the next commit.
8 | ---
9 | include:
10 | - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
11 | - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
12 |
--------------------------------------------------------------------------------
/debian/source/format:
--------------------------------------------------------------------------------
1 | 3.0 (quilt)
2 |
--------------------------------------------------------------------------------
/debian/tests/control:
--------------------------------------------------------------------------------
1 | Test-Command: gzbd --help; gzbd-viewer --help
2 | Depends: @
3 | Restrictions: superficial
4 |
--------------------------------------------------------------------------------
/debian/upstream/metadata:
--------------------------------------------------------------------------------
1 | ---
2 | Bug-Database: https://github.com/westerndigitalcorporation/libzbd/issues
3 | Bug-Submit: https://github.com/westerndigitalcorporation/libzbd/issues/new
4 | Repository: https://github.com/westerndigitalcorporation/libzbd.git
5 | Repository-Browse: https://github.com/westerndigitalcorporation/libzbd
6 |
--------------------------------------------------------------------------------
/debian/watch:
--------------------------------------------------------------------------------
1 | version=4
2 |
3 | opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%libzbd-$1.tar.gz%" \
4 | https://github.com/westerndigitalcorporation/libzbd/tags \
5 | (?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate
6 |
--------------------------------------------------------------------------------
/debian/zbd-utils.install:
--------------------------------------------------------------------------------
1 | usr/bin/*
2 | usr/share/*
3 |
--------------------------------------------------------------------------------
/include/libzbd/zbd.h:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-License-Identifier: LGPL-3.0-or-later
3 | *
4 | * Copyright (c) 2020 Western Digital Corporation or its affiliates.
5 | *
6 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
7 | * Ting Yao
8 | */
9 |
10 | #ifndef _LIBZBD_H_
11 | #define _LIBZBD_H_
12 |
13 | #ifdef __cplusplus
14 | extern "C" {
15 | #endif
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | /**
25 | * @brief Zone types
26 | *
27 | * @ZBD_ZONE_TYPE_CNV: The zone has no write pointer and can be writen
28 | * randomly. Zone reset has no effect on the zone.
29 | * @ZBD_ZONE_TYPE_SWR: The zone must be written sequentially
30 | * @ZBD_ZONE_TYPE_SWP: The zone can be written randomly
31 | */
32 | enum zbd_zone_type {
33 | ZBD_ZONE_TYPE_CNV = BLK_ZONE_TYPE_CONVENTIONAL,
34 | ZBD_ZONE_TYPE_SWR = BLK_ZONE_TYPE_SEQWRITE_REQ,
35 | ZBD_ZONE_TYPE_SWP = BLK_ZONE_TYPE_SEQWRITE_PREF,
36 | };
37 |
38 | /**
39 | * @brief Zone conditions (state)
40 | *
41 | * @ZBD_ZONE_COND_NOT_WP: The zone has no write pointer, it is conventional.
42 | * @ZBD_ZONE_COND_EMPTY: The zone is empty.
43 | * @ZBD_ZONE_COND_IMP_OPEN: The zone is open, but not explicitly opened.
44 | * @ZBD_ZONE_COND_EXP_OPEN: The zones was explicitly opened by an
45 | * OPEN ZONE command.
46 | * @ZBD_ZONE_COND_CLOSED: The zone was [explicitly] closed after writing.
47 | * @ZBD_ZONE_COND_FULL: The zone is marked as full, possibly by a zone
48 | * FINISH ZONE command.
49 | * @ZBD_ZONE_COND_READONLY: The zone is read-only.
50 | * @ZBD_ZONE_COND_OFFLINE: The zone is offline (dead).
51 | */
52 | enum zbd_zone_cond {
53 | ZBD_ZONE_COND_NOT_WP = BLK_ZONE_COND_NOT_WP,
54 | ZBD_ZONE_COND_EMPTY = BLK_ZONE_COND_EMPTY,
55 | ZBD_ZONE_COND_IMP_OPEN = BLK_ZONE_COND_IMP_OPEN,
56 | ZBD_ZONE_COND_EXP_OPEN = BLK_ZONE_COND_EXP_OPEN,
57 | ZBD_ZONE_COND_CLOSED = BLK_ZONE_COND_CLOSED,
58 | ZBD_ZONE_COND_FULL = BLK_ZONE_COND_FULL,
59 | ZBD_ZONE_COND_READONLY = BLK_ZONE_COND_READONLY,
60 | ZBD_ZONE_COND_OFFLINE = BLK_ZONE_COND_OFFLINE,
61 | };
62 |
63 | /**
64 | * @brief Zone flags
65 | *
66 | * @ZBD_ZONE_RWP_RECOMMENDED: The zone should be reset.
67 | * @ZBD_ZONE_NON_SEQ: The zone is using non-sequential write resources.
68 | */
69 | enum zbd_zone_flags {
70 | ZBD_ZONE_RWP_RECOMMENDED = (1U << 0),
71 | ZBD_ZONE_NON_SEQ_RESOURCES = (1U << 1),
72 | };
73 |
74 | /**
75 | * @brief Zone descriptor data structure
76 | *
77 | * Provide information on a zone with all position and size values in bytes.
78 | */
79 | struct zbd_zone {
80 | unsigned long long start; /* Zone start */
81 | unsigned long long len; /* Zone length */
82 | unsigned long long capacity; /* Zone capacity */
83 | unsigned long long wp; /* Zone write pointer */
84 | unsigned int flags; /* Zone state flags */
85 | unsigned int type; /* Zone type */
86 | unsigned int cond; /* Zone condition */
87 | uint8_t reserved[20]; /* Padding to 64B */
88 | } __attribute__((packed));
89 |
90 | /**
91 | * @brief Library log levels
92 | */
93 | enum zbd_log_level {
94 | ZBD_LOG_NONE = 0, /* Disable all messages */
95 | ZBD_LOG_ERROR, /* Output details about errors */
96 | ZBD_LOG_DEBUG, /* Debug-level messages */
97 | };
98 |
99 | /**
100 | * @brief Set the library log level
101 | * @param[in] log_level Library log level
102 | *
103 | * Set the library log level using the level name specified by \a log_level.
104 | * Log level are incremental: each level includes the levels preceding it.
105 | * Valid log level names are:
106 | * "none" : Silent operation (no messages)
107 | * "warning" : Print device level standard compliance problems
108 | * "error" : Print messages related to unexpected errors
109 | * "info" : Print normal information messages
110 | * "debug" : Verbose output decribing internally executed commands
111 | * The default level is "warning".
112 | */
113 | extern void zbd_set_log_level(enum zbd_log_level);
114 |
115 | /**
116 | * @brief Block device zone models.
117 | */
118 | enum zbd_dev_model {
119 | ZBD_DM_HOST_MANAGED = 1,
120 | ZBD_DM_HOST_AWARE,
121 | ZBD_DM_NOT_ZONED,
122 | };
123 |
124 | /**
125 | * @brief Device information vendor id string maximum length.
126 | */
127 | #define ZBD_VENDOR_ID_LENGTH 32
128 |
129 | /**
130 | * @brief Device information data structure
131 | *
132 | * Provide information on a device open using the \a zbd_open function.
133 | */
134 | struct zbd_info {
135 |
136 | /**
137 | * Device vendor, model and firmware revision string.
138 | */
139 | char vendor_id[ZBD_VENDOR_ID_LENGTH];
140 |
141 | /**
142 | * Total number of 512B sectors of the device.
143 | */
144 | unsigned long long nr_sectors;
145 |
146 | /**
147 | * Total number of logical blocks of the device.
148 | */
149 | unsigned long long nr_lblocks;
150 |
151 | /**
152 | * Total number of physical blocks of the device.
153 | */
154 | unsigned long long nr_pblocks;
155 |
156 | /**
157 | * Size in bytes of a zone.
158 | */
159 | unsigned long long zone_size;
160 |
161 | /**
162 | * Size in 512B sectors of a zone.
163 | */
164 | unsigned int zone_sectors;
165 |
166 | /**
167 | * Size in bytes of the device logical blocks.
168 | */
169 | unsigned int lblock_size;
170 |
171 | /**
172 | * Size in bytes of the device physical blocks.
173 | */
174 | unsigned int pblock_size;
175 |
176 | /**
177 | * Number of zones.
178 | */
179 | unsigned int nr_zones;
180 |
181 | /**
182 | * Maximum number of explicitely open zones. A value of 0 means that
183 | * the device has no limit. A value of -1 means that the value is
184 | * unknown.
185 | */
186 | unsigned int max_nr_open_zones;
187 |
188 | /**
189 | * Maximum number of active zones. A value of 0 means that the device
190 | * has no limit. A value of -1 means that the value is unknown.
191 | */
192 | unsigned int max_nr_active_zones;
193 |
194 | /**
195 | * Device zone model.
196 | */
197 | unsigned int model;
198 |
199 | /**
200 | * Padding to 128B.
201 | */
202 | uint8_t reserved[36];
203 | } __attribute__((packed));
204 |
205 | /**
206 | * @brief Test if a device is a zoned block device
207 | * @param[in] filename Path to the device file
208 | * @param[in] info Address where to store the device information
209 | *
210 | * Test if a device supports the ZBD/ZAC command set. If \a fake is false,
211 | * only test physical devices. Otherwise, also test regular files and
212 | * regular block devices that may be in use with the fake backend driver
213 | * to create an emulated host-managed zoned block device.
214 | * If \a info is not NULL and the device is identified as a zoned
215 | * block device, the device information is returned at the address
216 | * specified by \a info.
217 | *
218 | * @return Returns a negative error code if the device test failed.
219 | * 1 is returned if the device is identified as a zoned zoned block device.
220 | * Otherwise, 0 is returned.
221 | */
222 | extern int zbd_device_is_zoned(const char *filename);
223 |
224 |
225 | /**
226 | * @brief Open a ZBD device
227 | * @param[in] filename Path to a device file
228 | * @param[in] flags Device file open flags
229 | * @param[out] info Device information
230 | *
231 | * Opens the device specified by \a filename, and returns a file descriptor
232 | * number similarly to the regular open() system call. If @info is non-null,
233 | * information on the device is returned at the specified address.
234 | *
235 | * @return If the device is not a zoned block device, -ENXIO is returned.
236 | * Any other error code returned by open(2) can be returned as well.
237 | */
238 | extern int zbd_open(const char *filename, int flags, struct zbd_info *info);
239 |
240 | /**
241 | * @brief Close a ZBD device
242 | * @param[in] fd File descriptor obtained with \a zbd_open
243 | *
244 | * Performs the equivalent to close(2) for a zoned block device open
245 | * using \a zbd_open.
246 | */
247 | extern void zbd_close(int fd);
248 |
249 | /**
250 | * @brief Get a device information
251 | * @param[in] fd File descriptor obtained with \a zbd_open
252 | * @param[in] info Address of the information structure to fill
253 | *
254 | * Get information about an open device. The \a info parameter is used to
255 | * return a device information. \a info must be allocated by the caller.
256 | */
257 | extern int zbd_get_info(int fd, struct zbd_info *info);
258 |
259 | /**
260 | * @brief Reporting options definitions
261 | *
262 | * Used to filter the zone information returned by the execution of a
263 | * REPORT ZONES command. Filtering is based on the value of the reporting
264 | * option and on the condition of the zones at the time of the execution of
265 | * the REPORT ZONES command.
266 | *
267 | * ZBD_RO_PARTIAL is not a filter: this reporting option can be combined
268 | * (or'ed) with any other filter option to limit the number of reported
269 | * zone information to the size of the REPORT ZONES command buffer.
270 | */
271 | enum zbd_report_option {
272 | /* Report all zones */
273 | ZBD_RO_ALL = 0x00,
274 |
275 | /* Report only empty zones */
276 | ZBD_RO_EMPTY = 0x01,
277 |
278 | /* Report only implicitly open zones */
279 | ZBD_RO_IMP_OPEN = 0x02,
280 |
281 | /* Report only explicitly open zones */
282 | ZBD_RO_EXP_OPEN = 0x03,
283 |
284 | /* Report only closed zones */
285 | ZBD_RO_CLOSED = 0x04,
286 |
287 | /* Report only full zones */
288 | ZBD_RO_FULL = 0x05,
289 |
290 | /* Report only read-only zones */
291 | ZBD_RO_RDONLY = 0x06,
292 |
293 | /* Report only offline zones */
294 | ZBD_RO_OFFLINE = 0x07,
295 |
296 | /* Report only zones with reset recommended flag set */
297 | ZBD_RO_RWP_RECOMMENDED = 0x10,
298 |
299 | /* Report only zones with the non-sequential resource used flag set */
300 | ZBD_RO_NON_SEQ = 0x11,
301 |
302 | /* Report only conventional zones (non-write-pointer zones) */
303 | ZBD_RO_NOT_WP = 0x3f,
304 | };
305 |
306 | /**
307 | * @brief Get zone information
308 | * @param[in] fd File descriptor obtained with \a zbd_open
309 | * @param[in] ofst Byte offset from which to report zones
310 | * @param[in] len Maximum length in bytes from \a ofst of the device
311 | * capacity range to inspect for the report
312 | * @param[in] ro Reporting options
313 | * @param[in] zones Pointer to the array of zone information to fill
314 | * @param[out] nr_zones Number of zones in the array \a zones
315 | *
316 | * Get zone information of at most \a nr_zones zones in the range
317 | * [ofst..ofst+len] and matching the \a ro option. If \a len is 0,
318 | * at most \a nr_zones zones starting from \a ofst up to the end on the device
319 | * capacity will be reported.
320 | * Return the zone information obtained in the array \a zones and the number
321 | * of zones reported at the address specified by \a nr_zones.
322 | * The array \a zones must be allocated by the caller and \a nr_zones
323 | * must point to the size of the allocated array (number of zone information
324 | * structures in the array). The first zone reported will be the zone
325 | * containing or after \a ofst. The last zone reported will be the zone
326 | * containing or before \a ofst + \a len.
327 | *
328 | * @return Returns 0 on success and -1 otherwise.
329 | */
330 | extern int zbd_report_zones(int fd, off_t ofst, off_t len,
331 | enum zbd_report_option ro,
332 | struct zbd_zone *zones, unsigned int *nr_zones);
333 |
334 | /**
335 | * @brief Get number of zones
336 | * @param[in] fd File descriptor obtained with \a zbd_open
337 | * @param[in] ofst Byte offset from which to report zones
338 | * @param[in] len Maximum length in bytes from \a ofst of the device
339 | * capacity range to inspect for the report
340 | * @param[in] ro Reporting options
341 | * @param[out] nr_zones The number of matching zones
342 | *
343 | * Similar to \a zbd_report_zones, but returns only the number of zones that
344 | * \a zbd_report_zones would have returned. This is useful to determine the
345 | * number of zones of a device to allocate an array of zone information
346 | * structures for use with \a zbd_report_zones.
347 | *
348 | * @return Returns 0 on success and -1 otherwise.
349 | */
350 | static inline int zbd_report_nr_zones(int fd, off_t ofst, off_t len,
351 | enum zbd_report_option ro,
352 | unsigned int *nr_zones)
353 | {
354 | return zbd_report_zones(fd, ofst, len, ro, NULL, nr_zones);
355 | }
356 |
357 | /**
358 | * @brief Get zone information
359 | * @param[in] fd File descriptor obtained with \a zbd_open
360 | * @param[in] ofst Byte offset from which to report zones
361 | * @param[in] len Maximum length in bytes from \a ofst of the device
362 | * capacity range to inspect for the report
363 | * @param[in] ro Reporting options
364 | * @param[out] zones The array of zone information filled
365 | * @param[out] nr_zones Number of zones in the array \a zones
366 | *
367 | * Similar to \a zbd_report_zones, but also allocates an appropriatly sized
368 | * array of zone information structures and return the address of the array
369 | * at the address specified by \a zones. The size of the array allocated and
370 | * filled is returned at the address specified by \a nr_zones. Freeing of the
371 | * memory used by the array of zone information structures allocated by this
372 | * function is the responsability of the user.
373 | *
374 | * @return Returns 0 on success and -1 otherwise.
375 | * Returns -ENOMEM if memory could not be allocated for \a zones.
376 | */
377 | extern int zbd_list_zones(int fd, off_t ofst, off_t len,
378 | enum zbd_report_option ro,
379 | struct zbd_zone **zones, unsigned int *nr_zones);
380 |
381 | /**
382 | * @brief Zone management operations.
383 | */
384 | enum zbd_zone_op {
385 | /**
386 | * Reset zones write pointer.
387 | */
388 | ZBD_OP_RESET = 0x01,
389 | /**
390 | * Explicitly open zones.
391 | */
392 | ZBD_OP_OPEN = 0x02,
393 | /**
394 | * Close opened zones.
395 | */
396 | ZBD_OP_CLOSE = 0x03,
397 | /**
398 | * Transition zones to full state.
399 | */
400 | ZBD_OP_FINISH = 0x04,
401 | };
402 |
403 | /**
404 | * @brief Execute an operation on a range of zones
405 | * @param[in] fd File descriptor obtained with \a zbd_open
406 | * @param[in] ofst Byte offset identifying the first zone to operate on
407 | * @param[in] len Maximum length in bytes from \a ofst of the set of zones
408 | * to operate on
409 | * @param[in] op The operation to perform
410 | *
411 | * Exexcute an operation on the range of zones defined by [ofst..ofst+len]
412 | * If \a len is 0, all zones from \a ofst will be processed.
413 | * The validity of the operation (reset, open, close or finish) depends on the
414 | * type and condition of the target zones.
415 | *
416 | * @return Returns 0 on success and -1 otherwise.
417 | */
418 | extern int zbd_zones_operation(int fd, enum zbd_zone_op op,
419 | off_t ofst, off_t len);
420 |
421 | /**
422 | * @brief Reset the write pointer of a range of zones
423 | * @param[in] fd File descriptor obtained with \a zbd_open
424 | * @param[in] ofst Byte offset identifying the first zone to reset
425 | * @param[in] len Maximum length in bytes from \a ofst of the set of zones
426 | * to reset
427 | *
428 | * Resets the write pointer of the zones in the range [ofst..ofst+len].
429 | * If \a len is 0, all zones from \a ofst will be processed.
430 | *
431 | * @return Returns 0 on success and -1 otherwise.
432 | */
433 | static inline int zbd_reset_zones(int fd, off_t ofst, off_t len)
434 | {
435 | return zbd_zones_operation(fd, ZBD_OP_RESET, ofst, len);
436 | }
437 |
438 | /**
439 | * @brief Explicitly open a range of zones
440 | * @param[in] fd File descriptor obtained with \a zbd_open
441 | * @param[in] ofst Byte offset identifying the first zone to open
442 | * @param[in] len Maximum length in bytes from \a ofst of the set of zones
443 | * to open
444 | *
445 | * Explicitly open the zones in the range [ofst..ofst+len]. If \a len is 0,
446 | * all zones from \a ofst will be processed.
447 | *
448 | * @return Returns 0 on success and -1 otherwise.
449 | */
450 | static inline int zbd_open_zones(int fd, off_t ofst, off_t len)
451 | {
452 | return zbd_zones_operation(fd, ZBD_OP_OPEN, ofst, len);
453 | }
454 |
455 | /**
456 | * @brief Close a range of zones
457 | * @param[in] fd File descriptor obtained with \a zbd_open
458 | * @param[in] ofst Byte offset identifying the first zone to close
459 | * @param[in] len Maximum length in bytes from \a ofst of the set of zones
460 | * to close
461 | *
462 | * Close the zones in the range [ofst..ofst+len].
463 | * If \a len is 0, all zones from \a ofst will be processed.
464 | *
465 | * @return Returns 0 on success and -1 otherwise.
466 | */
467 | static inline int zbd_close_zones(int fd, off_t ofst, off_t len)
468 | {
469 | return zbd_zones_operation(fd, ZBD_OP_CLOSE, ofst, len);
470 | }
471 |
472 | /**
473 | * @brief Finish a range of zones
474 | * @param[in] fd File descriptor obtained with \a zbd_open
475 | * @param[in] ofst Byte offset identifying the first zone to finish
476 | * @param[in] len Maximum length in bytes from \a ofst of the set of zones
477 | * to finish
478 | *
479 | * Finish the zones in the range [ofst..ofst+len].
480 | * If \a len is 0, all zones from \a ofst will be processed.
481 | *
482 | * @return Returns 0 on success and -1 otherwise.
483 | */
484 | static inline int zbd_finish_zones(int fd, off_t ofst, off_t len)
485 | {
486 | return zbd_zones_operation(fd, ZBD_OP_FINISH, ofst, len);
487 | }
488 |
489 | /**
490 | * Accessors
491 | */
492 | #define zbd_zone_type(z) ((z)->type)
493 | #define zbd_zone_cnv(z) ((z)->type == ZBD_ZONE_TYPE_CNV)
494 | #define zbd_zone_swr(z) ((z)->type == ZBD_ZONE_TYPE_SWR)
495 | #define zbd_zone_swp(z) ((z)->type == ZBD_ZONE_TYPE_SWP)
496 | #define zbd_zone_seq(z) (zbd_zone_swr(z) || zbd_zone_swp(z))
497 |
498 | #define zbd_zone_cond(z) ((z)->cond)
499 | #define zbd_zone_not_wp(z) ((z)->cond == ZBD_ZONE_COND_NOT_WP)
500 | #define zbd_zone_empty(z) ((z)->cond == ZBD_ZONE_COND_EMPTY)
501 | #define zbd_zone_imp_open(z) ((z)->cond == ZBD_ZONE_COND_IMP_OPEN)
502 | #define zbd_zone_exp_open(z) ((z)->cond == ZBD_ZONE_COND_EXP_OPEN)
503 | #define zbd_zone_is_open(z) (zbd_zone_imp_open(z) || zbd_zone_exp_open(z))
504 | #define zbd_zone_closed(z) ((z)->cond == ZBD_ZONE_COND_CLOSED)
505 | #define zbd_zone_is_active(z) (zbd_zone_is_open(z) || zbd_zone_closed(z))
506 | #define zbd_zone_full(z) ((z)->cond == ZBD_ZONE_COND_FULL)
507 | #define zbd_zone_rdonly(z) ((z)->cond == ZBD_ZONE_COND_READONLY)
508 | #define zbd_zone_offline(z) ((z)->cond == ZBD_ZONE_COND_OFFLINE)
509 |
510 | #define zbd_zone_start(z) ((z)->start)
511 | #define zbd_zone_len(z) ((z)->len)
512 | #define zbd_zone_capacity(z) ((z)->capacity)
513 | #define zbd_zone_wp(z) ((z)->wp)
514 |
515 | #define zbd_zone_flags(z) ((z)->flags)
516 | #define zbd_zone_rwp_recommended(z) ((z)->flags & ZBD_ZONE_RWP_RECOMMENDED)
517 | #define zbd_zone_non_seq_resources(z) ((z)->flags & ZBD_ZONE_NON_SEQ_RESOURCES)
518 |
519 | /**
520 | * @brief Returns a string describing a device zone model
521 | * @param[in] model Device model
522 | * @param[in] s Get abbreviated name
523 | *
524 | * Return a string (long or abbreviated) describing a device zone model.
525 | *
526 | * @return Device model string or NULL for an invalid model.
527 | */
528 | extern const char *zbd_device_model_str(enum zbd_dev_model model, bool s);
529 |
530 | /**
531 | * @brief Returns a string describing a zone type
532 | * @param[in] z Zone descriptor
533 | * @param[in] s Get abbreviated zone type name
534 | *
535 | * Return a string (long or abbreviated) describing a zone type.
536 | *
537 | * @return Zone type string or NULL for an invalid zone type.
538 | */
539 | extern const char *zbd_zone_type_str(struct zbd_zone *z, bool s);
540 |
541 | /**
542 | * @brief Returns a string describing a zone condition
543 | * @param[in] z Zone descriptor
544 | * @param[in] s Get abbreviated zone condition name
545 | *
546 | * Return a string (long or abbreviated) describing a zone condition.
547 | *
548 | * @return Zone type string or NULL for an invalid zone condition.
549 | */
550 | extern const char *zbd_zone_cond_str(struct zbd_zone *z, bool s);
551 |
552 | #ifdef __cplusplus
553 | }
554 | #endif
555 |
556 | #endif /* _LIBZBD_H_ */
557 |
--------------------------------------------------------------------------------
/lib/Makefile.am:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-or-later
2 | #
3 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 |
5 | AM_CFLAGS = \
6 | $(CFLAGS) \
7 | -Wall -Wextra -Wno-unused-parameter \
8 | -I$(top_srcdir)/include
9 |
10 | pkgconfdir = $(libdir)/pkgconfig
11 | pkgconf_DATA = libzbd.pc
12 | pkginclude_HEADERS = ../include/libzbd/zbd.h
13 |
14 | lib_LTLIBRARIES = libzbd.la
15 |
16 | EXTRA_DIST = exports
17 |
18 | CFILES = \
19 | zbd.c \
20 | zbd_utils.c
21 |
22 | HFILES = \
23 | zbd.h
24 |
25 | libzbd_la_DEPENDENCIES = exports
26 | libzbd_la_SOURCES = $(CFILES) $(HFILES)
27 | libzbd_la_CFLAGS = $(AM_CFLAGS) -fPIC
28 | libzbd_la_LDFLAGS = \
29 | -lpthread \
30 | -Wl,--version-script,exports \
31 | -version-number @LIBZBD_VERSION_LT@
32 |
33 |
34 |
--------------------------------------------------------------------------------
/lib/exports:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-or-later
2 | #
3 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 |
5 | ZBD_GLOBAL {
6 | global:
7 | zbd_set_log_level;
8 | zbd_device_is_zoned;
9 | zbd_open;
10 | zbd_close;
11 | zbd_get_info;
12 | zbd_report_zones;
13 | zbd_list_zones;
14 | zbd_zones_operation;
15 | zbd_device_model_str;
16 | zbd_zone_type_str;
17 | zbd_zone_cond_str;
18 | local:
19 | *;
20 | };
21 |
--------------------------------------------------------------------------------
/lib/libzbd.pc.in:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-or-later
2 | #
3 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 |
5 | prefix=@prefix@
6 | exec_prefix=@exec_prefix@
7 | libdir=@libdir@
8 | includedir=@includedir@
9 |
10 | Name: libzbd
11 | Description: A library simplifying management of zoned block devices
12 | Version: @PACKAGE_VERSION@
13 | Cflags: -I${includedir}
14 | Libs: -L${libdir} -lzbd
15 |
16 |
--------------------------------------------------------------------------------
/lib/zbd.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: LGPL-3.0-or-later
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | * Ting Yao
7 | */
8 | #include "zbd.h"
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | /*
21 | * Per fd device information.
22 | */
23 | #define ZBD_FD_MAX 1024
24 |
25 | static struct zbd_info *zbd_fdi[ZBD_FD_MAX];
26 |
27 | static inline struct zbd_info *zbd_get_fd(int fd)
28 | {
29 | if (fd < 0 || fd >= ZBD_FD_MAX)
30 | return NULL;
31 |
32 | return zbd_fdi[fd];
33 | }
34 |
35 | static inline void zbd_put_fd(int fd)
36 | {
37 | free(zbd_fdi[fd]);
38 | zbd_fdi[fd] = NULL;
39 | }
40 |
41 | static int zbd_dev_path(const char *filename, char **path, char **devname)
42 | {
43 | char *p;
44 |
45 | /* Follow symlinks (required for device mapped devices) */
46 | p = realpath(filename, NULL);
47 | if (!p) {
48 | zbd_error("%s: Failed to get real path %d (%s)\n",
49 | filename, errno, strerror(errno));
50 | return -1;
51 | }
52 |
53 | *path = p;
54 | *devname = basename(p);
55 |
56 | return 0;
57 | }
58 |
59 | /*
60 | * Get a block device zone model.
61 | */
62 | static enum zbd_dev_model zbd_get_dev_model(char *devname)
63 | {
64 | char str[128];
65 | int ret;
66 |
67 | /* Check that this is a zoned block device */
68 | ret = zbd_get_sysfs_attr_str(devname, "queue/zoned",
69 | str, sizeof(str));
70 | if (ret) {
71 | long long val;
72 |
73 | /*
74 | * Assume old kernel or kernel without ZBD support enabled: try
75 | * a sysfs file that must exist for all block devices. If it is
76 | * found, then this is a regular non-zoned device.
77 | */
78 | ret = zbd_get_sysfs_attr_int64(devname,
79 | "queue/logical_block_size",
80 | &val);
81 | if (ret == 0)
82 | return ZBD_DM_NOT_ZONED;
83 | return -1;
84 | }
85 |
86 | if (strcmp(str, "host-aware") == 0)
87 | return ZBD_DM_HOST_AWARE;
88 | if (strcmp(str, "host-managed") == 0)
89 | return ZBD_DM_HOST_MANAGED;
90 | if (strcmp(str, "none") == 0)
91 | return ZBD_DM_NOT_ZONED;
92 |
93 | return -1;
94 | }
95 |
96 |
97 |
98 | /*
99 | * Get max number of open/active zones.
100 | */
101 | static void zbd_get_max_resources(char *devname, struct zbd_info *zbdi)
102 | {
103 | long long val;
104 | int ret;
105 |
106 | /*
107 | * According to max_open_zones/max_active_zones sysfs documentation,
108 | * a sysfs value of 0 means no limit.
109 | *
110 | * While the ZAC/ZBC standard has special treatment for unknown,
111 | * unknown is exported to sysfs as 0.
112 | *
113 | * Default both to unlimited, and set a limit if we managed to read
114 | * a limit from sysfs successfully.
115 | */
116 | ret = zbd_get_sysfs_attr_int64(devname, "queue/max_open_zones", &val);
117 | if (ret)
118 | val = 0;
119 | zbdi->max_nr_open_zones = val;
120 |
121 | ret = zbd_get_sysfs_attr_int64(devname, "queue/max_active_zones", &val);
122 | if (ret)
123 | val = 0;
124 | zbdi->max_nr_active_zones = val;
125 | }
126 |
127 | /*
128 | * Get vendor ID.
129 | */
130 | static int zbd_get_vendor_id(char *devname, struct zbd_info *zbdi)
131 | {
132 | char str[128];
133 | int ret, n = 0;
134 |
135 | ret = zbd_get_sysfs_attr_str(devname, "device/vendor",
136 | str, sizeof(str));
137 | if (!ret)
138 | n = snprintf(zbdi->vendor_id, ZBD_VENDOR_ID_LENGTH,
139 | "%s ", str);
140 |
141 | ret = zbd_get_sysfs_attr_str(devname, "device/model",
142 | str, sizeof(str));
143 | if (!ret)
144 | n += snprintf(&zbdi->vendor_id[n], ZBD_VENDOR_ID_LENGTH - n,
145 | "%s ", str);
146 |
147 | ret = zbd_get_sysfs_attr_str(devname, "device/rev",
148 | str, sizeof(str));
149 | if (!ret)
150 | n += snprintf(&zbdi->vendor_id[n], ZBD_VENDOR_ID_LENGTH - n,
151 | "%s", str);
152 |
153 | return n > 0;
154 | }
155 |
156 | /*
157 | * Get zone size in 512B sector unit.
158 | */
159 | #ifdef BLKGETZONESZ
160 | static int zbd_get_zone_sectors(int fd, char *devname, __u32 *zone_sectors)
161 | {
162 | int ret;
163 |
164 | ret = ioctl(fd, BLKGETZONESZ, zone_sectors);
165 | if (ret) {
166 | zbd_error("ioctl BLKGETZONESZ failed %d (%s)\n",
167 | errno, strerror(errno));
168 | return -1;
169 | }
170 |
171 | return 0;
172 | }
173 | #else
174 | static int zbd_get_zone_sectors(int fd, char *devname, __u32 *zone_sectors)
175 | {
176 | long long zs;
177 | int ret;
178 |
179 | ret = zbd_get_sysfs_attr_int64(devname, "queue/chunk_sectors", &zs);
180 | if (ret) {
181 | zbd_error("Get zone size from sysfs failed\n");
182 | return -1;
183 | }
184 |
185 | if (zs >= UINT_MAX) {
186 | zbd_error("Invalid zone sectors %lld\n", zs);
187 | return -1;
188 | }
189 |
190 | *zone_sectors = zs;
191 |
192 | return 0;
193 | }
194 | #endif
195 |
196 | static int zbd_get_zone_size(int fd, char *devname, struct zbd_info *zbdi)
197 | {
198 | __u32 zone_sectors;
199 | int ret;
200 |
201 | ret = zbd_get_zone_sectors(fd, devname, &zone_sectors);
202 | if (ret)
203 | return ret;
204 |
205 | if (!zone_sectors) {
206 | zbd_error("Invalid 0 zone size\n");
207 | return -1;
208 | }
209 |
210 | zbdi->zone_sectors = zone_sectors;
211 | zbdi->zone_size = (unsigned long long)zone_sectors << SECTOR_SHIFT;
212 |
213 | return 0;
214 | }
215 |
216 | /*
217 | * Get total number of zones.
218 | */
219 | static int zbd_get_nr_zones(int fd, char *devname, struct zbd_info *zbdi)
220 | {
221 | __u32 nr_zones;
222 |
223 | #ifdef BLKGETNRZONES
224 | int ret = ioctl(fd, BLKGETNRZONES, &nr_zones);
225 | if (ret != 0) {
226 | zbd_error("ioctl BLKGETNRZONES failed %d (%s)\n",
227 | errno, strerror(errno));
228 | return -1;
229 | }
230 | #else
231 | nr_zones = (zbdi->nr_sectors + zbdi->zone_sectors - 1)
232 | / zbdi->zone_sectors;
233 | #endif
234 |
235 | if (!nr_zones) {
236 | zbd_error("Invalid 0 number of zones\n");
237 | return -1;
238 | }
239 |
240 | zbdi->nr_zones = nr_zones;
241 |
242 | return 0;
243 | }
244 |
245 | static struct zbd_info *zbd_do_get_info(int fd, char *devname)
246 | {
247 | unsigned long long size64;
248 | struct zbd_info *zbdi;
249 | int ret, size32;
250 |
251 | zbdi = malloc(sizeof(struct zbd_info));
252 | if (!zbdi)
253 | return NULL;
254 |
255 | /* Get zone model */
256 | zbdi->model = zbd_get_dev_model(devname);
257 | if (zbdi->model != ZBD_DM_HOST_AWARE &&
258 | zbdi->model != ZBD_DM_HOST_MANAGED) {
259 | zbd_error("Invalid device zone model\n");
260 | goto err;
261 | }
262 |
263 | /* Get logical block size */
264 | ret = ioctl(fd, BLKSSZGET, &size32);
265 | if (ret != 0) {
266 | zbd_error("ioctl BLKSSZGET failed %d (%s)\n",
267 | errno, strerror(errno));
268 | goto err;
269 | }
270 | zbdi->lblock_size = size32;
271 | if (zbdi->lblock_size <= 0) {
272 | zbd_error("invalid logical sector size %d\n",
273 | size32);
274 | goto err;
275 | }
276 |
277 | /* Get physical block size */
278 | ret = ioctl(fd, BLKPBSZGET, &size32);
279 | if (ret != 0) {
280 | zbd_error("ioctl BLKPBSZGET failed %d (%s)\n",
281 | errno, strerror(errno));
282 | goto err;
283 | }
284 | zbdi->pblock_size = size32;
285 | if (zbdi->pblock_size <= 0) {
286 | zbd_error("Invalid physical sector size %d\n",
287 | size32);
288 | goto err;
289 | }
290 |
291 | /* Get capacity (Bytes) */
292 | ret = ioctl(fd, BLKGETSIZE64, &size64);
293 | if (ret != 0) {
294 | zbd_error("ioctl BLKGETSIZE64 failed %d (%s)\n",
295 | errno, strerror(errno));
296 | goto err;
297 | }
298 | zbdi->nr_sectors = size64 >> SECTOR_SHIFT;
299 |
300 | zbdi->nr_lblocks = size64 / zbdi->lblock_size;
301 | if (!zbdi->nr_lblocks) {
302 | zbd_error("Invalid capacity (logical blocks)\n");
303 | goto err;
304 | }
305 |
306 | zbdi->nr_pblocks = size64 / zbdi->pblock_size;
307 | if (!zbdi->nr_pblocks) {
308 | zbd_error("Invalid capacity (physical blocks)\n");
309 | goto err;
310 | }
311 |
312 | /* Get zone size */
313 | ret = zbd_get_zone_size(fd, devname, zbdi);
314 | if (ret)
315 | goto err;
316 |
317 | /* Get number of zones */
318 | ret = zbd_get_nr_zones(fd, devname, zbdi);
319 | if (ret)
320 | goto err;
321 |
322 | /* Get max number of open/active zones */
323 | zbd_get_max_resources(devname, zbdi);
324 |
325 | /* Finish setting */
326 | if (!zbd_get_vendor_id(devname, zbdi))
327 | strncpy(zbdi->vendor_id,
328 | "Unknown", ZBD_VENDOR_ID_LENGTH - 1);
329 |
330 | return zbdi;
331 | err:
332 | free(zbdi);
333 | return NULL;
334 | }
335 |
336 | /**
337 | * zbd_device_is_zoned - Test if a physical device is zoned.
338 | */
339 | int zbd_device_is_zoned(const char *filename)
340 | {
341 | char *path = NULL, *devname = NULL;
342 | enum zbd_dev_model model;
343 | struct stat st;
344 | int ret;
345 |
346 | ret = zbd_dev_path(filename, &path, &devname);
347 | if (ret)
348 | return ret;
349 |
350 | /* Check device */
351 | if (stat(path, &st) != 0) {
352 | zbd_error("Stat device file failed %d (%s)\n",
353 | errno, strerror(errno));
354 | free(path);
355 | return 0;
356 | }
357 |
358 | if (!S_ISBLK(st.st_mode)) {
359 | free(path);
360 | return 0;
361 | }
362 |
363 | model = zbd_get_dev_model(devname);
364 |
365 | free(path);
366 |
367 | return model == ZBD_DM_HOST_AWARE ||
368 | model == ZBD_DM_HOST_MANAGED;
369 | }
370 |
371 | /**
372 | * zbd_open - open a ZBD device
373 | */
374 | int zbd_open(const char *filename, int flags, struct zbd_info *info)
375 | {
376 | char *path = NULL, *devname = NULL;
377 | struct zbd_info *zbdi;
378 | int ret, fd;
379 |
380 | if (!zbd_device_is_zoned(filename)) {
381 | zbd_error("Device %s is not a zoned block device\n",
382 | filename);
383 | return -1;
384 | }
385 |
386 | ret = zbd_dev_path(filename, &path, &devname);
387 | if (ret)
388 | return ret;
389 |
390 | /* Open block device */
391 | fd = open(path, flags | O_LARGEFILE); //direct
392 | if (fd < 0) {
393 | zbd_error("open %s failed %d (%s)\n",
394 | filename, errno, strerror(errno));
395 | goto err;
396 | }
397 |
398 | /* Get device information */
399 | zbdi = zbd_do_get_info(fd, devname);
400 | if (!zbdi)
401 | goto err;
402 |
403 | zbd_fdi[fd] = zbdi;
404 | if (info)
405 | memcpy(info, zbdi, sizeof(struct zbd_info));
406 |
407 | free(path);
408 |
409 | return fd;
410 |
411 | err:
412 | if (fd >= 0) {
413 | close(fd);
414 | fd = -1;
415 | }
416 |
417 | free(path);
418 |
419 | return fd;
420 | }
421 |
422 | /**
423 | * zbd_close - close a ZBD Device
424 | */
425 | void zbd_close(int fd)
426 | {
427 | struct zbd_info *zbdi = zbd_get_fd(fd);
428 |
429 | if (!zbdi) {
430 | zbd_error("Invalid file descriptor %d\n\n", fd);
431 | return;
432 | }
433 |
434 | close(fd);
435 | zbd_put_fd(fd);
436 | }
437 |
438 | /**
439 | * zbd_get_info - Get a ZBD device information
440 | */
441 | int zbd_get_info(int fd, struct zbd_info *info)
442 | {
443 | struct zbd_info *zbdi = zbd_get_fd(fd);
444 |
445 | if (!zbdi) {
446 | zbd_error("Invalid file descriptor %d\n\n", fd);
447 | return -1;
448 | }
449 |
450 | if (!info)
451 | return -1;
452 |
453 | memcpy(info, zbdi, sizeof(struct zbd_info));
454 |
455 | return 0;
456 | }
457 |
458 | /*
459 | * zbd_should_report_zone - Test if a zone must be reported.
460 | */
461 | static bool zbd_should_report_zone(struct zbd_zone *zone,
462 | enum zbd_report_option ro)
463 | {
464 | switch (ro) {
465 | case ZBD_RO_ALL:
466 | return true;
467 | case ZBD_RO_NOT_WP:
468 | return zbd_zone_not_wp(zone);
469 | case ZBD_RO_EMPTY:
470 | return zbd_zone_empty(zone);
471 | case ZBD_RO_IMP_OPEN:
472 | return zbd_zone_imp_open(zone);
473 | case ZBD_RO_EXP_OPEN:
474 | return zbd_zone_exp_open(zone);
475 | case ZBD_RO_CLOSED:
476 | return zbd_zone_closed(zone);
477 | case ZBD_RO_FULL:
478 | return zbd_zone_full(zone);
479 | case ZBD_RO_RDONLY:
480 | return zbd_zone_rdonly(zone);
481 | case ZBD_RO_OFFLINE:
482 | return zbd_zone_offline(zone);
483 | case ZBD_RO_RWP_RECOMMENDED:
484 | return zbd_zone_rwp_recommended(zone);
485 | case ZBD_RO_NON_SEQ:
486 | return zbd_zone_non_seq_resources(zone);
487 | default:
488 | return false;
489 | }
490 | }
491 |
492 | /*
493 | * zbd_parse_zone - Fill a zone descriptor
494 | */
495 | static inline void zbd_parse_zone(struct zbd_zone *zone, struct blk_zone *blkz,
496 | struct blk_zone_report *rep)
497 | {
498 | zone->start = blkz->start << SECTOR_SHIFT;
499 | zone->len = blkz->len << SECTOR_SHIFT;
500 | if (rep->flags & BLK_ZONE_REP_CAPACITY)
501 | zone->capacity = blkz->capacity << SECTOR_SHIFT;
502 | else
503 | zone->capacity = zone->len;
504 | zone->wp = blkz->wp << SECTOR_SHIFT;
505 |
506 | zone->type = blkz->type;
507 | zone->cond = blkz->cond;
508 | zone->flags = 0;
509 | if (blkz->reset)
510 | zone->flags |= ZBD_ZONE_RWP_RECOMMENDED;
511 | if (blkz->non_seq)
512 | zone->flags |= ZBD_ZONE_NON_SEQ_RESOURCES;
513 | }
514 |
515 | #define ZBD_REPORT_MAX_NR_ZONE 8192
516 |
517 | /**
518 | * zbd_report_zones - Get zone information
519 | */
520 | int zbd_report_zones(int fd, off_t ofst, off_t len, enum zbd_report_option ro,
521 | struct zbd_zone *zones, unsigned int *nr_zones)
522 | {
523 | struct zbd_info *zbdi = zbd_get_fd(fd);
524 | unsigned long long zone_size_mask, end;
525 | struct blk_zone_report *rep;
526 | size_t rep_size;
527 | unsigned int rep_nr_zones;
528 | unsigned int nrz, n = 0, i = 0;
529 | struct blk_zone *blkz;
530 | struct zbd_zone z;
531 | int ret = 0;
532 |
533 | if (!zbdi) {
534 | zbd_error("Invalid file descriptor %d\n\n", fd);
535 | return -1;
536 | }
537 |
538 | /*
539 | * To get zone reports, we need zones and nr_zones.
540 | * To get only the number of zones, we need only nr_zones.
541 | */
542 | if ((!zones && !nr_zones) || (zones && !nr_zones))
543 | return -1;
544 |
545 | /*
546 | * When reporting only the number of zones (zones == NULL case),
547 | * ignore the value pointed by nr_zones.
548 | */
549 | if (zones) {
550 | nrz = *nr_zones;
551 | if (!nrz)
552 | return 0;
553 | } else {
554 | nrz = 0;
555 | }
556 |
557 | zone_size_mask = zbdi->zone_size - 1;
558 | if (len == 0)
559 | len = zbdi->nr_sectors << SECTOR_SHIFT;
560 |
561 | end = ((ofst + len + zone_size_mask) & (~zone_size_mask))
562 | >> SECTOR_SHIFT;
563 | if (end > zbdi->nr_sectors)
564 | end = zbdi->nr_sectors;
565 |
566 | ofst = (ofst & (~zone_size_mask)) >> SECTOR_SHIFT;
567 | if ((unsigned long long)ofst >= zbdi->nr_sectors) {
568 | *nr_zones = 0;
569 | return 0;
570 | }
571 |
572 | /* Get all zones information */
573 | rep_nr_zones = ZBD_REPORT_MAX_NR_ZONE;
574 | if (nrz && nrz < rep_nr_zones)
575 | rep_nr_zones = nrz;
576 | rep_size = sizeof(struct blk_zone_report) +
577 | sizeof(struct blk_zone) * rep_nr_zones;
578 | rep = (struct blk_zone_report *)malloc(rep_size);
579 | if (!rep) {
580 | zbd_error("%d: No memory for array of zones\n\n", fd);
581 | return -ENOMEM;
582 | }
583 |
584 | blkz = (struct blk_zone *)(rep + 1);
585 | while ((!nrz || n < nrz) && (unsigned long long)ofst < end) {
586 |
587 | memset(rep, 0, rep_size);
588 | rep->sector = ofst;
589 | rep->nr_zones = rep_nr_zones;
590 |
591 | ret = ioctl(fd, BLKREPORTZONE, rep);
592 | if (ret != 0) {
593 | ret = -errno;
594 | zbd_error("%d: ioctl BLKREPORTZONE at %llu failed %d (%s)\n",
595 | fd, (unsigned long long)ofst,
596 | errno, strerror(errno));
597 | goto out;
598 | }
599 |
600 | if (!rep->nr_zones)
601 | break;
602 |
603 | for (i = 0; i < rep->nr_zones; i++) {
604 | if ((nrz && (n >= nrz)) ||
605 | ((unsigned long long)ofst >= end))
606 | break;
607 |
608 | zbd_parse_zone(&z, &blkz[i], rep);
609 | if (zbd_should_report_zone(&z, ro)) {
610 | if (zones)
611 | memcpy(&zones[n], &z, sizeof(z));
612 | n++;
613 | }
614 |
615 | ofst = blkz[i].start + blkz[i].len;
616 | }
617 | }
618 |
619 | /* Return number of zones */
620 | *nr_zones = n;
621 |
622 | out:
623 | free(rep);
624 |
625 | return ret;
626 | }
627 |
628 | /**
629 | * zbd_list_zones - Get zone information
630 | */
631 | int zbd_list_zones(int fd, off_t ofst, off_t len,
632 | enum zbd_report_option ro,
633 | struct zbd_zone **pzones, unsigned int *pnr_zones)
634 | {
635 | struct zbd_info *zbdi = zbd_get_fd(fd);
636 | struct zbd_zone *zones = NULL;
637 | unsigned int nr_zones = 0;
638 | int ret;
639 |
640 | if (!zbdi) {
641 | zbd_error("Invalid file descriptor %d\n\n", fd);
642 | return -1;
643 | }
644 |
645 | /* Get number of zones */
646 | ret = zbd_report_nr_zones(fd, ofst, len, ro, &nr_zones);
647 | if (ret < 0)
648 | return ret;
649 |
650 | if (!nr_zones)
651 | goto out;
652 |
653 | /* Allocate zone array */
654 | zones = (struct zbd_zone *) calloc(nr_zones, sizeof(struct zbd_zone));
655 | if (!zones)
656 | return -ENOMEM;
657 |
658 | /* Get zones information */
659 | ret = zbd_report_zones(fd, ofst, len, ro, zones, &nr_zones);
660 | if (ret != 0) {
661 | zbd_error("%d: zbd_report_zones failed %d\n",
662 | fd, ret);
663 | free(zones);
664 | return ret;
665 | }
666 |
667 | out:
668 | *pzones = zones;
669 | *pnr_zones = nr_zones;
670 | return 0;
671 | }
672 |
673 | /*
674 | * BLKOPENZONE, BLKCLOSEZONE and BLKFINISHZONE ioctl commands
675 | * were introduced with kernel 5.5. If they are not defined on the
676 | * current system, manually define these operations here to generate
677 | * code portable to newer kernels.
678 | */
679 | #ifndef BLKOPENZONE
680 | #define BLKOPENZONE _IOW(0x12, 134, struct blk_zone_range)
681 | #endif
682 | #ifndef BLKCLOSEZONE
683 | #define BLKCLOSEZONE _IOW(0x12, 135, struct blk_zone_range)
684 | #endif
685 | #ifndef BLKFINISHZONE
686 | #define BLKFINISHZONE _IOW(0x12, 136, struct blk_zone_range)
687 | #endif
688 |
689 | #ifndef ENOIOCTLCMD
690 | #define ENOIOCTLCMD 515
691 | #endif
692 |
693 | /**
694 | * zbd_zone_operation - Execute an operation on a zone
695 | */
696 | int zbd_zones_operation(int fd, enum zbd_zone_op op, off_t ofst, off_t len)
697 | {
698 | struct zbd_info *zbdi = zbd_get_fd(fd);
699 | unsigned long long zone_size_mask, end;
700 | struct blk_zone_range range;
701 | const char *ioctl_name;
702 | unsigned long ioctl_op;
703 | int ret;
704 |
705 | if (!zbdi) {
706 | zbd_error("Invalid file descriptor %d\n\n", fd);
707 | return -1;
708 | }
709 |
710 | zone_size_mask = zbdi->zone_size - 1;
711 | if (len == 0)
712 | len = zbdi->nr_sectors << SECTOR_SHIFT;
713 |
714 | end = ((ofst + len + zone_size_mask) & (~zone_size_mask))
715 | >> SECTOR_SHIFT;
716 | if (end > zbdi->nr_sectors)
717 | end = zbdi->nr_sectors;
718 |
719 | /* Check the operation */
720 | switch (op) {
721 | case ZBD_OP_RESET:
722 | ioctl_name = "BLKRESETZONE";
723 | ioctl_op = BLKRESETZONE;
724 | break;
725 | case ZBD_OP_OPEN:
726 | ioctl_name = "BLKOPENZONE";
727 | ioctl_op = BLKOPENZONE;
728 | break;
729 | case ZBD_OP_CLOSE:
730 | ioctl_name = "BLKCLOSEZONE";
731 | ioctl_op = BLKCLOSEZONE;
732 | break;
733 | case ZBD_OP_FINISH:
734 | ioctl_name = "BLKFINISHZONE";
735 | ioctl_op = BLKFINISHZONE;
736 | break;
737 | default:
738 | zbd_error("Invalid zone operation 0x%x\n", op);
739 | errno = EINVAL;
740 | return -1;
741 | }
742 |
743 | ofst = (ofst & (~zone_size_mask)) >> SECTOR_SHIFT;
744 | if ((unsigned long long)ofst >= zbdi->nr_sectors ||
745 | end == (unsigned long long)ofst)
746 | return 0;
747 |
748 | /* Execute the operation */
749 | range.sector = ofst;
750 | range.nr_sectors = end - ofst;
751 | ret = ioctl(fd, ioctl_op, &range);
752 | if (ret != 0) {
753 | if (errno == ENOIOCTLCMD || errno == ENOTTY) {
754 | zbd_error("ioctl %s is not supported\n",
755 | ioctl_name);
756 | errno = ENOTSUP;
757 | } else {
758 | zbd_error("ioctl %s failed %d (%s)\n",
759 | ioctl_name, errno, strerror(errno));
760 | }
761 | return -1;
762 | }
763 |
764 | return 0;
765 | }
766 |
--------------------------------------------------------------------------------
/lib/zbd.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: LGPL-3.0-or-later */
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | * Ting Yao
7 | */
8 | #ifndef __LIBZBD_INTERNAL_H__
9 | #define __LIBZBD_INTERNAL_H__
10 |
11 | #define _GNU_SOURCE
12 |
13 | #include "config.h"
14 | #include "libzbd/zbd.h"
15 |
16 | #include
17 | #include
18 |
19 | /*
20 | * 512B sector size shift.
21 | */
22 | #define SECTOR_SHIFT 9
23 |
24 | /*
25 | * Handle kernel zone capacity support
26 | */
27 | #ifndef HAVE_BLK_ZONE_REP_V2
28 | #define BLK_ZONE_REP_CAPACITY (1 << 0)
29 |
30 | struct blk_zone_v2 {
31 | __u64 start; /* Zone start sector */
32 | __u64 len; /* Zone length in number of sectors */
33 | __u64 wp; /* Zone write pointer position */
34 | __u8 type; /* Zone type */
35 | __u8 cond; /* Zone condition */
36 | __u8 non_seq; /* Non-sequential write resources active */
37 | __u8 reset; /* Reset write pointer recommended */
38 | __u8 resv[4];
39 | __u64 capacity; /* Zone capacity in number of sectors */
40 | __u8 reserved[24];
41 | };
42 | #define blk_zone blk_zone_v2
43 |
44 | struct blk_zone_report_v2 {
45 | __u64 sector;
46 | __u32 nr_zones;
47 | __u32 flags;
48 | struct blk_zone zones[0];
49 | };
50 | #define blk_zone_report blk_zone_report_v2
51 | #endif /* HAVE_BLK_ZONE_REP_V2 */
52 |
53 | extern int zbd_get_sysfs_attr_int64(char *devname, const char *attr,
54 | long long *val);
55 | extern int zbd_get_sysfs_attr_str(char *devname, const char *attr,
56 | char *val, int val_len);
57 |
58 | /*
59 | * Library log level (per thread).
60 | */
61 | extern __thread int zbd_log_level;
62 |
63 | #define zbd_print(stream,format,args...) \
64 | do { \
65 | fprintf((stream), format, ## args); \
66 | fflush(stream); \
67 | } while (0)
68 |
69 | #define zbd_print_level(l,stream,format,args...) \
70 | do { \
71 | if ((l) <= zbd_log_level) \
72 | zbd_print((stream), "(libzbd) " format, \
73 | ## args); \
74 | } while (0)
75 |
76 | #define zbd_error(format,args...) \
77 | zbd_print_level(ZBD_LOG_ERROR, stderr, "[ERROR] " format, ##args)
78 |
79 | #define zbd_debug(format,args...) \
80 | zbd_print_level(ZBD_LOG_DEBUG, stdout, format, ##args)
81 |
82 | #define zbd_panic(format,args...) \
83 | do { \
84 | zbd_print_level(ZBD_LOG_ERROR, \
85 | stderr, \
86 | "[PANIC] " format, \
87 | ##args); \
88 | assert(0); \
89 | } while (0)
90 |
91 | #define zbd_assert(cond) \
92 | do { \
93 | if (!(cond)) \
94 | zbd_panic("Condition %s failed\n", \
95 | # cond); \
96 | } while (0)
97 |
98 | #endif
99 |
100 | /* __LIBZBD_INTERNAL_H__ */
101 |
--------------------------------------------------------------------------------
/lib/zbd_utils.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: LGPL-3.0-or-later
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | * Ting Yao
7 | */
8 | #include "zbd.h"
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | /*
17 | * Log level.
18 | */
19 | __thread int zbd_log_level = ZBD_LOG_NONE;
20 |
21 | void zbd_set_log_level(enum zbd_log_level level)
22 | {
23 | switch (level) {
24 | case ZBD_LOG_NONE:
25 | case ZBD_LOG_ERROR:
26 | case ZBD_LOG_DEBUG:
27 | zbd_log_level = level;
28 | break;
29 | default:
30 | fprintf(stderr, "libzbd: Invalid log level %d\n",
31 | level);
32 | break;
33 | }
34 | }
35 |
36 | /*
37 | * To handle string conversions.
38 | */
39 | struct zbd_str {
40 | unsigned int val;
41 | const char *str;
42 | const char *str_short;
43 | };
44 |
45 | static const char *zbd_get_str(struct zbd_str *str, unsigned int val, bool s)
46 | {
47 | unsigned int i = 0;
48 |
49 | while (str[i].val != UINT_MAX) {
50 | if (str[i].val == val)
51 | break;
52 | i++;
53 | }
54 |
55 | if (s)
56 | return str[i].str_short;
57 |
58 | return str[i].str;
59 | }
60 |
61 | static struct zbd_str zbd_dm_str[] = {
62 | { ZBD_DM_HOST_MANAGED, "host-managed", "HM" },
63 | { ZBD_DM_HOST_AWARE, "host-aware", "HA" },
64 | { ZBD_DM_NOT_ZONED, "not-zoned", "NZ" },
65 | { UINT_MAX, "unknown", "??" },
66 | };
67 |
68 | /**
69 | * zbd_device_model_str - Returns a device zone model name
70 | */
71 | const char *zbd_device_model_str(enum zbd_dev_model model, bool s)
72 | {
73 | return zbd_get_str(zbd_dm_str, model, s);
74 | }
75 |
76 | static struct zbd_str zbd_ztype_str[] = {
77 | { ZBD_ZONE_TYPE_CNV, "conventional", "cnv" },
78 | { ZBD_ZONE_TYPE_SWR, "seq-write-required", "swr" },
79 | { ZBD_ZONE_TYPE_SWP, "seq-write-preferred", "swp" },
80 | { UINT_MAX, "unknown", "???" }
81 | };
82 |
83 | /**
84 | * zbd_zone_type_str - returns a string describing a zone type
85 | */
86 | const char *zbd_zone_type_str(struct zbd_zone *z, bool s)
87 | {
88 | return zbd_get_str(zbd_ztype_str, z->type, s);
89 | }
90 |
91 | static struct zbd_str zbd_zcond_str[] = {
92 | { ZBD_ZONE_COND_NOT_WP, "not-write-pointer", "nw" },
93 | { ZBD_ZONE_COND_EMPTY, "empty", "em" },
94 | { ZBD_ZONE_COND_FULL, "full", "fu" },
95 | { ZBD_ZONE_COND_IMP_OPEN, "open-implicit", "oi" },
96 | { ZBD_ZONE_COND_EXP_OPEN, "open-explicit", "oe" },
97 | { ZBD_ZONE_COND_CLOSED, "closed", "cl" },
98 | { ZBD_ZONE_COND_READONLY, "read-only", "ro" },
99 | { ZBD_ZONE_COND_OFFLINE, "offline", "ol" },
100 | { UINT_MAX, "unknown", "??" }
101 | };
102 |
103 | /**
104 | * zbd_zone_cond_str - Returns a string describing a zone condition
105 | */
106 | const char *zbd_zone_cond_str(struct zbd_zone *z, bool s)
107 | {
108 | return zbd_get_str(zbd_zcond_str, z->cond, s);
109 | }
110 |
111 | /*
112 | * Strip a string of trailing spaces and carriage return.
113 | */
114 | static int zbd_str_strip(char *str)
115 | {
116 | int i = strlen(str) - 1;
117 |
118 | while (i >= 0) {
119 | if (str[i] == ' ' ||
120 | str[i] == '\t' ||
121 | str[i] == '\r' ||
122 | str[i] == '\n') {
123 | str[i] = '\0';
124 | i--;
125 | } else {
126 | break;
127 | }
128 | }
129 |
130 | return i + 1;
131 | }
132 |
133 | static int zbd_get_sysfs_attr(char *devname, const char *attr,
134 | char *str, int str_len)
135 | {
136 | char attr_path[128];
137 | FILE *file;
138 | int ret = 0;
139 |
140 | snprintf(attr_path, sizeof(attr_path), "/sys/block/%s/%s",
141 | devname, attr);
142 | file = fopen(attr_path, "r");
143 | if (!file)
144 | return -ENOENT;
145 |
146 | if (!fgets(str, str_len, file)) {
147 | ret = -EINVAL;
148 | goto close;
149 | }
150 |
151 | if (!zbd_str_strip(str))
152 | ret = -EINVAL;
153 |
154 | close:
155 | fclose(file);
156 |
157 | return ret;
158 | }
159 |
160 | int zbd_get_sysfs_attr_int64(char *devname, const char *attr, long long *val)
161 | {
162 | char str[128];
163 | int ret;
164 |
165 | ret = zbd_get_sysfs_attr(devname, attr, str, 128);
166 | if (ret)
167 | return ret;
168 |
169 | *val = atoll(str);
170 |
171 | return 0;
172 | }
173 |
174 | int zbd_get_sysfs_attr_str(char *devname, const char *attr,
175 | char *val, int val_len)
176 | {
177 | return zbd_get_sysfs_attr(devname, attr, val, val_len);
178 | }
179 |
--------------------------------------------------------------------------------
/libzbd.spec:
--------------------------------------------------------------------------------
1 | Name: %{pkg_name}
2 | Version: %{pkg_version}
3 | Release: 1%{?dist}
4 | Summary: A library to control zoned block devices
5 |
6 | License: LGPLv3+ and GPLv3+
7 | URL: https://github.com/westerndigitalcorporation/%{name}
8 | Source0: %{url}/releases/download/v%{version}/%{name}-%{version}.tar.gz
9 |
10 | BuildRequires: desktop-file-utils
11 | BuildRequires: gtk3-devel
12 | BuildRequires: autoconf
13 | BuildRequires: automake
14 | BuildRequires: libtool
15 | BuildRequires: make
16 | BuildRequires: gcc
17 |
18 | %description
19 | libzbd is a library providing functions simplifying the management and
20 | use of zoned block devices using the kernel ioctl interface.
21 |
22 | # Development headers package
23 | %package devel
24 | Summary: Development header files for libzbd
25 | Requires: %{name}%{?_isa} = %{version}-%{release}
26 |
27 | %description devel
28 | This package provides development header files for libzbd.
29 |
30 | # Command line tools package
31 | %package cli-tools
32 | Summary: Command line tools using libzbd
33 | Requires: %{name}%{?_isa} = %{version}-%{release}
34 |
35 | %description cli-tools
36 | This package provides command line tools using libzbd.
37 |
38 | # Graphic tools package
39 | %package gtk-tools
40 | Summary: GTK tools using libzbd
41 | Requires: %{name}%{?_isa} = %{version}-%{release}
42 |
43 | %description gtk-tools
44 | This package provides GTK-based graphical tools using libzbd.
45 |
46 | %prep
47 | %autosetup
48 |
49 | %build
50 | sh autogen.sh
51 | %configure --libdir="%{_libdir}" --includedir="%{_includedir}"
52 | %make_build
53 |
54 | %install
55 | %make_install PREFIX=%{_prefix}
56 | chmod -x ${RPM_BUILD_ROOT}%{_mandir}/man8/*.8*
57 |
58 | find ${RPM_BUILD_ROOT} -name '*.la' -delete
59 |
60 | desktop-file-validate %{buildroot}/%{_datadir}/applications/gzbd.desktop
61 | desktop-file-validate %{buildroot}/%{_datadir}/applications/gzbd-viewer.desktop
62 |
63 | %ldconfig_scriptlets
64 |
65 | %files
66 | %{_libdir}/*.so.*
67 | %exclude %{_libdir}/*.a
68 | %exclude %{_libdir}/pkgconfig/*.pc
69 | %license LICENSES/LGPL-3.0-or-later.txt
70 | %doc README.md
71 |
72 | %files devel
73 | %{_includedir}/*
74 | %{_libdir}/*.so
75 | %{_libdir}/pkgconfig/*.pc
76 | %license LICENSES/LGPL-3.0-or-later.txt
77 |
78 | %files cli-tools
79 | %{_bindir}/zbd
80 | %{_mandir}/man8/zbd.8*
81 | %license LICENSES/GPL-3.0-or-later.txt
82 |
83 | %files gtk-tools
84 | %{_bindir}/gzbd
85 | %{_datadir}/polkit-1/actions/org.gnome.gzbd.policy
86 | %{_datadir}/applications/gzbd.desktop
87 | %{_datadir}/pixmaps/gzbd.png
88 | %{_bindir}/gzbd-viewer
89 | %{_datadir}/polkit-1/actions/org.gnome.gzbd-viewer.policy
90 | %{_datadir}/applications/gzbd-viewer.desktop
91 | %{_datadir}/pixmaps/gzbd-viewer.png
92 | %{_mandir}/man8/gzbd.8*
93 | %{_mandir}/man8/gzbd-viewer.8*
94 | %license LICENSES/GPL-3.0-or-later.txt
95 |
96 | %changelog
97 | * Tue Jan 31 2023 Damien Le Moal 2.0.4
98 | - Update to version 2.0.4
99 |
--------------------------------------------------------------------------------
/m4/dontremove:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: CC0-1.0
2 | #
3 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 |
5 | the only purpose of this file is to keep the m4 directory in git
6 |
--------------------------------------------------------------------------------
/tools/Makefile.am:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: LGPL-3.0-or-later
2 | #
3 | # SPDX-FileCopyrightText: 2021 Western Digital Corporation or its affiliates.
4 |
5 | SUBDIRS = . $(subdirs)
6 |
7 | AM_CFLAGS = \
8 | $(CFLAGS) \
9 | -Wall -Wextra -Wno-unused-parameter \
10 | -I$(top_srcdir)/include
11 |
12 | libzbd_ldadd = $(top_builddir)/lib/libzbd.la
13 |
14 | bin_PROGRAMS =
15 | dist_man8_MANS =
16 |
17 | include cli/Makefile.am
18 |
19 | if BUILD_GUI
20 |
21 | polkitdir = $(datadir)/polkit-1/actions
22 | polkit_action_in_FILES =
23 |
24 | desktopdir = $(datadir)/applications
25 | desktop_in_FILES =
26 |
27 | pixmapdir = $(datadir)/pixmaps
28 | pixmap_FILES =
29 |
30 | bin_subst = sed -e 's,[@]bindir[@],$(bindir),g'
31 |
32 | include gui/Makefile.am
33 | include viewer/Makefile.am
34 |
35 | polkit_action_FILES = $(polkit_action_in_FILES:.policy.in=.policy)
36 | polkit_DATA = $(polkit_action_FILES)
37 |
38 | desktop_FILES = $(desktop_in_FILES:.desktop.in=.desktop)
39 | desktop_DATA = $(desktop_FILES)
40 |
41 | pixmap_DATA = $(pixmap_FILES)
42 |
43 | CLEANFILES = $(desktop_FILES) $(polkit_action_FILES)
44 |
45 | EXTRA_DIST = \
46 | $(polkit_action_in_FILES) \
47 | $(desktop_in_FILES) \
48 | $(pixmap_FILES)
49 |
50 | endif
51 |
--------------------------------------------------------------------------------
/tools/cli/Makefile.am:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: GPL-3.0-or-later
2 | #
3 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 |
5 | bin_PROGRAMS += zbd
6 | zbd_SOURCES = \
7 | cli/zbd.c \
8 | cli/zbd_dump.c \
9 | cli/zbd.h
10 | zbd_LDADD = $(libzbd_ldadd)
11 |
12 | dist_man8_MANS += cli/zbd.8
13 |
--------------------------------------------------------------------------------
/tools/cli/zbd.8:
--------------------------------------------------------------------------------
1 | .\" SPDX-License-Identifier: GPL-3.0-or-later
2 | .\" SPDX-FileCopyrightText: 2020, Western Digital Corporation or its affiliates.
3 | .\" Written by Damien Le Moal
4 | .\"
5 | .TH ZBD 8
6 | .SH NAME
7 | zbd \- manage zoned block devices
8 |
9 | .SH SYNOPSIS
10 | .B zbd
11 | .I command
12 | [options]
13 | .I device
14 |
15 | .SH DESCRIPTION
16 | .B zbd
17 | is used to manipulate zones of a zoned block device. Zoned block devies are
18 | block devices that support the SCSI Zoned Block Commands (ZBC),
19 | ATA Zoned-device ATA Commands (ZAC) or NVMe Zoned NameSpace commands (ZNS).
20 | The zones to operate on can be specified using the offset and length options.
21 | .PP
22 | The
23 | .I device
24 | argument must be the pathname of the target zoned block device.
25 |
26 | .SH COMMANDS
27 |
28 | By default, all commands will operate from the zone at device offset 0 and
29 | operate on all zones. The common options \fB-ofst\fP and \fB-len\fP can be
30 | used to modify the operation range of commands.
31 |
32 | .SS report
33 | The command \fBzbd report\fP is used to obtain and display the device zone
34 | information.
35 | .PP
36 | By default, the command will report all zones from the start of the device
37 | up to the last zone of the device. Options may be used to modify this behavior,
38 | changing the starting zone or the size of the report.
39 |
40 | .B Report output
41 | .TS
42 | tab(:);
43 | l l.
44 | Zone:Zone number
45 | type:Type of the zone
46 | ofst:Zone start offset in Bytes
47 | len:Zone length in Bytes
48 | cap:Zone usable capacity in Bytes
49 | wp:Zone write pointer position in Bytes
50 | cond:Zone condition
51 | non_seq:Non-sequential write resources active
52 | reset:Reset write pointer recommended
53 | .TE
54 |
55 | .B Zone types
56 | .TS
57 | tab(:);
58 | l l.
59 | cnv:Conventional
60 | swr:Sequential write required
61 | swp:Sequential write preferred
62 | ???:Unknown (should not be reported)
63 | .TE
64 |
65 | .B Zone conditions
66 | .TS
67 | tab(:);
68 | l l.
69 | nw:Not write pointer
70 | em:Empty
71 | fu:Full
72 | oe:Explicitly opened
73 | oi:Implicitly opened
74 | cl:Closed
75 | ol:Offline
76 | ro:Read only
77 | ??:Reserved conditions (should not be reported)
78 | .TE
79 |
80 | .SS reset
81 | The command \fBzbd reset\fP is used to reset the write pointer of one or
82 | more zones.
83 |
84 | .SS open
85 | The command \fBzbd open\fP is used to explicitly open one or more zones.
86 |
87 | .SS close
88 | The command \fBzbd close\fP is used to close one or more zones.
89 |
90 | .SS finish
91 | The command \fBzbd finish\fP is used to finish (transition to full) one
92 | or more zones.
93 |
94 | .SS dump
95 | Save the zone information and zone data of a zoned device to files. The
96 | files are by default saved in the current working directory. A different
97 | output path can be specified using the option \fB-d\fP. The file names are
98 | prefixed by default using the device base name. The device zone information
99 | is saved in the file \fI_zone_info.dump\fP and the device zone data
100 | is saved in the file \fI_zone_data.dump\fP. The file name prefix
101 | can be changed using the option \fB-f\fP.
102 |
103 | .PP
104 | The \fBdump\fP command will save the data of all conventional zones and the
105 | written data of all sequential zones in the operation range. The options
106 | \fB-ofst\fP and \fB-len\fP can be used to limit in size the operation range.
107 |
108 | .PP
109 | The zone information file always saves zone information for all zones of the
110 | device, regardless of the specified operation range. This file can be specified
111 | in place of a device name with the \fBreport\fP command to inspect its content.
112 |
113 | .SS restore
114 | Set a zoned block device zone status and zone data according to the zone
115 | information and zoned data saved in files generated using the \fBdump\fP
116 | command. The resulting state of the target device will be identical to that
117 | of the device used to generate the dump files. The \fBrestore\fP command
118 | will only operate on the zone range that was used with the \fBdump\fP
119 | command. The target zoned block device must be compatible with the zoned
120 | block device used as the source for the dump operation. That is, the target
121 | device must have the same capacity and zone configuration (zone type, zone
122 | size and zone capacity) as the source device.
123 |
124 | .PP
125 | The path and name prefix of the dump files to use for restoring a device
126 | can be changed using the options \fB-d\fP and \fB-f\fP.
127 |
128 | .SH OPTIONS
129 | Options applicable to all commands are as follows.
130 | .TP
131 | .BR \-v
132 | Verbose mode (for debug).
133 | .TP
134 | .BR \-i
135 | Display device information before executing the command.
136 | .TP
137 | .BR "\-ofst " \fIoffset\fP
138 | Start offset in Bytes of the first zone to operate on.
139 | .TP
140 | .BR "\-len " \fIlength\fP
141 | Length in Bytes of the range of zones to operate on.
142 | .TP
143 | .BR \-h , " \-\-help"
144 | Display help text and exit.
145 | .TP
146 | Options applicable only to the \fBzbd report\fP command are as follows.
147 | .TP
148 | .BR \-csv
149 | Output zone information using the comma-separated (csv) format.
150 | .TP
151 | .BR \-n
152 | Display the number of zones that are to be reported.
153 | .TP
154 | .BR \-c
155 | Display the total capacity of all reported zones. When used with the
156 | \fB-n\fP and \fB-csv\fP options, the number of zones is output first.
157 | .TP
158 | .BR "\-ro " \fIfilter\fP
159 | Sepcify reporting option to filter the zone report. \fIfilter\fP
160 | can be one of the following.
161 | .TS
162 | tab(:);
163 | l l.
164 | em:Empty zones
165 | oi:Implicitly opened zones
166 | oe:Explicitly opened zones
167 | cl:Closed zones
168 | fu:Full zones
169 | ro:Read only zones
170 | ol:Offline zones
171 | nw:Conventional zones
172 | ns:Non_seq write resource zones
173 | rw:Reset write pointer recommended zones
174 | .TE
175 |
176 | .SH AUTHOR
177 | .nf
178 | Damien Le Moal
179 | .fi
180 |
181 | .SH SEE ALSO
182 | .BR blkzone (8)
183 |
184 | .SH AVAILABILITY
185 | The zbd command is part of the libzbd library available from
186 | .UR https://\:github.com\:/westerndigitalcorporation\:/libzbd
187 | .UE .
188 |
--------------------------------------------------------------------------------
/tools/cli/zbd.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | * Ting Yao
7 | */
8 | #include "zbd.h"
9 |
10 | static int zbd_mgmt(int fd, struct zbd_opts *opts)
11 | {
12 | int ret;
13 |
14 | /* Check zone range */
15 | if (opts->ofst % opts->dev_info.zone_size ||
16 | opts->len % opts->dev_info.zone_size) {
17 | fprintf(stderr, "Invalid unaligned offset/length\n");
18 | return 1;
19 | }
20 |
21 | switch (opts->cmd) {
22 | case ZBD_RESET:
23 | ret = zbd_reset_zones(fd, opts->ofst, opts->len);
24 | break;
25 | case ZBD_OPEN:
26 | ret = zbd_open_zones(fd, opts->ofst, opts->len);
27 | break;
28 | case ZBD_CLOSE:
29 | ret = zbd_close_zones(fd, opts->ofst, opts->len);
30 | break;
31 | case ZBD_FINISH:
32 | ret = zbd_finish_zones(fd, opts->ofst, opts->len);
33 | break;
34 | default:
35 | fprintf(stderr, "Invalid operation\n");
36 | return 1;
37 | }
38 |
39 | if (ret)
40 | fprintf(stderr, "Zone operation failed %d (%s)\n",
41 | errno, strerror(errno));
42 |
43 | return ret;
44 | }
45 |
46 | static void zbd_print_zone(struct zbd_opts *opts, struct zbd_zone *z)
47 | {
48 | unsigned int zno = zbd_zone_start(z) / opts->dev_info.zone_size;
49 |
50 | if (opts->rep_csv) {
51 | printf("%05u, %u, %014llu, %014llu, %014llu, %014llu, 0x%01x, %01d, %01d\n",
52 | zno,
53 | (unsigned int)zbd_zone_type(z),
54 | zbd_zone_start(z) / opts->unit,
55 | zbd_zone_len(z) / opts->unit,
56 | zbd_zone_capacity(z) / opts->unit,
57 | zbd_zone_wp(z) / opts->unit,
58 | zbd_zone_cond(z),
59 | zbd_zone_non_seq_resources(z) ? 1 : 0,
60 | zbd_zone_rwp_recommended(z) ? 1 : 0);
61 | return;
62 | }
63 |
64 | if (zbd_zone_cnv(z)) {
65 | printf("Zone %05u: %s, ofst %014llu, len %014llu, cap %014llu\n",
66 | zno,
67 | zbd_zone_type_str(z, true),
68 | zbd_zone_start(z) / opts->unit,
69 | zbd_zone_len(z) / opts->unit,
70 | zbd_zone_capacity(z) / opts->unit);
71 | return;
72 | }
73 |
74 | if (zbd_zone_seq(z)) {
75 | printf("Zone %05u: %s, ofst %014llu, len %014llu, cap %014llu, "
76 | "wp %014llu, %s, non_seq %01d, reset %01d\n",
77 | zno,
78 | zbd_zone_type_str(z, true),
79 | zbd_zone_start(z) / opts->unit,
80 | zbd_zone_len(z) / opts->unit,
81 | zbd_zone_capacity(z) / opts->unit,
82 | zbd_zone_wp(z) / opts->unit,
83 | zbd_zone_cond_str(z, true),
84 | zbd_zone_non_seq_resources(z) ? 1 : 0,
85 | zbd_zone_rwp_recommended(z) ? 1 : 0);
86 | return;
87 | }
88 |
89 | printf("Zone %05u: unknown type 0x%01x, ofst %014llu, len %014llu\n",
90 | zno, zbd_zone_type(z),
91 | zbd_zone_start(z) / opts->unit,
92 | zbd_zone_len(z) / opts->unit);
93 | }
94 |
95 | static int zbd_report(int fd, struct zbd_opts *opts)
96 | {
97 | struct zbd_zone *zones = NULL;
98 | unsigned int i, nz;
99 | int ret;
100 |
101 | /* Allocate zone array */
102 | nz = (opts->len + opts->dev_info.zone_size - 1) /
103 | opts->dev_info.zone_size;
104 | if (!nz)
105 | return 0;
106 |
107 | zones = (struct zbd_zone *) calloc(nz, sizeof(struct zbd_zone));
108 | if (!zones) {
109 | fprintf(stderr, "No memory\n");
110 | return 1;
111 | }
112 |
113 | if (!opts->rep_dump) {
114 | /* Get zone information from the device */
115 | ret = zbd_report_zones(fd, opts->ofst, opts->len, opts->rep_opt,
116 | zones, &nz);
117 | if (ret != 0) {
118 | fprintf(stderr, "zbd_report_zones() failed %d\n", ret);
119 | ret = 1;
120 | goto out;
121 | }
122 | } else {
123 | /* Get zone information from the dump file */
124 | ret = zbd_dump_report_zones(fd, opts, zones, &nz);
125 | if (ret != 0) {
126 | ret = 1;
127 | goto out;
128 | }
129 | }
130 |
131 | if (opts->rep_num_zones) {
132 | if (opts->rep_csv) {
133 | if (!opts->rep_capacity)
134 | printf("%u\n", nz);
135 | else
136 | printf("%u, ", nz);
137 | } else {
138 | printf("%u zones\n", nz);
139 | }
140 | if (!opts->rep_capacity)
141 | goto out;
142 | }
143 |
144 | if (opts->rep_capacity) {
145 | unsigned long long capacity = 0;
146 |
147 | for (i = 0; i < nz; i++)
148 | capacity += zbd_zone_capacity(&zones[i]) / opts->unit;
149 |
150 | if (opts->rep_csv) {
151 | printf("%llu\n", capacity);
152 | } else {
153 | if (opts->unit != 1)
154 | printf("%llu x %zu B total zone capacity\n",
155 | capacity, opts->unit);
156 | else
157 | printf("%llu B total zone capacity\n",
158 | capacity);
159 | }
160 | goto out;
161 | }
162 |
163 | if (opts->rep_csv)
164 | printf("zone num, type, ofst, len, cap, wp, cond, non_seq, reset\n");
165 | for (i = 0; i < nz; i++)
166 | zbd_print_zone(opts, &zones[i]);
167 |
168 | out:
169 | free(zones);
170 | return ret;
171 | }
172 |
173 | struct zbd_action {
174 | int (*action)(int, struct zbd_opts *);
175 | int flags;
176 | };
177 |
178 | static struct zbd_action zact[] = {
179 | { zbd_report, O_RDONLY }, /* ZBD_REPORT */
180 | { zbd_mgmt, O_WRONLY }, /* ZBD_RESET */
181 | { zbd_mgmt, O_WRONLY }, /* ZBD_OPEN */
182 | { zbd_mgmt, O_WRONLY }, /* ZBD_CLOSE */
183 | { zbd_mgmt, O_WRONLY }, /* ZBD_FINISH */
184 | { zbd_dump, O_RDONLY }, /* ZBD_DUMP */
185 | { zbd_restore, O_RDWR | O_DIRECT }, /* ZBD_RESTORE */
186 | };
187 |
188 | static void zbd_print_dev_info(struct zbd_opts *opts)
189 | {
190 | if (opts->cmd == ZBD_REPORT && opts->rep_csv)
191 | return;
192 |
193 | printf("Device %s:\n", opts->dev_path);
194 |
195 | printf(" Vendor ID: %s\n",
196 | opts->dev_info.vendor_id);
197 | printf(" Zone model: %s\n",
198 | zbd_device_model_str(opts->dev_info.model, false));
199 | printf(" Capacity: %.03F GB (%llu 512-bytes sectors)\n",
200 | (double)(opts->dev_info.nr_sectors << 9) / 1000000000,
201 | opts->dev_info.nr_sectors);
202 | printf(" Logical blocks: %llu blocks of %u B\n",
203 | opts->dev_info.nr_lblocks, opts->dev_info.lblock_size);
204 | printf(" Physical blocks: %llu blocks of %u B\n",
205 | opts->dev_info.nr_pblocks, opts->dev_info.pblock_size);
206 | printf(" Zones: %u zones of %.1F MB\n",
207 | opts->dev_info.nr_zones,
208 | (double)opts->dev_info.zone_size / 1048576.0);
209 |
210 | printf(" Maximum number of open zones: ");
211 | if (opts->dev_info.max_nr_open_zones == 0)
212 | printf("no limit\n");
213 | else
214 | printf("%u\n", opts->dev_info.max_nr_open_zones);
215 |
216 | printf(" Maximum number of active zones: ");
217 | if (opts->dev_info.max_nr_active_zones == 0)
218 | printf("no limit\n");
219 | else
220 | printf("%u\n", opts->dev_info.max_nr_active_zones);
221 | }
222 |
223 | static int zbd_usage(char *cmd)
224 | {
225 | printf("Usage: %s [options] \n"
226 | "Commands:\n"
227 | " report : Get zone information from a device or from\n"
228 | " a zone information dump file\n"
229 | " reset : Reset zone(s) of a device\n"
230 | " open : Explicitly open zone(s) of a device\n"
231 | " close : Close zone(s) of a device\n"
232 | " finish : Finish zone(s) of a device\n"
233 | " dump : Dump a device zone information and zone data to\n"
234 | " files (see -d and -f options).\n"
235 | " restore : Restore a device zones status and data from dump\n"
236 | " files (see -d and -f options).\n"
237 | "Common options:\n"
238 | " -v : Verbose mode (for debug)\n"
239 | " -i : Display device information\n"
240 | " -ofst : Start offset of the first zone of the\n"
241 | " target range (default: 0)\n"
242 | " -len : Size of the zone range to operate on\n"
243 | " (default: device capacity)\n"
244 | " -u : Size unit to use for ofst and len options,\n"
245 | " and for displaying zone report results.\n"
246 | " (default: 1)\n"
247 | "Report command options:\n"
248 | " -csv : Use csv output format\n"
249 | " -n : Only output the number of zones reported\n"
250 | " -c : Only output the total capacity of zones reported\n"
251 | " -ro : Specify a zone report option. Possible values are:\n"
252 | " * \"em\": empty zones\n"
253 | " * \"oi\": implicitly open zones\n"
254 | " * \"oe\": explicitly open zones\n"
255 | " * \"cl\": closed zones\n"
256 | " * \"fu\": full zones\n"
257 | " * \"ro\": read-only zones\n"
258 | " * \"ol\": offline zones\n"
259 | " * \"nw\": conventional zones\n"
260 | " * \"ns\": non-seq write resource zones\n"
261 | " * \"rw\": reset-wp recommended zones\n"
262 | "dump and restore commands options:\n"
263 | " -d : Path where to save dump files.\n"
264 | " -f : Name prefix for the dump files. If not\n"
265 | " specified, the device base name is used\n"
266 | " as a dump file name prefix\n",
267 | cmd);
268 | return 1;
269 | }
270 |
271 | int main(int argc, char **argv)
272 | {
273 | struct zbd_opts opts;
274 | bool dev_info = false;
275 | int dev_fd = 0, i, ret = 1;
276 | long long capacity;
277 | char dev_path[PATH_MAX];
278 |
279 | memset(&opts, 0, sizeof(struct zbd_opts));
280 | opts.rep_opt = ZBD_RO_ALL;
281 | opts.rep_dump = false;
282 | opts.unit = 1;
283 |
284 | /* Parse options */
285 | if (argc < 3)
286 | return zbd_usage(argv[0]);
287 |
288 | if (strcmp(argv[1], "report") == 0) {
289 | opts.cmd = ZBD_REPORT;
290 | } else if (strcmp(argv[1], "reset") == 0) {
291 | opts.cmd = ZBD_RESET;
292 | } else if (strcmp(argv[1], "open") == 0) {
293 | opts.cmd = ZBD_OPEN;
294 | } else if (strcmp(argv[1], "close") == 0) {
295 | opts.cmd = ZBD_CLOSE;
296 | } else if (strcmp(argv[1], "finish") == 0) {
297 | opts.cmd = ZBD_FINISH;
298 | } else if (strcmp(argv[1], "dump") == 0) {
299 | opts.cmd = ZBD_DUMP;
300 | } else if (strcmp(argv[1], "restore") == 0) {
301 | opts.cmd = ZBD_RESTORE;
302 | } else {
303 | fprintf(stderr, "Invalid command \"%s\"\n", argv[1]);
304 | return 1;
305 | }
306 |
307 | for (i = 2; i < (argc - 1); i++) {
308 |
309 | /*
310 | * Common options.
311 | */
312 | if (strcmp(argv[i], "-v") == 0) {
313 |
314 | zbd_set_log_level(ZBD_LOG_DEBUG);
315 |
316 | } else if (strcmp(argv[i], "-i") == 0) {
317 |
318 | dev_info = true;
319 |
320 | } else if (strcmp(argv[i], "-ofst") == 0) {
321 |
322 | if (i >= (argc - 1)) {
323 | fprintf(stderr, "Invalid command line\n");
324 | return 1;
325 | }
326 | i++;
327 |
328 | opts.ofst = strtoll(argv[i], NULL, 10);
329 |
330 | } else if (strcmp(argv[i], "-len") == 0) {
331 |
332 | if (i >= (argc - 1)) {
333 | fprintf(stderr, "Invalid command line\n");
334 | return 1;
335 | }
336 | i++;
337 |
338 | opts.len = strtoll(argv[i], NULL, 10);
339 |
340 | } else if (strcmp(argv[i], "-u") == 0) {
341 |
342 | if (i >= (argc - 1)) {
343 | fprintf(stderr, "Invalid command line\n");
344 | return 1;
345 | }
346 | i++;
347 |
348 | opts.unit = strtoll(argv[i], NULL, 10);
349 |
350 | /*
351 | * Report zones options.
352 | */
353 | } else if (strcmp(argv[i], "-csv") == 0) {
354 |
355 | opts.rep_csv = true;
356 |
357 | } else if (strcmp(argv[i], "-n") == 0) {
358 |
359 | opts.rep_num_zones = true;
360 |
361 | } else if (strcmp(argv[i], "-c") == 0) {
362 |
363 | opts.rep_capacity = true;
364 |
365 | } else if (strcmp(argv[i], "-ro") == 0) {
366 |
367 | if (i >= (argc - 1)) {
368 | fprintf(stderr, "Invalid command line\n");
369 | return 1;
370 | }
371 | i++;
372 |
373 | if (strcmp(argv[i], "em") == 0) {
374 | opts.rep_opt = ZBD_RO_EMPTY;
375 | } else if (strcmp(argv[i], "oi") == 0) {
376 | opts.rep_opt = ZBD_RO_IMP_OPEN;
377 | } else if (strcmp(argv[i], "oe") == 0) {
378 | opts.rep_opt = ZBD_RO_EXP_OPEN;
379 | } else if (strcmp(argv[i], "cl") == 0) {
380 | opts.rep_opt = ZBD_RO_CLOSED;
381 | } else if (strcmp(argv[i], "fu") == 0) {
382 | opts.rep_opt = ZBD_RO_FULL;
383 | } else if (strcmp(argv[i], "ro") == 0) {
384 | opts.rep_opt = ZBD_RO_RDONLY;
385 | } else if (strcmp(argv[i], "ol") == 0) {
386 | opts.rep_opt = ZBD_RO_OFFLINE;
387 | } else if (strcmp(argv[i], "rw") == 0) {
388 | opts.rep_opt = ZBD_RO_RWP_RECOMMENDED;
389 | } else if (strcmp(argv[i], "ns") == 0) {
390 | opts.rep_opt = ZBD_RO_NON_SEQ;
391 | } else if (strcmp(argv[i], "nw") == 0) {
392 | opts.rep_opt = ZBD_RO_NOT_WP;
393 | } else {
394 | fprintf(stderr,
395 | "Unknown report option \"%s\"\n",
396 | argv[i]);
397 | return 1;
398 | }
399 |
400 | /*
401 | * Dump and restore command options.
402 | */
403 | } else if (strcmp(argv[i], "-d") == 0) {
404 |
405 | if (i >= (argc - 1)) {
406 | fprintf(stderr, "Invalid command line\n");
407 | return 1;
408 | }
409 | i++;
410 |
411 | opts.dump_path = argv[i];
412 |
413 | } else if (strcmp(argv[i], "-f") == 0) {
414 |
415 | if (i >= (argc - 1)) {
416 | fprintf(stderr, "Invalid command line\n");
417 | return 1;
418 | }
419 | i++;
420 |
421 | opts.dump_prefix = argv[i];
422 |
423 | } else if (argv[i][0] == '-') {
424 |
425 | fprintf(stderr, "Unknown option \"%s\"\n", argv[i]);
426 | return 1;
427 |
428 | } else {
429 | break;
430 | }
431 |
432 | }
433 |
434 | if (i != (argc - 1)) {
435 | fprintf(stderr, "No device specified\n");
436 | return 1;
437 | }
438 |
439 | if (!realpath(argv[i], dev_path)) {
440 | fprintf(stderr, "Invalid device path %s\n", argv[i]);
441 | return 1;
442 | }
443 | opts.dev_path = dev_path;
444 |
445 | /*
446 | * Special case for zone report using zone info dump file.
447 | */
448 | if (opts.cmd == ZBD_REPORT) {
449 | dev_fd = zbd_open_dump(&opts);
450 | if (dev_fd < 0)
451 | return 1;
452 | }
453 |
454 | if (!dev_fd) {
455 | /* Open device */
456 | dev_fd = zbd_open(opts.dev_path,
457 | zact[opts.cmd].flags | O_LARGEFILE,
458 | &opts.dev_info);
459 | if (dev_fd < 0) {
460 | fprintf(stderr, "Open %s failed (%s)\n",
461 | opts.dev_path, strerror(errno));
462 | return 1;
463 | }
464 | }
465 |
466 | /* Check unit, offset and length */
467 | capacity = (long long)opts.dev_info.nr_sectors << 9;
468 | if (opts.unit > 1 &&
469 | (opts.unit > opts.dev_info.zone_size || opts.unit % 512)) {
470 | fprintf(stderr, "Invalid unit\n");
471 | ret = 1;
472 | goto out;
473 | }
474 |
475 | if (opts.ofst % 512 || opts.len % 512) {
476 | fprintf(stderr, "Invalid unaligned offset/length\n");
477 | ret = 1;
478 | goto out;
479 | }
480 |
481 | if (opts.ofst >= capacity) {
482 | ret = 0;
483 | goto out;
484 | }
485 |
486 | if (!opts.len)
487 | opts.len = capacity;
488 | if (opts.ofst + opts.len > capacity)
489 | opts.len = capacity - opts.ofst;
490 |
491 | if (dev_info)
492 | zbd_print_dev_info(&opts);
493 |
494 | ret = zact[opts.cmd].action(dev_fd, &opts);
495 | if (ret)
496 | ret = 1;
497 |
498 | out:
499 | if (!opts.rep_dump)
500 | zbd_close(dev_fd);
501 | else
502 | close(dev_fd);
503 |
504 | return ret;
505 | }
506 |
507 |
--------------------------------------------------------------------------------
/tools/cli/zbd.h:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 | /*
3 | * SPDX-FileCopyrightText: 2021 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | */
7 | #ifndef _ZBD_TOOL_H_
8 | #define _ZBD_TOOL_H_
9 |
10 | #define _LARGEFILE64_SOURCE
11 | #define _GNU_SOURCE
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #include "libzbd/zbd.h"
26 |
27 | enum zbd_cmd {
28 | ZBD_REPORT,
29 | ZBD_RESET,
30 | ZBD_OPEN,
31 | ZBD_CLOSE,
32 | ZBD_FINISH,
33 | ZBD_DUMP,
34 | ZBD_RESTORE,
35 | };
36 |
37 | /*
38 | * Command line options and device information.
39 | */
40 | struct zbd_opts {
41 | /* Common options */
42 | char *dev_path;
43 | char *dump_path;
44 | char *dump_prefix;
45 | struct zbd_info dev_info;
46 | enum zbd_cmd cmd;
47 | long long ofst;
48 | long long len;
49 | size_t unit;
50 |
51 | /* Report zones options */
52 | bool rep_csv;
53 | bool rep_num_zones;
54 | bool rep_capacity;
55 | bool rep_dump;
56 | enum zbd_report_option rep_opt;
57 | };
58 |
59 | /*
60 | * Zone information dump file header.
61 | */
62 | struct zbd_dump {
63 | struct zbd_info dev_info; /* 128 */
64 |
65 | unsigned int zstart; /* 132 */
66 | unsigned int zend; /* 136 */
67 |
68 | uint8_t reserved[56]; /* 192 */
69 | } __attribute__((packed));
70 |
71 | int zbd_open_dump(struct zbd_opts *opts);
72 | int zbd_dump_report_zones(int fd, struct zbd_opts *opts,
73 | struct zbd_zone *zones, unsigned int *nr_zones);
74 | int zbd_dump(int fd, struct zbd_opts *opts);
75 | int zbd_restore(int fd, struct zbd_opts *opts);
76 |
77 | #endif /* _ZBD_TOOL_H_ */
78 |
--------------------------------------------------------------------------------
/tools/cli/zbd_dump.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | */
7 | #include "./zbd.h"
8 |
9 | static ssize_t zbd_rw(int fd, bool read, void *buf, size_t count, off_t offset)
10 | {
11 | size_t remaining = count;
12 | off_t ofst = offset;
13 | ssize_t ret;
14 |
15 | while (remaining) {
16 | if (read)
17 | ret = pread(fd, buf, remaining, ofst);
18 | else
19 | ret = pwrite(fd, buf, remaining, ofst);
20 | if (ret < 0) {
21 | fprintf(stderr, "%s failed %d (%s)\n",
22 | read ? "read" : "write",
23 | errno, strerror(errno));
24 | return -1;
25 | }
26 | if (!ret)
27 | break;
28 |
29 | remaining -= ret;
30 | ofst += ret;
31 | }
32 |
33 | return count - remaining;
34 | }
35 |
36 | static inline ssize_t zbd_read(int fd, void *buf, size_t count, off_t offset)
37 | {
38 | return zbd_rw(fd, true, buf, count, offset);
39 | }
40 |
41 | static inline ssize_t zbd_write(int fd, void *buf, size_t count, off_t offset)
42 | {
43 | return zbd_rw(fd, false, buf, count, offset);
44 | }
45 |
46 | int zbd_open_dump(struct zbd_opts *opts)
47 | {
48 | struct zbd_dump dump;
49 | struct stat st;
50 | int dev_fd = 0;
51 | ssize_t ret;
52 |
53 | ret = stat(opts->dev_path, &st);
54 | if (ret) {
55 | fprintf(stderr, "stat %s failed\n", opts->dev_path);
56 | return -1;
57 | }
58 |
59 | if (!S_ISREG(st.st_mode))
60 | return 0;
61 |
62 | printf("Regular file specified: assuming dump file\n");
63 |
64 | dev_fd = open(opts->dev_path, O_RDONLY | O_LARGEFILE);
65 | if (dev_fd < 0) {
66 | fprintf(stderr, "Open %s failed (%s)\n",
67 | opts->dev_path, strerror(errno));
68 | return -1;
69 | }
70 |
71 | ret = zbd_read(dev_fd, &dump, sizeof(struct zbd_dump), 0);
72 | if (ret != sizeof(struct zbd_dump)) {
73 | fprintf(stderr, "Read dump header failed\n");
74 | close(dev_fd);
75 | return -1;
76 | }
77 |
78 | memcpy(&opts->dev_info, &dump.dev_info, sizeof(struct zbd_info));
79 | opts->rep_dump = true;
80 |
81 | return dev_fd;
82 | }
83 |
84 | static bool zbd_dump_should_report_zone(struct zbd_zone *zone,
85 | enum zbd_report_option ro)
86 | {
87 | switch (ro) {
88 | case ZBD_RO_ALL:
89 | return true;
90 | case ZBD_RO_NOT_WP:
91 | return zbd_zone_not_wp(zone);
92 | case ZBD_RO_EMPTY:
93 | return zbd_zone_empty(zone);
94 | case ZBD_RO_IMP_OPEN:
95 | return zbd_zone_imp_open(zone);
96 | case ZBD_RO_EXP_OPEN:
97 | return zbd_zone_exp_open(zone);
98 | case ZBD_RO_CLOSED:
99 | return zbd_zone_closed(zone);
100 | case ZBD_RO_FULL:
101 | return zbd_zone_full(zone);
102 | case ZBD_RO_RDONLY:
103 | return zbd_zone_rdonly(zone);
104 | case ZBD_RO_OFFLINE:
105 | return zbd_zone_offline(zone);
106 | case ZBD_RO_RWP_RECOMMENDED:
107 | return zbd_zone_rwp_recommended(zone);
108 | case ZBD_RO_NON_SEQ:
109 | return zbd_zone_non_seq_resources(zone);
110 | default:
111 | return false;
112 | }
113 | }
114 |
115 | int zbd_dump_report_zones(int fd, struct zbd_opts *opts,
116 | struct zbd_zone *zones, unsigned int *nr_zones)
117 | {
118 | unsigned int i, nz = 0, zstart, zend;
119 | struct zbd_zone zone;
120 | loff_t ofst;
121 | ssize_t ret;
122 |
123 | zstart = opts->ofst / opts->dev_info.zone_size;
124 | zend = (opts->ofst + opts->len + opts->dev_info.zone_size - 1)
125 | / opts->dev_info.zone_size;
126 |
127 | ofst = sizeof(struct zbd_dump) + zstart * sizeof(struct zbd_zone);
128 | for (i = zstart; i < zend; i++) {
129 | ret = zbd_read(fd, &zone, sizeof(struct zbd_zone), ofst);
130 | if (ret != sizeof(struct zbd_zone)) {
131 | fprintf(stderr, "Read zone information failed\n");
132 | return -1;
133 | }
134 |
135 | if (zbd_dump_should_report_zone(&zone, opts->rep_opt)) {
136 | memcpy(&zones[nz], &zone, sizeof(struct zbd_zone));
137 | nz++;
138 | }
139 |
140 | ofst += sizeof(struct zbd_zone);
141 | }
142 |
143 | *nr_zones = nz;
144 |
145 | return 0;
146 | }
147 |
148 | #define ZBD_DUMP_IO_SIZE (1024 * 1024)
149 |
150 | static ssize_t zbd_dump_one_zone(int fd, struct zbd_opts *opts,
151 | struct zbd_zone *zone, int dump_fd, void *buf)
152 | {
153 | long long ofst, end;
154 | ssize_t ret, iosize;
155 |
156 | /* Ignore offline zones */
157 | if (zbd_zone_offline(zone))
158 | return 0;
159 |
160 | /* Copy zone data */
161 | ofst = zbd_zone_start(zone);
162 | if (zbd_zone_seq(zone) && !zbd_zone_full(zone))
163 | end = zbd_zone_wp(zone);
164 | else
165 | end = ofst + zbd_zone_capacity(zone);
166 |
167 | while (ofst < end) {
168 |
169 | if (ofst + ZBD_DUMP_IO_SIZE > end)
170 | iosize = end - ofst;
171 | else
172 | iosize = ZBD_DUMP_IO_SIZE;
173 |
174 | ret = zbd_read(fd, buf, iosize, ofst);
175 | if (ret != iosize) {
176 | fprintf(stderr, "Read zone data failed\n");
177 | return -1;
178 | }
179 |
180 | ret = zbd_write(dump_fd, buf, iosize, ofst);
181 | if (ret != iosize) {
182 | fprintf(stderr, "Write zone data failed\n");
183 | return -1;
184 | }
185 |
186 | ofst += iosize;
187 | }
188 |
189 | return end - zbd_zone_start(zone);
190 | }
191 |
192 | static int zbd_dump_zone_data(int fd, struct zbd_opts *opts,
193 | struct zbd_zone *zones, struct zbd_dump *dump)
194 | {
195 | long long dumped_bytes = 0;
196 | unsigned int dumped_zones = 0;
197 | char *data_path = NULL;
198 | int data_fd = 0;
199 | unsigned int i;
200 | ssize_t ret;
201 | void *buf;
202 |
203 | /* Get an IO buffer */
204 | ret = posix_memalign(&buf, sysconf(_SC_PAGESIZE), ZBD_DUMP_IO_SIZE);
205 | if (ret) {
206 | fprintf(stderr, "No memory\n");
207 | return -1;
208 | }
209 |
210 | /* Dump zone data */
211 | ret = asprintf(&data_path, "%s/%s_zone_data.dump",
212 | opts->dump_path, opts->dump_prefix);
213 |
214 | if (ret < 0) {
215 | fprintf(stderr, "No memory\n");
216 | goto out;
217 | }
218 |
219 | printf(" Dumping zones [%u..%u] data to %s (this may take a while)...\n",
220 | dump->zstart, dump->zend - 1, data_path);
221 |
222 | data_fd = open(data_path, O_WRONLY | O_LARGEFILE | O_TRUNC | O_CREAT,
223 | 0644);
224 | if (data_fd < 0) {
225 | fprintf(stderr, "Create data file %s failed %d (%s)\n",
226 | data_path, errno, strerror(errno));
227 | ret = -1;
228 | goto out;
229 | }
230 |
231 | /*
232 | * Make sure that the zone data dump file size is always equal
233 | * to the device capacity, even for partial dumps.
234 | */
235 | ret = ftruncate(data_fd, opts->dev_info.nr_sectors << 9);
236 | if (ret) {
237 | fprintf(stderr, "Truncate data file %s failed %d (%s)\n",
238 | data_path, errno, strerror(errno));
239 | goto out;
240 | }
241 |
242 | for (i = dump->zstart; i < dump->zend; i++) {
243 | ret = zbd_dump_one_zone(fd, opts, &zones[i], data_fd, buf);
244 | if (ret < 0)
245 | goto out;
246 | if (ret) {
247 | dumped_bytes += ret;
248 | dumped_zones++;
249 | }
250 | }
251 |
252 | printf(" Dumped %lld B from %u zones\n",
253 | dumped_bytes, dumped_zones);
254 |
255 | ret = fsync(data_fd);
256 | if (ret)
257 | fprintf(stderr, "fsync data file %s failed %d (%s)\n",
258 | data_path, errno, strerror(errno));
259 |
260 | out:
261 | if (data_fd > 0)
262 | close(data_fd);
263 | free(data_path);
264 | free(buf);
265 |
266 | return ret;
267 | }
268 |
269 | static int zbd_dump_zone_info(int fd, struct zbd_opts *opts,
270 | struct zbd_zone *zones, struct zbd_dump *dump)
271 | {
272 | char *info_path = NULL;
273 | int info_fd = 0;
274 | ssize_t ret, sz;
275 |
276 | /* Dump zone information */
277 | ret = asprintf(&info_path, "%s/%s_zone_info.dump",
278 | opts->dump_path, opts->dump_prefix);
279 | if (ret < 0) {
280 | fprintf(stderr, "No memory\n");
281 | return -1;
282 | }
283 |
284 | printf(" Dumping zone information to %s\n", info_path);
285 |
286 | info_fd = open(info_path, O_WRONLY | O_LARGEFILE | O_TRUNC | O_CREAT,
287 | 0644);
288 | if (info_fd < 0) {
289 | fprintf(stderr, "Create file %s failed %d (%s)\n",
290 | info_path, errno, strerror(errno));
291 | ret = -1;
292 | goto out;
293 | }
294 |
295 | ret = zbd_write(info_fd, dump, sizeof(struct zbd_dump), 0);
296 | if (ret != (ssize_t)sizeof(struct zbd_dump)) {
297 | fprintf(stderr, "Write dump header failed\n");
298 | ret = -1;
299 | goto out;
300 | }
301 |
302 | sz = sizeof(struct zbd_zone) * opts->dev_info.nr_zones;
303 | ret = zbd_write(info_fd, zones, sz, sizeof(struct zbd_dump));
304 | if (ret != sz) {
305 | fprintf(stderr, "Write zone information failed\n");
306 | ret = -1;
307 | goto out;
308 | }
309 |
310 | ret = fsync(info_fd);
311 | if (ret)
312 | fprintf(stderr, "fsync zone information file %s failed %d (%s)\n",
313 | info_path, errno, strerror(errno));
314 |
315 | out:
316 | if (info_fd > 0)
317 | close(info_fd);
318 | free(info_path);
319 |
320 | return ret;
321 | }
322 |
323 | static void zbd_dump_prep_path(struct zbd_opts *opts)
324 | {
325 | if (!opts->dump_path) {
326 | opts->dump_path = get_current_dir_name();
327 | if (!opts->dump_path)
328 | opts->dump_path = ".";
329 | }
330 |
331 | if (!opts->dump_prefix)
332 | opts->dump_prefix = basename(opts->dev_path);
333 | }
334 |
335 | int zbd_dump(int fd, struct zbd_opts *opts)
336 | {
337 | struct zbd_zone *zones = NULL;
338 | struct zbd_dump dump;
339 | unsigned int nz;
340 | int ret;
341 |
342 | zbd_dump_prep_path(opts);
343 |
344 | /* Setup dump header */
345 | memset(&dump, 0, sizeof(struct zbd_dump));
346 | memcpy(&dump.dev_info, &opts->dev_info, sizeof(struct zbd_info));
347 | dump.zstart = opts->ofst / opts->dev_info.zone_size;
348 | dump.zend = (opts->ofst + opts->len + opts->dev_info.zone_size - 1)
349 | / opts->dev_info.zone_size;
350 |
351 | /* Get zone information */
352 | ret = zbd_list_zones(fd, 0, 0, ZBD_RO_ALL, &zones, &nz);
353 | if (ret != 0) {
354 | fprintf(stderr, "zbd_list_zones() failed %d\n", ret);
355 | return 1;
356 | }
357 | if (nz != opts->dev_info.nr_zones) {
358 | fprintf(stderr,
359 | "Invalid number of zones: expected %u, got %u\n",
360 | opts->dev_info.nr_zones, nz);
361 | ret = 1;
362 | goto out;
363 | }
364 |
365 | printf("%s: %u zones\n", opts->dev_path, opts->dev_info.nr_zones);
366 |
367 | /* Dump zone information and zone data */
368 | ret = zbd_dump_zone_data(fd, opts, zones, &dump);
369 | if (ret)
370 | goto out;
371 |
372 | ret = zbd_dump_zone_info(fd, opts, zones, &dump);
373 |
374 | out:
375 | free(zones);
376 | return ret;
377 | }
378 |
379 | struct zbd_restore {
380 | int data_fd;
381 | struct zbd_info dev_info;
382 | struct zbd_zone *dump_zones;
383 | struct zbd_zone *dev_zones;
384 | unsigned int zstart;
385 | unsigned int zend;
386 | void *buf;
387 | long long restored_bytes;
388 | unsigned int restored_zones;
389 | };
390 |
391 | static int zbd_load_zone_info(struct zbd_restore *ropts,
392 | struct zbd_opts *opts)
393 | {
394 | struct zbd_dump dump;
395 | struct zbd_zone *devz, *dumpz;
396 | unsigned int nr_open_zones = 0;
397 | unsigned int nr_active_zones = 0;
398 | char *info_path = NULL;
399 | ssize_t ret, sz;
400 | unsigned int i;
401 | int info_fd;
402 |
403 | /* Dump zone information */
404 | ret = asprintf(&info_path, "%s/%s_zone_info.dump",
405 | opts->dump_path, opts->dump_prefix);
406 | if (ret < 0) {
407 | fprintf(stderr, "No memory\n");
408 | return -1;
409 | }
410 |
411 | printf(" Getting zone information from %s\n", info_path);
412 |
413 | info_fd = open(info_path, O_RDONLY | O_LARGEFILE);
414 | if (info_fd < 0) {
415 | fprintf(stderr,
416 | "Open zone information dump file %s failed %d (%s)\n",
417 | info_path, errno, strerror(errno));
418 | ret = -1;
419 | goto out;
420 | }
421 |
422 | /* Read dump header */
423 | ret = zbd_read(info_fd, &dump, sizeof(struct zbd_dump), 0);
424 | if (ret != (ssize_t)sizeof(struct zbd_dump)) {
425 | fprintf(stderr, "Read dump header failed\n");
426 | ret = -1;
427 | goto out;
428 | }
429 | memcpy(&ropts->dev_info, &dump.dev_info, sizeof(struct zbd_info));
430 | ropts->zstart = dump.zstart;
431 | ropts->zend = dump.zend;
432 |
433 | /* Check device information against target device */
434 | ret = -1;
435 | if (ropts->dev_info.nr_sectors != opts->dev_info.nr_sectors) {
436 | fprintf(stderr, "Incompatible capacity\n");
437 | goto out;
438 | }
439 | if (ropts->dev_info.lblock_size != opts->dev_info.lblock_size) {
440 | fprintf(stderr, "Incompatible logical block size\n");
441 | goto out;
442 | }
443 | if (ropts->dev_info.pblock_size != opts->dev_info.pblock_size) {
444 | fprintf(stderr, "Incompatible physical block size\n");
445 | goto out;
446 | }
447 | if (ropts->dev_info.nr_zones != opts->dev_info.nr_zones) {
448 | fprintf(stderr, "Incompatible number of zones\n");
449 | goto out;
450 | }
451 | if (ropts->dev_info.zone_size != opts->dev_info.zone_size) {
452 | fprintf(stderr, "Incompatible zone size\n");
453 | goto out;
454 | }
455 |
456 | /* Read dumped zone information */
457 | ropts->dump_zones = calloc(ropts->dev_info.nr_zones,
458 | sizeof(struct zbd_zone));
459 | if (!ropts->dump_zones) {
460 | fprintf(stderr, "No memory\n");
461 | goto out;
462 | }
463 |
464 | sz = sizeof(struct zbd_zone) * ropts->dev_info.nr_zones;
465 | ret = zbd_read(info_fd, ropts->dump_zones, sz, sizeof(struct zbd_dump));
466 | if (ret != sz) {
467 | fprintf(stderr, "Read zone information failed %zd %zd\n",
468 | ret, sz);
469 | ret = -1;
470 | goto out;
471 | }
472 |
473 | /* Check zones against target device zones */
474 | for (i = 0; i < ropts->dev_info.nr_zones; i++) {
475 | dumpz = &ropts->dump_zones[i];
476 | devz = &ropts->dev_zones[i];
477 |
478 | ret = -1;
479 | if (zbd_zone_type(dumpz) != zbd_zone_type(devz)) {
480 | fprintf(stderr, "Incompatible zone %u type\n", i);
481 | goto out;
482 | }
483 | if (zbd_zone_start(dumpz) != zbd_zone_start(devz)) {
484 | fprintf(stderr, "Incompatible zone %u start\n", i);
485 | goto out;
486 | }
487 | if (zbd_zone_len(dumpz) != zbd_zone_len(devz)) {
488 | fprintf(stderr, "Incompatible zone %u start\n", i);
489 | goto out;
490 | }
491 | if (zbd_zone_capacity(dumpz) != zbd_zone_capacity(devz)) {
492 | fprintf(stderr, "Incompatible zone %u start\n", i);
493 | goto out;
494 | }
495 | if (zbd_zone_offline(devz) && !zbd_zone_offline(dumpz)) {
496 | fprintf(stderr, "Incompatible offline zone %u\n", i);
497 | goto out;
498 | }
499 | if (zbd_zone_rdonly(devz)) {
500 | fprintf(stderr, "Incompatible read-only zone %u\n", i);
501 | goto out;
502 | }
503 |
504 | /* Count open and active zones */
505 | if (zbd_zone_is_open(dumpz))
506 | nr_open_zones++;
507 | if (zbd_zone_is_active(dumpz))
508 | nr_active_zones++;
509 | }
510 |
511 | /*
512 | * Check that the target drive has enough open and
513 | * active zones resource.
514 | */
515 | if (opts->dev_info.max_nr_open_zones &&
516 | nr_open_zones > opts->dev_info.max_nr_open_zones) {
517 | fprintf(stderr,
518 | "Incompatible maximum number of open zones\n");
519 | goto out;
520 | }
521 | if (opts->dev_info.max_nr_active_zones &&
522 | nr_active_zones > opts->dev_info.max_nr_active_zones) {
523 | fprintf(stderr,
524 | "Incompatible maximum number of active zones\n");
525 | goto out;
526 | }
527 |
528 | ret = 0;
529 | out:
530 |
531 | free(info_path);
532 | close(info_fd);
533 |
534 | return ret;
535 | }
536 |
537 | static int zbd_open_zone_data(struct zbd_restore *ropts,
538 | struct zbd_opts *opts)
539 | {
540 | char *data_path = NULL;
541 | struct stat st;
542 | int ret;
543 |
544 | /* Dump zone information */
545 | ret = asprintf(&data_path, "%s/%s_zone_data.dump",
546 | opts->dump_path, opts->dump_prefix);
547 | if (ret < 0) {
548 | fprintf(stderr, "No memory\n");
549 | return -1;
550 | }
551 |
552 | printf(" Restoring zones [%u..%u] data from %s (this may take a while)...\n",
553 | ropts->zstart, ropts->zend - 1, data_path);
554 |
555 | ropts->data_fd = open(data_path, O_RDONLY | O_LARGEFILE);
556 | if (ropts->data_fd < 0) {
557 | fprintf(stderr,
558 | "Open zone data dump file %s failed %d (%s)\n",
559 | data_path, errno, strerror(errno));
560 | ret = -1;
561 | goto out;
562 | }
563 |
564 | /* Check zone data file size */
565 | ret = fstat(ropts->data_fd, &st);
566 | if (ret) {
567 | fprintf(stderr,
568 | "stat zone data dump file %s failed %d (%s)\n",
569 | data_path, errno, strerror(errno));
570 | goto out;
571 | }
572 |
573 | if ((unsigned long long)st.st_size != opts->dev_info.nr_sectors << 9) {
574 | fprintf(stderr, "Invalid zone data dump file size\n");
575 | ret = -1;
576 | }
577 |
578 | out:
579 | free(data_path);
580 |
581 | return ret;
582 | }
583 |
584 | static long long zbd_restore_zone_data(int fd, struct zbd_restore *ropts,
585 | struct zbd_zone *dumpz)
586 | {
587 | long long ofst, end;
588 | ssize_t ret, iosize;
589 |
590 | /* Copy zone dump data */
591 | ofst = zbd_zone_start(dumpz);
592 | if (zbd_zone_seq(dumpz) && !zbd_zone_full(dumpz))
593 | end = zbd_zone_wp(dumpz);
594 | else
595 | end = ofst + zbd_zone_capacity(dumpz);
596 |
597 | while (ofst < end) {
598 |
599 | if (ofst + ZBD_DUMP_IO_SIZE > end)
600 | iosize = end - ofst;
601 | else
602 | iosize = ZBD_DUMP_IO_SIZE;
603 |
604 | ret = zbd_read(ropts->data_fd, ropts->buf, iosize, ofst);
605 | if (ret != iosize) {
606 | fprintf(stderr, "Read zone dump data failed\n");
607 | return -1;
608 | }
609 |
610 | ret = zbd_write(fd, ropts->buf, iosize, ofst);
611 | if (ret != iosize) {
612 | fprintf(stderr, "Write zone data failed\n");
613 | return -1;
614 | }
615 |
616 | ofst += iosize;
617 | }
618 |
619 | return end - zbd_zone_start(dumpz);
620 | }
621 |
622 | static int zbd_restore_one_zone(int fd, struct zbd_restore *ropts,
623 | struct zbd_zone *dumpz, struct zbd_zone *devz)
624 | {
625 | long long restored_bytes;
626 | int ret;
627 |
628 | /* Copy zone data */
629 | restored_bytes = zbd_restore_zone_data(fd, ropts, dumpz);
630 | if (restored_bytes < 0)
631 | return restored_bytes;
632 |
633 | if (restored_bytes) {
634 | ropts->restored_bytes += restored_bytes;
635 | ropts->restored_zones++;
636 | }
637 |
638 | /* Restore zone condition */
639 | if (zbd_zone_closed(dumpz)) {
640 | ret = zbd_close_zones(fd, zbd_zone_start(devz),
641 | zbd_zone_len(devz));
642 | if (ret) {
643 | fprintf(stderr,
644 | "Close target zone at %llu failed %d (%s)\n",
645 | zbd_zone_start(devz),
646 | errno, strerror(errno));
647 | return ret;
648 | }
649 | } else if (zbd_zone_exp_open(dumpz)) {
650 | ret = zbd_open_zones(fd, zbd_zone_start(devz),
651 | zbd_zone_len(devz));
652 | if (ret) {
653 | fprintf(stderr,
654 | "Open target zone at %llu failed %d (%s)\n",
655 | zbd_zone_start(devz),
656 | errno, strerror(errno));
657 | return ret;
658 | }
659 | }
660 |
661 | return 0;
662 | }
663 |
664 | int zbd_restore(int fd, struct zbd_opts *opts)
665 | {
666 | struct zbd_restore ropts;
667 | struct zbd_zone *dumpz, *devz;
668 | unsigned int i, nz = 0;
669 | int ret;
670 |
671 | memset(&ropts, 0, sizeof(struct zbd_restore));
672 |
673 | zbd_dump_prep_path(opts);
674 |
675 | /* Get zone information from the target device */
676 | ret = zbd_list_zones(fd, 0, 0, ZBD_RO_ALL, &ropts.dev_zones, &nz);
677 | if (ret != 0) {
678 | fprintf(stderr, "zbd_list_zones() failed %d\n", ret);
679 | return ret;
680 | }
681 | if (nz != opts->dev_info.nr_zones) {
682 | fprintf(stderr,
683 | "Invalid number of zones: expected %u, got %u\n",
684 | opts->dev_info.nr_zones, nz);
685 | ret = -1;
686 | goto out;
687 | }
688 |
689 | /* Get and check zone information from the dump file */
690 | ret = zbd_load_zone_info(&ropts, opts);
691 | if (ret)
692 | goto out;
693 |
694 | /* Open and check the zone data dump file */
695 | ret = zbd_open_zone_data(&ropts, opts);
696 | if (ret)
697 | goto out;
698 |
699 | /* Get an IO buffer */
700 | ret = posix_memalign(&ropts.buf, sysconf(_SC_PAGESIZE),
701 | ZBD_DUMP_IO_SIZE);
702 | if (ret) {
703 | fprintf(stderr, "No memory\n");
704 | goto out;
705 | }
706 |
707 | /*
708 | * Restore the target device. To avoid hitting the max active or max
709 | * open zone limits of the target drive, process all zones in several
710 | * passes with each pass handling one condition.
711 | */
712 |
713 | /* Pass 1: Reset all zones in the dump range */
714 | for (i = ropts.zstart; i < ropts.zend; i++) {
715 | dumpz = &ropts.dump_zones[i];
716 | devz = &ropts.dev_zones[i];
717 |
718 | if (zbd_zone_offline(dumpz))
719 | continue;
720 | if (!zbd_zone_seq(dumpz) || zbd_zone_empty(dumpz))
721 | continue;
722 |
723 | ret = zbd_reset_zones(fd, zbd_zone_start(devz),
724 | zbd_zone_len(devz));
725 | if (ret) {
726 | fprintf(stderr, "Reset target zone %u failed %d (%s)\n",
727 | i, errno, strerror(errno));
728 | goto out;
729 | }
730 | }
731 |
732 | /* Pass 2: copy data of conventional and full sequential zones */
733 | for (i = ropts.zstart; i < ropts.zend; i++) {
734 | dumpz = &ropts.dump_zones[i];
735 | devz = &ropts.dev_zones[i];
736 |
737 | if (!zbd_zone_cnv(dumpz) && !zbd_zone_full(dumpz))
738 | continue;
739 |
740 | ret = zbd_restore_one_zone(fd, &ropts, dumpz, devz);
741 | if (ret < 0)
742 | goto out;
743 | }
744 |
745 | /* Pass 3: handle closed zones */
746 | for (i = ropts.zstart; i < ropts.zend; i++) {
747 | dumpz = &ropts.dump_zones[i];
748 | devz = &ropts.dev_zones[i];
749 |
750 | if (!zbd_zone_closed(dumpz))
751 | continue;
752 |
753 | ret = zbd_restore_one_zone(fd, &ropts, dumpz, devz);
754 | if (ret < 0)
755 | goto out;
756 | }
757 |
758 | /* Pass 4: handle explicitly open zones */
759 | for (i = ropts.zstart; i < ropts.zend; i++) {
760 | dumpz = &ropts.dump_zones[i];
761 | devz = &ropts.dev_zones[i];
762 |
763 | if (!zbd_zone_exp_open(dumpz))
764 | continue;
765 |
766 | ret = zbd_restore_one_zone(fd, &ropts, dumpz, devz);
767 | if (ret < 0)
768 | goto out;
769 | }
770 |
771 | /* Pass 5: handle implicitly open zones */
772 | for (i = ropts.zstart; i < ropts.zend; i++) {
773 | dumpz = &ropts.dump_zones[i];
774 | devz = &ropts.dev_zones[i];
775 |
776 | if (!zbd_zone_imp_open(dumpz))
777 | continue;
778 |
779 | ret = zbd_restore_one_zone(fd, &ropts, dumpz, devz);
780 | if (ret < 0)
781 | goto out;
782 | }
783 |
784 | printf(" Restored %lld B in %u zones\n",
785 | ropts.restored_bytes, ropts.restored_zones);
786 |
787 | ret = fsync(fd);
788 | if (ret)
789 | fprintf(stderr, "fsync target device failed %d (%s)\n",
790 | errno, strerror(errno));
791 |
792 | fsync(fd);
793 |
794 | out:
795 | if (ropts.data_fd > 0)
796 | close(ropts.data_fd);
797 | free(ropts.dev_zones);
798 | free(ropts.dump_zones);
799 | free(ropts.buf);
800 |
801 | return ret;
802 | }
803 |
--------------------------------------------------------------------------------
/tools/gui/Makefile.am:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: GPL-3.0-or-later
2 | #
3 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 |
5 | polkit_action_in_FILES += gui/org.gnome.gzbd.policy.in
6 | desktop_in_FILES += gui/gzbd.desktop.in
7 | pixmap_FILES += gui/gzbd.png
8 |
9 | bin_PROGRAMS += gzbd
10 | gzbd_SOURCES = \
11 | gui/gzbd.c \
12 | gui/gzbd_if.c \
13 | gui/gzbd_if_dev.c \
14 | gui/gzbd.h
15 |
16 | gzbd_CFLAGS = $(CFLAGS) $(GTK_CFLAGS)
17 | gzbd_LDADD = $(libzbd_ldadd) $(GTK_LIBS) -lpthread
18 |
19 | dist_man8_MANS += gui/gzbd.8
20 |
21 | gui/org.gnome.gzbd.policy: gui/org.gnome.gzbd.policy.in Makefile
22 | $(bin_subst) < gui/org.gnome.gzbd.policy.in > gui/org.gnome.gzbd.policy
23 |
24 | gui/gzbd.desktop: gui/gzbd.desktop.in Makefile
25 | $(bin_subst) < gui/gzbd.desktop.in > gui/gzbd.desktop
26 |
--------------------------------------------------------------------------------
/tools/gui/gzbd.8:
--------------------------------------------------------------------------------
1 | .\" SPDX-License-Identifier: GPL-3.0-or-later
2 | .\" SPDX-FileCopyrightText: 2020, Western Digital Corporation or its affiliates.
3 | .\" Written by Damien Le Moal
4 | .\"
5 | .TH GZBD 8
6 | .SH NAME
7 | gzbd \- zoned block device management graphical interface
8 |
9 | .SH SYNOPSIS
10 | .B gzbd
11 | [options]
12 | .I device
13 |
14 | .SH DESCRIPTION
15 | .B gzbd
16 | is a graphical user interface allowing to view a zoned block device zones
17 | and to execute zone commands. The zone reset, open, close and finish commands
18 | can be applied to zones selected from the list of zones displayed.
19 | The list of zones displayed can be filtered based on zone type and condition.
20 | Zones are also represented graphically using colored boxes using a similar
21 | color coding as gzbd-viewer.
22 |
23 | .PP
24 | The
25 | .I device
26 | argument must be the pathname of the target zoned block device.
27 |
28 | .SH OPTIONS
29 | .TP
30 | .BR \-? , " \-\-help"
31 | Show help options and exit.
32 | .TP
33 | .BR \-\-help\-all
34 | Show all help options and exit.
35 | .TP
36 | .BR \-\-help\-gtk
37 | Show GTK+ options and exit.
38 | .TP
39 | .BR \-v , " \-\-verbose"
40 | Use libzbd verbose mode (for debug).
41 | .TP
42 | .BR \-b , " \-\-block " \fIsize\fP
43 | Use \fIsize\fP as the unit for displaying a zone position,
44 | length and write pointer position instead of the default byte
45 | value.
46 | .TP
47 | .BR \-\-display=DISPLAY
48 | Specify the X display to use.
49 |
50 | .SH AUTHOR
51 | .nf
52 | Damien Le Moal
53 | .fi
54 |
55 | .SH SEE ALSO
56 | .BR zbd (8) ", " gzbd-viewer (8)
57 |
58 | .SH AVAILABILITY
59 | The gzbd utility is part of the libzbd library available from
60 | .UR https://\:github.com\:/westerndigitalcorporation\:/libzbd
61 | .UE .
62 |
--------------------------------------------------------------------------------
/tools/gui/gzbd.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | */
7 | #define _LARGEFILE64_SOURCE
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include "gzbd.h"
21 |
22 | /**
23 | * Device control.
24 | */
25 | dz_t dz;
26 |
27 | /**
28 | * Signal handling.
29 | */
30 | static gboolean dz_process_signal(GIOChannel *source,
31 | GIOCondition condition,
32 | gpointer user_data)
33 | {
34 | char buf[32];
35 | ssize_t size;
36 |
37 | if (condition & G_IO_IN) {
38 | size = read(g_io_channel_unix_get_fd(source), buf, sizeof(buf));
39 | if (size > 0) {
40 | /* Got signal */
41 | gtk_main_quit();
42 | return TRUE;
43 | }
44 | }
45 |
46 | return FALSE;
47 | }
48 |
49 | static void dz_sig_handler(int sig)
50 | {
51 | /* Propagate signal through the pipe */
52 | write(dz.sig_pipe[1], &sig, sizeof(int));
53 | }
54 |
55 | static void dz_set_signal_handlers(void)
56 | {
57 | GIOChannel *sig_channel;
58 | long fd_flags;
59 | int ret;
60 |
61 | ret = pipe(dz.sig_pipe);
62 | if (ret < 0) {
63 | perror("pipe");
64 | exit(1);
65 | }
66 |
67 | fd_flags = fcntl(dz.sig_pipe[1], F_GETFL);
68 | if (fd_flags < 0) {
69 | perror("Read descriptor flags");
70 | exit(1);
71 | }
72 | ret = fcntl(dz.sig_pipe[1], F_SETFL, fd_flags | O_NONBLOCK);
73 | if (ret < 0) {
74 | perror("Write descriptor flags");
75 | exit(1);
76 | }
77 |
78 | /* Install the unix signal handler */
79 | signal(SIGINT, dz_sig_handler);
80 | signal(SIGQUIT, dz_sig_handler);
81 | signal(SIGTERM, dz_sig_handler);
82 |
83 | /* Convert the reading end of the pipe into a GIOChannel */
84 | sig_channel = g_io_channel_unix_new(dz.sig_pipe[0]);
85 | g_io_channel_set_encoding(sig_channel, NULL, NULL);
86 | g_io_channel_set_flags(sig_channel,
87 | g_io_channel_get_flags(sig_channel) |
88 | G_IO_FLAG_NONBLOCK,
89 | NULL);
90 | g_io_add_watch(sig_channel,
91 | G_IO_IN | G_IO_PRI,
92 | dz_process_signal, NULL);
93 | }
94 |
95 | int main(int argc, char **argv)
96 | {
97 | gboolean init_ret;
98 | gboolean verbose = FALSE;
99 | GError *error = NULL;
100 | int i, ret = 0;
101 | GOptionEntry options[] = {
102 | {
103 | "verbose", 'v', 0,
104 | G_OPTION_ARG_NONE, &verbose,
105 | "Set libzbd verbose mode",
106 | NULL
107 | },
108 | {
109 | "block", 'b', 0,
110 | G_OPTION_ARG_INT, &dz.block_size,
111 | "Use block bytes as the unit for displaying zone "
112 | "position, length and write pointer position instead "
113 | "of the default byte value",
114 | NULL
115 | },
116 | { NULL }
117 | };
118 |
119 | /* Init */
120 | memset(&dz, 0, sizeof(dz));
121 | dz.block_size = 1;
122 | for (i = 0; i < DZ_MAX_DEV; i++)
123 | dz.dev[i].dev_fd = -1;
124 |
125 | init_ret = gtk_init_with_args(&argc, &argv,
126 | "",
127 | options, NULL, &error);
128 | if (init_ret == FALSE ||
129 | error != NULL) {
130 | printf("Failed to parse command line arguments: %s\n",
131 | error->message);
132 | g_error_free(error);
133 | return 1;
134 | }
135 |
136 | if (dz.block_size <= 0) {
137 | fprintf(stderr, "Invalid block size\n");
138 | return 1;
139 | }
140 |
141 | if (verbose)
142 | zbd_set_log_level(ZBD_LOG_DEBUG);
143 |
144 | dz_set_signal_handlers();
145 |
146 | /* Create GUI */
147 | dz_if_create();
148 |
149 | /* Check user credentials */
150 | if (getuid() != 0) {
151 | dz_if_err("Root privileges are required for running gzbd",
152 | "Since gzbd is capable of erasing vast amounts of"
153 | " data, only root may run it.");
154 | ret = 1;
155 | goto out;
156 | }
157 |
158 | /* Add devices listed on command line */
159 | for (i = 1; i < argc; i++)
160 | dz_if_add_device(argv[i]);
161 |
162 | /* Main event loop */
163 | gtk_main();
164 |
165 | out:
166 | /* Cleanup GUI */
167 | dz_if_destroy();
168 |
169 | return ret;
170 | }
171 |
172 | /*
173 | * Report zones.
174 | */
175 | static int dz_report_zones(dz_dev_t *dzd)
176 | {
177 | unsigned int i, j = 0;
178 | int ret;
179 |
180 | if (!dzd->zones || !dzd->max_nr_zones) {
181 |
182 | /* Get list of all zones */
183 | dzd->zone_ro = ZBD_RO_ALL;
184 | ret = zbd_list_zones(dzd->dev_fd,
185 | 0, 0, dzd->zone_ro,
186 | &dzd->zbdz, &dzd->nr_zones);
187 | if (ret != 0)
188 | return ret;
189 |
190 | if (!dzd->nr_zones) {
191 | /* That should not happen */
192 | return -EIO;
193 | }
194 |
195 | /* Allocate zone array */
196 | dzd->max_nr_zones = dzd->nr_zones;
197 | dzd->zones = (dz_dev_zone_t *)
198 | calloc(dzd->max_nr_zones, sizeof(dz_dev_zone_t));
199 | if (!dzd->zones)
200 | return -ENOMEM;
201 |
202 | for (i = 0; i < dzd->max_nr_zones; i++) {
203 | dzd->zones[i].no = i;
204 | dzd->zones[i].visible = 1;
205 | memcpy(&dzd->zones[i].info, &dzd->zbdz[i],
206 | sizeof(struct zbd_zone));
207 | }
208 |
209 | return 0;
210 |
211 | }
212 |
213 | /* Refresh zone list */
214 | dzd->nr_zones = dzd->max_nr_zones;
215 | ret = zbd_report_zones(dzd->dev_fd,
216 | 0, 0, dzd->zone_ro,
217 | dzd->zbdz, &dzd->nr_zones);
218 | if (ret != 0) {
219 | fprintf(stderr, "Get zone information failed %d (%s)\n",
220 | errno, strerror(errno));
221 | dzd->nr_zones = 0;
222 | }
223 |
224 | /* Apply filter */
225 | for (i = 0; i < dzd->max_nr_zones; i++) {
226 | if (j < dzd->nr_zones &&
227 | zbd_zone_start(&dzd->zones[i].info) ==
228 | zbd_zone_start(&dzd->zbdz[j])) {
229 | memcpy(&dzd->zones[i].info, &dzd->zbdz[j],
230 | sizeof(struct zbd_zone));
231 | dzd->zones[i].visible = 1;
232 | j++;
233 | } else {
234 | dzd->zones[i].visible = 0;
235 | }
236 | }
237 |
238 | return ret;
239 | }
240 |
241 | /*
242 | * Zone operation.
243 | */
244 | static int dz_zone_operation(dz_dev_t *dzd)
245 | {
246 | int zno = dzd->zone_no;
247 | off_t ofst, len;
248 | int ret;
249 |
250 | if (zno >= (int)dzd->nr_zones) {
251 | fprintf(stderr, "Invalid zone number %d / %u\n",
252 | zno,
253 | dzd->nr_zones);
254 | return -1;
255 | }
256 |
257 | if (zno < 0) {
258 | ofst = 0;
259 | len = dzd->capacity;
260 | } else {
261 | ofst = zbd_zone_start(&dzd->zones[zno].info);
262 | len = zbd_zone_len(&dzd->zones[zno].info);
263 | }
264 |
265 | ret = zbd_zones_operation(dzd->dev_fd, dzd->zone_op, ofst, len);
266 | if (ret != 0)
267 | fprintf(stderr, "zbd_zone_operation failed %d\n", ret);
268 |
269 | return ret;
270 | }
271 |
272 | /*
273 | * Command thread routine.
274 | */
275 | void *dz_cmd_run(void *data)
276 | {
277 | dz_dev_t *dzd = data;
278 | int do_report_zones = 1;
279 | int ret;
280 |
281 | switch (dzd->cmd_id) {
282 | case DZ_CMD_REPORT_ZONES:
283 | do_report_zones = 0;
284 | ret = dz_report_zones(dzd);
285 | break;
286 | case DZ_CMD_ZONE_OP:
287 | ret = dz_zone_operation(dzd);
288 | break;
289 | default:
290 | fprintf(stderr, "Invalid command ID %d\n", dzd->cmd_id);
291 | ret = -1;
292 | break;
293 | }
294 |
295 | if (do_report_zones)
296 | ret = dz_report_zones(dzd);
297 |
298 | if (dzd->cmd_dialog) {
299 | int response_id;
300 | if (ret == 0)
301 | response_id = GTK_RESPONSE_OK;
302 | else
303 | response_id = GTK_RESPONSE_REJECT;
304 | gtk_dialog_response(GTK_DIALOG(dzd->cmd_dialog), response_id);
305 | }
306 |
307 | return (void *)((unsigned long) ret);
308 | }
309 |
310 | static GtkWidget *dz_cmd_dialog(char *msg)
311 | {
312 | GtkWidget *dialog, *content_area;
313 | GtkWidget *spinner;
314 |
315 | dialog = gtk_message_dialog_new(GTK_WINDOW(dz.window),
316 | GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
317 | GTK_MESSAGE_OTHER,
318 | GTK_BUTTONS_NONE,
319 | "%s", msg);
320 | content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
321 |
322 | spinner = gtk_spinner_new();
323 | gtk_widget_show(spinner);
324 | gtk_container_add(GTK_CONTAINER(content_area), spinner);
325 | gtk_spinner_start(GTK_SPINNER(spinner));
326 |
327 | gtk_widget_show_all(dialog);
328 |
329 | return dialog;
330 | }
331 |
332 |
333 | /*
334 | * Open a device.
335 | */
336 | dz_dev_t *dz_open(char *path)
337 | {
338 | dz_dev_t *dzd = NULL;
339 | int i, ret;
340 |
341 | /* Get an unused device */
342 | for (i = 0; i < DZ_MAX_DEV; i++) {
343 | if (dz.dev[i].dev_fd < 0) {
344 | dzd = &dz.dev[i];
345 | break;
346 | }
347 | }
348 |
349 | if (!dzd) {
350 | dz_if_err("Too many open devices",
351 | "At most %d devices can be open",
352 | (int)DZ_MAX_DEV);
353 | fprintf(stderr, "Too many open devices\n");
354 | return NULL;
355 | }
356 |
357 | /* Open device file */
358 | strncpy(dzd->path, path, sizeof(dzd->path) - 1);
359 | dzd->dev_fd = zbd_open(dzd->path, O_RDWR | O_LARGEFILE, &dzd->info);
360 | if (dzd->dev_fd < 0) {
361 | ret = errno;
362 | dz_if_err("Open device failed",
363 | "Open %s failed %d (%s)",
364 | dzd->path, ret, strerror(ret));
365 | fprintf(stderr, "Open device %s failed %d (%s)\n",
366 | dzd->path, ret, strerror(ret));
367 | return NULL;
368 | }
369 |
370 | dzd->capacity = dzd->info.nr_sectors << 9;
371 |
372 | dzd->block_size = dz.block_size;
373 | if (!dzd->block_size) {
374 | dzd->block_size = 1;
375 | } else if (dzd->info.zone_size % dzd->block_size) {
376 | dz_if_err("Invalid block size",
377 | "The device zone size is not a multiple of the block size");
378 | fprintf(stderr, "Invalid block size\n");
379 | ret = 1;
380 | goto out;
381 | }
382 |
383 | /* Get zone information */
384 | ret = dz_report_zones(dzd);
385 | if (ret != 0)
386 | goto out;
387 |
388 | dz.nr_devs++;
389 |
390 | out:
391 | if (ret != 0) {
392 | dz_close(dzd);
393 | dzd = NULL;
394 | }
395 |
396 | return dzd;
397 | }
398 |
399 | /*
400 | * Close a device.
401 | */
402 | void dz_close(dz_dev_t *dzd)
403 | {
404 |
405 | if (dzd->dev_fd < 0)
406 | return;
407 |
408 | free(dzd->zbdz);
409 | dzd->zbdz = NULL;
410 |
411 | free(dzd->zones);
412 | dzd->zones = NULL;
413 |
414 | zbd_close(dzd->dev_fd);
415 | dzd->dev_fd = -1;
416 |
417 | memset(dzd, 0, sizeof(dz_dev_t));
418 | dz.nr_devs--;
419 | }
420 |
421 | /*
422 | * Execute a command.
423 | */
424 | int dz_cmd_exec(dz_dev_t *dzd, int cmd_id, char *msg)
425 | {
426 | int ret;
427 |
428 | /* Set command */
429 | dzd->cmd_id = cmd_id;
430 | if (msg)
431 | /* Create a dialog */
432 | dzd->cmd_dialog = dz_cmd_dialog(msg);
433 | else
434 | dzd->cmd_dialog = NULL;
435 |
436 | /* Create command thread */
437 | ret = pthread_create(&dzd->cmd_thread, NULL, dz_cmd_run, dzd);
438 | if (ret != 0)
439 | goto out;
440 |
441 | if (dzd->cmd_dialog) {
442 | if (gtk_dialog_run(GTK_DIALOG(dzd->cmd_dialog))
443 | == GTK_RESPONSE_OK)
444 | ret = 0;
445 | else
446 | ret = -1;
447 | } else {
448 | void *cmd_ret;
449 | pthread_join(dzd->cmd_thread, &cmd_ret);
450 | ret = (long)cmd_ret;
451 | }
452 |
453 | pthread_join(dzd->cmd_thread, NULL);
454 |
455 | out:
456 | if (dzd->cmd_dialog) {
457 | gtk_widget_destroy(dzd->cmd_dialog);
458 | dzd->cmd_dialog = NULL;
459 | }
460 |
461 | return ret;
462 | }
463 |
464 |
--------------------------------------------------------------------------------
/tools/gui/gzbd.desktop.in:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=gzbd
3 | Comment=Manage Zoned Block Devices
4 | Keywords=disk;drive;harddisk;hdd;disc;smr;SMR;ssd;zns;ZNS;
5 | Exec=pkexec --disable-internal-agent "@bindir@/gzbd"
6 | Icon=gzbd
7 | Terminal=false
8 | Type=Application
9 | Categories=GNOME;GTK;System;
10 |
--------------------------------------------------------------------------------
/tools/gui/gzbd.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-3.0-or-later */
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | */
7 | #ifndef __GZBD_H__
8 | #define __GZBD_H__
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | #include
15 |
16 | /**
17 | * Default refresh interval (milliseconds).
18 | */
19 | #define DZ_INTERVAL 1000
20 |
21 | /**
22 | * Zone information list columns.
23 | */
24 | enum {
25 | DZ_ZONE_NUM = 0,
26 | DZ_ZONE_LIST_COLUMS
27 | };
28 |
29 | /**
30 | * Device command IDs.
31 | */
32 | enum {
33 | DZ_CMD_REPORT_ZONES,
34 | DZ_CMD_ZONE_OP,
35 | };
36 |
37 | /**
38 | * Maximum number of devices that can be open.
39 | */
40 | #define DZ_MAX_DEV 32
41 |
42 | /**
43 | * Device zone information.
44 | */
45 | typedef struct dz_dev_zone {
46 |
47 | int no;
48 | int visible;
49 | struct zbd_zone info;
50 |
51 | } dz_dev_zone_t;
52 |
53 | /**
54 | * GUI Tab data.
55 | */
56 | typedef struct dz_dev {
57 |
58 | char path[128];
59 | int opening;
60 |
61 | int dev_fd;
62 | struct zbd_info info;
63 | unsigned long long capacity;
64 | int block_size;
65 | int use_hexa;
66 |
67 | int zone_ro;
68 | unsigned int zone_op;
69 | int zone_no;
70 | unsigned int max_nr_zones;
71 | unsigned int nr_zones;
72 | struct zbd_zone *zbdz;
73 | dz_dev_zone_t *zones;
74 |
75 | /**
76 | * Command execution.
77 | */
78 | int cmd_id;
79 | pthread_t cmd_thread;
80 | GtkWidget *cmd_dialog;
81 |
82 | /**
83 | * Interface stuff.
84 | */
85 | GtkWidget *page;
86 | GtkWidget *page_frame;
87 |
88 | GtkWidget *zfilter_combo;
89 | GtkWidget *zlist_frame_label;
90 | GtkWidget *zlist_treeview;
91 | GtkTreeModel *zlist_model;
92 | GtkListStore *zlist_store;
93 | unsigned int zlist_start_no;
94 | unsigned int zlist_end_no;
95 | int zlist_selection;
96 | GtkWidget *znum_entry;
97 | GtkWidget *zblock_entry;
98 |
99 | GtkWidget *zones_da;
100 |
101 | } dz_dev_t;
102 |
103 | /**
104 | * GUI data.
105 | */
106 | typedef struct dz {
107 |
108 | dz_dev_t dev[DZ_MAX_DEV];
109 | int nr_devs;
110 |
111 | int interval;
112 | int block_size;
113 | int abort;
114 |
115 | /**
116 | * Interface stuff.
117 | */
118 | GtkWidget *window;
119 | GtkWidget *vbox;
120 | GtkWidget *notebook;
121 | GtkWidget *no_dev_frame;
122 |
123 | GdkRGBA conv_color;
124 | GdkRGBA seqnw_color;
125 | GdkRGBA seqw_color;
126 | GdkRGBA nonw_color;
127 |
128 | /**
129 | * For handling signals.
130 | */
131 | int sig_pipe[2];
132 |
133 | } dz_t;
134 |
135 | /**
136 | * System time in usecs.
137 | */
138 | static inline unsigned long long dz_usec(void)
139 | {
140 | struct timeval tv;
141 |
142 | gettimeofday(&tv, NULL);
143 |
144 | return (unsigned long long) tv.tv_sec * 1000000LL +
145 | (unsigned long long) tv.tv_usec;
146 | }
147 |
148 | extern dz_t dz;
149 |
150 | dz_dev_t *dz_open(char *path);
151 | void dz_close(dz_dev_t *dzd);
152 |
153 | int dz_cmd_exec(dz_dev_t *dzd, int cmd_id, char *msg);
154 |
155 | void dz_if_err(const char *msg, const char *fmt, ...);
156 | void dz_if_create(void);
157 | void dz_if_destroy(void);
158 | void dz_if_add_device(char *dev_path);
159 | dz_dev_t *dz_if_dev_open(char *path);
160 | void dz_if_dev_close(dz_dev_t *dzd);
161 | void dz_if_dev_update(dz_dev_t *dzd, int do_report_zones);
162 |
163 | #endif /* __GZBD_H__ */
164 |
--------------------------------------------------------------------------------
/tools/gui/gzbd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/westerndigitalcorporation/libzbd/d7cf16b234f15bf0e6e9f571ffc1c1cf86af89cd/tools/gui/gzbd.png
--------------------------------------------------------------------------------
/tools/gui/gzbd_if.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | */
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "gzbd.h"
15 |
16 | static void dz_if_show_nodev(void);
17 | static void dz_if_hide_nodev(void);
18 |
19 | static gboolean dz_if_resize_cb(GtkWidget *widget, GdkEvent *event,
20 | gpointer user_data);
21 | static void dz_if_delete_cb(GtkWidget *widget, GdkEvent *event,
22 | gpointer user_data);
23 | static void dz_if_open_cb(GtkWidget *widget, gpointer user_data);
24 | static void dz_if_close_cb(GtkWidget *widget, gpointer user_data);
25 | static void dz_if_close_page_cb(GtkWidget *widget, gpointer user_data);
26 | static void dz_if_exit_cb(GtkWidget *widget, gpointer user_data);
27 |
28 | void dz_if_err(const char *msg, const char *fmt, ...)
29 | {
30 | GtkWidget *dialog;
31 |
32 | dialog = gtk_message_dialog_new(GTK_WINDOW(dz.window),
33 | GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
34 | GTK_MESSAGE_ERROR,
35 | GTK_BUTTONS_OK,
36 | "%s", msg);
37 | if (fmt) {
38 | va_list args;
39 | char secondary[256];
40 |
41 | va_start(args, fmt);
42 | vsnprintf(secondary, 255, fmt, args);
43 | va_end(args);
44 |
45 | gtk_message_dialog_format_secondary_text
46 | (GTK_MESSAGE_DIALOG(dialog),
47 | "%s", secondary);
48 | }
49 |
50 | gtk_dialog_run(GTK_DIALOG(dialog));
51 | gtk_widget_destroy(dialog);
52 | }
53 |
54 | void dz_if_create(void)
55 | {
56 | GtkWidget *toolbar;
57 | GtkWidget *sep;
58 | GtkToolItem *ti;
59 |
60 | /* Get colors */
61 | gdk_rgba_parse(&dz.conv_color, "Magenta");
62 | gdk_rgba_parse(&dz.seqnw_color, "Green");
63 | gdk_rgba_parse(&dz.seqw_color, "Red");
64 | gdk_rgba_parse(&dz.nonw_color, "RosyBrown");
65 |
66 | /* Window */
67 | dz.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
68 | gtk_window_set_title(GTK_WINDOW(dz.window),
69 | "Zoned Block Device Zone State");
70 | gtk_container_set_border_width(GTK_CONTAINER(dz.window), 10);
71 |
72 | g_signal_connect((gpointer) dz.window, "delete-event",
73 | G_CALLBACK(dz_if_delete_cb),
74 | NULL);
75 |
76 | /* Top vbox */
77 | dz.vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
78 | gtk_widget_show(dz.vbox);
79 | gtk_container_add(GTK_CONTAINER(dz.window), dz.vbox);
80 |
81 | /* Toolbar */
82 | toolbar = gtk_toolbar_new();
83 | gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
84 | gtk_widget_show(toolbar);
85 | gtk_box_pack_start(GTK_BOX(dz.vbox), toolbar, FALSE, FALSE, 0);
86 |
87 | /* Toolbar open button */
88 | ti = gtk_tool_button_new(gtk_image_new_from_icon_name("document-open",
89 | GTK_ICON_SIZE_LARGE_TOOLBAR), "Open");
90 | gtk_tool_item_set_tooltip_text(ti, "Open a device");
91 | gtk_tool_item_set_is_important(ti, TRUE);
92 | gtk_toolbar_insert(GTK_TOOLBAR(toolbar), ti, -1);
93 | g_signal_connect(G_OBJECT(ti), "clicked",
94 | G_CALLBACK(dz_if_open_cb), NULL);
95 |
96 | /* Toolbar close button */
97 | ti = gtk_tool_button_new(gtk_image_new_from_icon_name("window-close",
98 | GTK_ICON_SIZE_LARGE_TOOLBAR), "Close");
99 | gtk_tool_item_set_tooltip_text(ti, "Close current device");
100 | gtk_tool_item_set_is_important(ti, TRUE);
101 | gtk_toolbar_insert(GTK_TOOLBAR(toolbar), ti, -1);
102 | g_signal_connect(G_OBJECT(ti), "clicked",
103 | G_CALLBACK(dz_if_close_cb), NULL);
104 |
105 | /* Separator */
106 | ti = gtk_separator_tool_item_new();
107 | gtk_toolbar_insert(GTK_TOOLBAR(toolbar), ti, -1);
108 |
109 | /* Toolbar exit button */
110 | ti = gtk_tool_button_new(gtk_image_new_from_icon_name("application-exit",
111 | GTK_ICON_SIZE_LARGE_TOOLBAR), "Quit");
112 | gtk_tool_item_set_tooltip_text(ti, "Quit");
113 | gtk_toolbar_insert(GTK_TOOLBAR(toolbar), ti, -1);
114 | g_signal_connect(G_OBJECT(ti), "clicked",
115 | G_CALLBACK(dz_if_exit_cb), NULL);
116 |
117 | /* Separator */
118 | sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
119 | gtk_widget_show(sep);
120 | gtk_box_pack_start(GTK_BOX(dz.vbox), sep, FALSE, FALSE, 0);
121 |
122 | /* Initially, no device open: show "no device" frame */
123 | dz_if_show_nodev();
124 |
125 | /* Finish setup */
126 | g_signal_connect((gpointer) dz.window, "configure-event",
127 | G_CALLBACK(dz_if_resize_cb), NULL);
128 |
129 | gtk_window_set_default_size(GTK_WINDOW(dz.window), 1024, 768);
130 | gtk_widget_show_all(dz.window);
131 | }
132 |
133 | void dz_if_destroy(void)
134 | {
135 | if ( dz.window ) {
136 | gtk_widget_destroy(dz.window);
137 | dz.window = NULL;
138 | }
139 | }
140 |
141 | void dz_if_add_device(char *dev_path)
142 | {
143 | GtkWidget *label;
144 | GtkWidget *hbox;
145 | GtkWidget *button;
146 | dz_dev_t *dzd;
147 | int page_no;
148 | char str[256];
149 |
150 | /* Open device */
151 | dzd = dz_if_dev_open(dev_path);
152 | if (!dzd)
153 | return;
154 |
155 | dz_if_hide_nodev();
156 |
157 | /* Add page */
158 | hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
159 |
160 | snprintf(str, sizeof(str) - 1, "%s", dzd->path);
161 | label = gtk_label_new(NULL);
162 | gtk_label_set_text(GTK_LABEL(label), str);
163 | gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
164 | gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
165 |
166 | button = gtk_button_new_from_icon_name("window-close", GTK_ICON_SIZE_BUTTON);
167 | gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
168 | g_signal_connect((gpointer) button, "clicked",
169 | G_CALLBACK(dz_if_close_page_cb), dzd);
170 |
171 | gtk_widget_show_all(hbox);
172 |
173 | page_no = gtk_notebook_append_page(GTK_NOTEBOOK(dz.notebook),
174 | dzd->page_frame, hbox);
175 | dzd->page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(dz.notebook), page_no);
176 | gtk_notebook_set_current_page(GTK_NOTEBOOK(dz.notebook), page_no);
177 | }
178 |
179 | static void dz_if_show_nodev(void)
180 | {
181 |
182 | if (dz.notebook) {
183 | /* Remove notebook */
184 | gtk_widget_destroy(dz.notebook);
185 | dz.notebook = NULL;
186 | }
187 |
188 | if (!dz.no_dev_frame) {
189 |
190 | GtkWidget *frame;
191 | GtkWidget *label;
192 |
193 | frame = gtk_frame_new(NULL);
194 | gtk_widget_show(frame);
195 | gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
196 | gtk_container_set_border_width(GTK_CONTAINER(frame), 10);
197 | gtk_box_pack_start(GTK_BOX(dz.vbox), frame, TRUE, TRUE, 0);
198 |
199 | label = gtk_label_new(NULL);
200 | gtk_widget_show(label);
201 | gtk_label_set_text(GTK_LABEL(label), "No device open");
202 | gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
203 | gtk_container_add(GTK_CONTAINER(frame), label);
204 |
205 | dz.no_dev_frame = frame;
206 |
207 | }
208 | }
209 |
210 | static void dz_if_hide_nodev(void)
211 | {
212 |
213 | if (dz.no_dev_frame) {
214 | /* Remove "no device" frame */
215 | gtk_widget_destroy(dz.no_dev_frame);
216 | dz.no_dev_frame = NULL;
217 | }
218 |
219 | if (!dz.notebook) {
220 | /* Create the notebook */
221 | dz.notebook = gtk_notebook_new();
222 | gtk_notebook_set_tab_pos(GTK_NOTEBOOK(dz.notebook), GTK_POS_TOP);
223 | gtk_notebook_set_show_border(GTK_NOTEBOOK(dz.notebook), TRUE);
224 | gtk_notebook_set_show_tabs(GTK_NOTEBOOK(dz.notebook), TRUE);
225 | gtk_widget_show(dz.notebook);
226 | gtk_box_pack_start(GTK_BOX(dz.vbox), dz.notebook, TRUE, TRUE, 0);
227 | }
228 | }
229 |
230 | static void dz_if_remove_device(dz_dev_t *dzd)
231 | {
232 | int page_no = gtk_notebook_page_num(GTK_NOTEBOOK(dz.notebook), dzd->page_frame);
233 |
234 | /* Close the device */
235 | dz_if_dev_close(dzd);
236 |
237 | /* Remove the page */
238 | gtk_notebook_remove_page(GTK_NOTEBOOK(dz.notebook), page_no);
239 | dzd->page = NULL;
240 |
241 | if (dz.nr_devs == 0)
242 | /* Show "no device" */
243 | dz_if_show_nodev();
244 | }
245 |
246 | static dz_dev_t *dz_if_get_device(void)
247 | {
248 | dz_dev_t *dzd = NULL;
249 | GtkWidget *page = NULL;
250 | int i;
251 |
252 | if (!dz.notebook)
253 | return NULL;
254 |
255 | page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(dz.notebook),
256 | gtk_notebook_get_current_page(GTK_NOTEBOOK(dz.notebook)));
257 | for (i = 0; i < DZ_MAX_DEV; i++) {
258 | dzd = &dz.dev[i];
259 | if (dzd->dev_fd >= 0 && dzd->page == page)
260 | return dzd;
261 | }
262 |
263 | return NULL;
264 | }
265 |
266 | static void dz_if_open_cb(GtkWidget *widget, gpointer user_data)
267 | {
268 | GtkFileChooser *chooser;
269 | GtkFileFilter *filter;
270 | GtkWidget *dialog;
271 | char *dev_path = NULL;
272 | gint res;
273 |
274 | /* File chooser */
275 | dialog = gtk_file_chooser_dialog_new("Open Zoned Block Device",
276 | GTK_WINDOW(dz.window),
277 | GTK_FILE_CHOOSER_ACTION_OPEN,
278 | "_Cancel", GTK_RESPONSE_CANCEL,
279 | "_Open", GTK_RESPONSE_ACCEPT,
280 | NULL);
281 |
282 | chooser = GTK_FILE_CHOOSER(dialog);
283 | gtk_file_chooser_set_current_folder(chooser, "/dev/");
284 |
285 | filter = gtk_file_filter_new();
286 | gtk_file_filter_set_name(filter, "Block Device Files");
287 | gtk_file_filter_add_mime_type(filter, "inode/blockdevice");
288 | gtk_file_chooser_add_filter(chooser, filter);
289 |
290 | res = gtk_dialog_run(GTK_DIALOG(dialog));
291 | if (res == GTK_RESPONSE_ACCEPT)
292 | dev_path = gtk_file_chooser_get_filename(chooser);
293 |
294 | gtk_widget_destroy(dialog);
295 |
296 | if (dev_path) {
297 | dz_if_add_device(dev_path);
298 | g_free(dev_path);
299 | }
300 | }
301 |
302 | static void dz_if_close_cb(GtkWidget *widget, gpointer user_data)
303 | {
304 | dz_dev_t *dzd = dz_if_get_device();
305 |
306 | if (dzd)
307 | dz_if_remove_device(dzd);
308 | }
309 |
310 | static void dz_if_close_page_cb(GtkWidget *widget, gpointer user_data)
311 | {
312 | dz_dev_t *dzd = (dz_dev_t *) user_data;
313 |
314 | if (dzd)
315 | dz_if_remove_device(dzd);
316 | }
317 |
318 | static void dz_if_exit_cb(GtkWidget *widget, gpointer user_data)
319 | {
320 | dz_dev_t *dzd;
321 | int i;
322 |
323 | if (dz.notebook) {
324 | for (i = 0; i < DZ_MAX_DEV; i++) {
325 | dzd = &dz.dev[i];
326 | if (dzd->dev_fd >= 0)
327 | dz_if_remove_device(dzd);
328 | }
329 | }
330 |
331 | gtk_main_quit();
332 | }
333 |
334 | static gboolean dz_if_resize_cb(GtkWidget *widget, GdkEvent *event,
335 | gpointer user_data)
336 | {
337 | dz_dev_t *dzd = dz_if_get_device();
338 |
339 | if (dzd)
340 | dz_if_dev_update(dzd, 0);
341 |
342 | return FALSE;
343 | }
344 |
345 | static void dz_if_delete_cb(GtkWidget *widget, GdkEvent *event,
346 | gpointer user_data)
347 | {
348 | dz_dev_t *dzd;
349 | int i;
350 |
351 | dz.window = NULL;
352 |
353 | if (dz.notebook) {
354 | for (i = 0; i < DZ_MAX_DEV; i++) {
355 | dzd = &dz.dev[i];
356 | if (dzd->dev_fd >= 0)
357 | dz_if_remove_device(dzd);
358 | }
359 | }
360 |
361 | gtk_main_quit();
362 | }
363 |
--------------------------------------------------------------------------------
/tools/gui/org.gnome.gzbd.policy.in:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | The libzbd Project
7 | https://github.com/westerndigitalcorporation/libzbd
8 | gzbd
9 |
10 |
11 | Run gzbd as root
12 | Authentication is required to run gzbd as root
13 |
14 | auth_admin
15 | auth_admin
16 | auth_admin
17 |
18 | @bindir@/gzbd
19 | true
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tools/viewer/Makefile.am:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: GPL-3.0-or-later
2 | #
3 | # SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 |
5 | AM_LDFLAGS = -lm
6 |
7 | polkit_action_in_FILES += viewer/org.gnome.gzbd-viewer.policy.in
8 | desktop_in_FILES += viewer/gzbd-viewer.desktop.in
9 | pixmap_FILES += viewer/gzbd-viewer.png
10 |
11 | bin_PROGRAMS += gzbd-viewer
12 | gzbd_viewer_SOURCES = \
13 | viewer/gzbd_viewer.c \
14 | viewer/gzbd_viewer_if.c \
15 | viewer/gzbd_viewer.h
16 |
17 | gzbd_viewer_CFLAGS = $(CFLAGS) $(GTK_CFLAGS)
18 | gzbd_viewer_LDADD = $(libzbd_ldadd) $(GTK_LIBS)
19 |
20 | dist_man8_MANS += viewer/gzbd-viewer.8
21 |
22 | viewer/org.gnome.gzbd-viewer.policy: viewer/org.gnome.gzbd-viewer.policy.in Makefile
23 | $(bin_subst) < viewer/org.gnome.gzbd-viewer.policy.in > viewer/org.gnome.gzbd-viewer.policy
24 |
25 | viewer/gzbd-viewer.desktop: viewer/gzbd-viewer.desktop.in Makefile
26 | $(bin_subst) < viewer/gzbd-viewer.desktop.in > viewer/gzbd-viewer.desktop
27 |
--------------------------------------------------------------------------------
/tools/viewer/gzbd-viewer.8:
--------------------------------------------------------------------------------
1 | .\" SPDX-License-Identifier: GPL-3.0-or-later
2 | .\" SPDX-FileCopyrightText: 2020, Western Digital Corporation or its affiliates.
3 | .\" Written by Damien Le Moal
4 | .\"
5 | .TH GZBD-VIEWER 8
6 | .SH NAME
7 | gzbd-viewer \- zoned block devices zone state graphical interface
8 |
9 | .SH SYNOPSIS
10 | .B gzbd-viewer
11 | [options]
12 | .I device
13 |
14 | .SH DESCRIPTION
15 | .B gzbd-viewer
16 | is used to dynamically display the state of zones of a zoned block device.
17 | Each zone is displayed as a box representing the sector range of the zone.
18 | A zone box is filled with different colors based on the zone type and on
19 | the use of sectors within the zone. The condition of zones is indicated
20 | by highlighting a zone box border with different colors.
21 |
22 | .B Zone type and usage color codes
23 | .TS
24 | tab(:);
25 | l l.
26 | Magenta:Conventional zones
27 | Red:Written sectors of a sequential zone
28 | Green:Unwritten and usable sectors of a sequential zone
29 | Rosy brown:Unusable sectors of a sequential zone
30 | .TE
31 |
32 | .B Zone condition color codes
33 | .TS
34 | tab(:);
35 | l l.
36 | Light blue:Explicitly opened zones
37 | Dark blue:Implicitly opened zones
38 | Dark orange:Closed zones
39 | Grey:Offline zones
40 | .TE
41 |
42 | .PP
43 | The
44 | .I device
45 | argument must be the pathname of the target zoned block device.
46 |
47 | .PP
48 | By default, \fBgzbd-viewer\fP will display at most 100 zones using a
49 | 10 x 10 grid. This default display size can be changed using options
50 | as explained below.
51 |
52 | .SH OPTIONS
53 | .TP
54 | .BR \-? , " \-\-help"
55 | Show help options and exit.
56 | .TP
57 | .BR \-\-help\-all
58 | Show all help options and exit.
59 | .TP
60 | .BR \-\-help\-gtk
61 | Show GTK+ options and exit.
62 | .TP
63 | .BR \-v , " \-\-verbose"
64 | Use libzbd verbose mode (for debug).
65 | .TP
66 | .BR \-i , " \-\-interval " \fImsec\fP
67 | Set refresh interval to \fImsec\fP milliseconds (default: 500).
68 | .TP
69 | .BR \-w , " \-\-width " \fIcolumns\fP
70 | Display zones in a matrix of \fIcolumns\fP columns.
71 | .TP
72 | .BR \-h , " \-\-height " \fIrows\fP
73 | Display zones in a matrix of \fIrows\fP rows.
74 | .TP
75 | .BR \-b , " \-\-block " \fIsize\fP
76 | Use \fIsize\fP as the unit for displaying a zone position,
77 | length and write pointer position instead of the default byte
78 | value.
79 | .TP
80 | .BR \-\-display=DISPLAY
81 | Specify the X display to use.
82 |
83 | .SH AUTHOR
84 | .nf
85 | Damien Le Moal
86 | .fi
87 |
88 | .SH SEE ALSO
89 | .BR zbd (8) ", " gzbd (8)
90 |
91 | .SH AVAILABILITY
92 | The gzbd-viewer utility is part of the libzbd library available from
93 | .UR https://\:github.com\:/westerndigitalcorporation\:/libzbd
94 | .UE .
95 |
--------------------------------------------------------------------------------
/tools/viewer/gzbd-viewer.desktop.in:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=gzbd-viewer
3 | Comment=Display the zone state of a Zoned Block Device
4 | Keywords=disk;drive;harddisk;hdd;disc;smr;SMR;ssd;zns;ZNS;
5 | Exec=pkexec --disable-internal-agent "@bindir@/gzbd-viewer"
6 | Icon=gzbd-viewer
7 | Terminal=false
8 | Type=Application
9 | Categories=GNOME;GTK;System;
10 |
--------------------------------------------------------------------------------
/tools/viewer/gzbd-viewer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/westerndigitalcorporation/libzbd/d7cf16b234f15bf0e6e9f571ffc1c1cf86af89cd/tools/viewer/gzbd-viewer.png
--------------------------------------------------------------------------------
/tools/viewer/gzbd_viewer.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | */
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | #include "gzbd_viewer.h"
17 |
18 | /*
19 | * Device control.
20 | */
21 | struct gzv gzv;
22 |
23 | /*
24 | * Signal handling.
25 | */
26 | static gboolean gzv_process_signal(GIOChannel *source,
27 | GIOCondition condition,
28 | gpointer user_data)
29 | {
30 | char buf[32];
31 | ssize_t size;
32 |
33 | if (condition & G_IO_IN) {
34 | size = read(g_io_channel_unix_get_fd(source), buf, sizeof(buf));
35 | if (size > 0) {
36 | /* Got signal */
37 | gtk_main_quit();
38 | return TRUE;
39 | }
40 | }
41 |
42 | return FALSE;
43 | }
44 |
45 | static void gzv_sig_handler(int sig)
46 | {
47 | /* Propagate signal through the pipe */
48 | if (write(gzv.sig_pipe[1], &sig, sizeof(int)) < 0)
49 | printf("Signal %d processing failed\n", sig);
50 | }
51 |
52 | static void gzv_set_signal_handlers(void)
53 | {
54 | GIOChannel *sig_channel;
55 | long fd_flags;
56 | int ret;
57 |
58 | ret = pipe(gzv.sig_pipe);
59 | if (ret < 0) {
60 | perror("pipe");
61 | exit(1);
62 | }
63 |
64 | fd_flags = fcntl(gzv.sig_pipe[1], F_GETFL);
65 | if (fd_flags < 0) {
66 | perror("Read descriptor flags");
67 | exit(1);
68 | }
69 | ret = fcntl(gzv.sig_pipe[1], F_SETFL, fd_flags | O_NONBLOCK);
70 | if (ret < 0) {
71 | perror("Write descriptor flags");
72 | exit(1);
73 | }
74 |
75 | /* Install the unix signal handler */
76 | signal(SIGINT, gzv_sig_handler);
77 | signal(SIGQUIT, gzv_sig_handler);
78 | signal(SIGTERM, gzv_sig_handler);
79 |
80 | /* Convert the reading end of the pipe into a GIOChannel */
81 | sig_channel = g_io_channel_unix_new(gzv.sig_pipe[0]);
82 | g_io_channel_set_encoding(sig_channel, NULL, NULL);
83 | g_io_channel_set_flags(sig_channel,
84 | g_io_channel_get_flags(sig_channel) |
85 | G_IO_FLAG_NONBLOCK,
86 | NULL);
87 | g_io_add_watch(sig_channel,
88 | G_IO_IN | G_IO_PRI,
89 | gzv_process_signal, NULL);
90 | }
91 |
92 | /*
93 | * Fix offset/length values to the specified block size.
94 | */
95 | static void gzv_fix_zone_values(struct zbd_zone *zbdz, int nrz)
96 | {
97 | int i;
98 |
99 | if (gzv.block_size == 1)
100 | return;
101 |
102 | for (i = 0; i < nrz; i++) {
103 | zbdz->start /= gzv.block_size;
104 | zbdz->len /= gzv.block_size;
105 | zbdz->capacity /= gzv.block_size;
106 | if (!zbd_zone_cnv(zbdz))
107 | zbdz->wp /= gzv.block_size;
108 | zbdz++;
109 | }
110 | }
111 |
112 | /*
113 | * Close a device.
114 | */
115 | static void gzv_close(void)
116 | {
117 |
118 | if (gzv.dev_fd < 0)
119 | return;
120 |
121 | zbd_close(gzv.dev_fd);
122 | gzv.dev_fd = -1;
123 |
124 | free(gzv.zones);
125 | free(gzv.grid_zones);
126 | }
127 |
128 | static char *gzv_choose_dev(void)
129 | {
130 | GtkFileChooser *chooser;
131 | GtkFileFilter *filter;
132 | GtkWidget *dialog;
133 | char *path = NULL;
134 | gint res;
135 |
136 | /* File chooser */
137 | dialog = gtk_file_chooser_dialog_new("Open Zoned Block Device",
138 | GTK_WINDOW(gzv.window),
139 | GTK_FILE_CHOOSER_ACTION_OPEN,
140 | "_Cancel", GTK_RESPONSE_CANCEL,
141 | "_Open", GTK_RESPONSE_ACCEPT,
142 | NULL);
143 |
144 | chooser = GTK_FILE_CHOOSER(dialog);
145 | gtk_file_chooser_set_current_folder(chooser, "/dev");
146 |
147 | filter = gtk_file_filter_new();
148 | gtk_file_filter_set_name(filter, "Block Device Files");
149 | gtk_file_filter_add_mime_type(filter, "inode/blockdevice");
150 | gtk_file_chooser_add_filter(chooser, filter);
151 |
152 | res = gtk_dialog_run(GTK_DIALOG(dialog));
153 | if (res == GTK_RESPONSE_ACCEPT)
154 | path = gtk_file_chooser_get_filename(chooser);
155 |
156 | gtk_widget_destroy(dialog);
157 |
158 | return path;
159 | }
160 |
161 | /*
162 | * Open a device.
163 | */
164 | static int gzv_open(void)
165 | {
166 | unsigned int i;
167 | int ret;
168 |
169 | /* Open device file */
170 | gzv.dev_fd = zbd_open(gzv.path, O_RDONLY, &gzv.info);
171 | if (gzv.dev_fd < 0)
172 | return -1;
173 |
174 | if (gzv.block_size > 1 &&
175 | gzv.info.zone_size % gzv.block_size) {
176 | fprintf(stderr, "Invalid block size\n");
177 | return 1;
178 | }
179 |
180 | /* Get list of all zones */
181 | ret = zbd_list_zones(gzv.dev_fd, 0, 0, ZBD_RO_ALL,
182 | &gzv.zones, &gzv.nr_zones);
183 | if (ret != 0)
184 | goto out;
185 | if (!gzv.nr_zones) {
186 | fprintf(stderr, "No zones reported\n");
187 | ret = 1;
188 | goto out;
189 | }
190 |
191 | gzv_fix_zone_values(gzv.zones, gzv.nr_zones);
192 |
193 | for (i = 0; i < gzv.nr_zones; i++) {
194 | if (zbd_zone_cnv(&gzv.zones[i]))
195 | gzv.nr_conv_zones++;
196 | }
197 |
198 | /* Set defaults */
199 | if (!gzv.nr_col && !gzv.nr_row && gzv.nr_zones < 100) {
200 | gzv.nr_col = sqrt(gzv.nr_zones);
201 | gzv.nr_row = (gzv.nr_zones + gzv.nr_col - 1) / gzv.nr_col;
202 | } else {
203 | if (!gzv.nr_col)
204 | gzv.nr_col = 10;
205 | if (!gzv.nr_row)
206 | gzv.nr_row = 10;
207 | }
208 | gzv.max_row = (gzv.nr_zones + gzv.nr_col - 1) / gzv.nr_col;
209 |
210 | /* Allocate zone array */
211 | gzv.nr_grid_zones = gzv.nr_col * gzv.nr_row;
212 | gzv.grid_zones = calloc(gzv.nr_grid_zones, sizeof(struct gzv_zone));
213 | if (!gzv.grid_zones) {
214 | ret = -ENOMEM;
215 | goto out;
216 | }
217 |
218 | for (i = 0; i < gzv.nr_grid_zones && i < gzv.nr_zones; i++) {
219 | gzv.grid_zones[i].zno = i;
220 | gzv.grid_zones[i].zbdz = &gzv.zones[i];
221 | }
222 |
223 | out:
224 | if (ret)
225 | gzv_close();
226 |
227 | return ret;
228 | }
229 |
230 | int main(int argc, char **argv)
231 | {
232 | gboolean init_ret;
233 | gboolean verbose = FALSE;
234 | GError *error = NULL;
235 | int ret = 0;
236 | GOptionEntry options[] = {
237 | {
238 | "verbose", 'v', 0,
239 | G_OPTION_ARG_NONE, &verbose,
240 | "Set libzbd verbose mode",
241 | NULL
242 | },
243 | {
244 | "interval", 'i', 0,
245 | G_OPTION_ARG_INT, &gzv.refresh_interval,
246 | "Refresh interval (milliseconds)",
247 | NULL
248 | },
249 | {
250 | "width", 'w', 0,
251 | G_OPTION_ARG_INT, &gzv.nr_col,
252 | "Number of zones per row (default: 10)",
253 | NULL
254 | },
255 | {
256 | "height", 'h', 0,
257 | G_OPTION_ARG_INT, &gzv.nr_row,
258 | "Number of rows (default: 10)",
259 | NULL
260 | },
261 | {
262 | "block", 'b', 0,
263 | G_OPTION_ARG_INT, &gzv.block_size,
264 | "Use block bytes as the unit for displaying zone "
265 | "position, length and write pointer position instead "
266 | "of the default byte value",
267 | NULL
268 | },
269 | { NULL }
270 | };
271 |
272 | /* Init */
273 | memset(&gzv, 0, sizeof(gzv));
274 | gzv.dev_fd = -1;
275 | gzv.block_size = 1;
276 | init_ret = gtk_init_with_args(&argc, &argv,
277 | "",
278 | options, NULL, &error);
279 | if (init_ret == FALSE ||
280 | error != NULL) {
281 | printf("Failed to parse command line arguments: %s\n",
282 | error->message);
283 | g_error_free(error);
284 | return 1;
285 | }
286 |
287 | if (gzv.refresh_interval < 0) {
288 | fprintf(stderr, "Invalid update interval\n");
289 | return 1;
290 | }
291 |
292 | if (gzv.block_size <= 0) {
293 | fprintf(stderr, "Invalid block size\n");
294 | return 1;
295 | }
296 |
297 | if (verbose)
298 | zbd_set_log_level(ZBD_LOG_DEBUG);
299 |
300 | /* Set default values and open the device */
301 | if (!gzv.refresh_interval)
302 | gzv.refresh_interval = 500;
303 |
304 | /* Create the main window */
305 | gzv_if_create_window();
306 |
307 | /* Check user credentials */
308 | if (getuid() != 0) {
309 | gzv_if_err("Root privileges are required for running gzbd-viewer",
310 | "Opening a block device file can only be done with "
311 | "elevated priviledges");
312 | ret = 1;
313 | goto out;
314 | }
315 |
316 | if (argc < 2) {
317 | /* No device specified: use the file chooser */
318 | gzv.path = gzv_choose_dev();
319 | if (!gzv.path) {
320 | gzv_if_err("No device specified",
321 | "Specifying a zoned block device is mandatory");
322 | fprintf(stderr, "No device specified\n");
323 | ret = 1;
324 | goto out;
325 | }
326 | } else {
327 | gzv.path = argv[1];
328 | }
329 |
330 | if (gzv_open()) {
331 | ret = errno;
332 | gzv_if_err("Open device failed",
333 | "Opening %s generated error %d (%s)",
334 | gzv.path, ret, strerror(ret));
335 | fprintf(stderr, "Open device %s failed %d (%s)\n",
336 | gzv.path, ret, strerror(ret));
337 | ret = 1;
338 | goto out;
339 | }
340 |
341 | gzv_set_signal_handlers();
342 |
343 | /* Create GUI */
344 | gzv_if_create();
345 |
346 | /* Main event loop */
347 | gtk_main();
348 |
349 | out:
350 | /* Cleanup GUI */
351 | gzv_if_destroy();
352 |
353 | return ret;
354 | }
355 |
356 | /*
357 | * Report zones.
358 | */
359 | int gzv_report_zones(unsigned int zno_start, unsigned int nr_zones)
360 | {
361 | unsigned int nrz;
362 | int ret;
363 |
364 | if (zno_start >= gzv.nr_zones)
365 | return 0;
366 |
367 | nrz = nr_zones;
368 | if (zno_start + nrz > gzv.nr_zones)
369 | nrz = gzv.nr_zones - zno_start;
370 |
371 | /* Get zone information */
372 | ret = zbd_report_zones(gzv.dev_fd,
373 | zbd_zone_start(&gzv.zones[zno_start]),
374 | nrz * gzv.info.zone_size,
375 | ZBD_RO_ALL, &gzv.zones[zno_start], &nrz);
376 | if (ret) {
377 | fprintf(stderr, "Get zone information failed %d (%s)\n",
378 | errno, strerror(errno));
379 | return ret;
380 | }
381 |
382 | gzv_fix_zone_values(&gzv.zones[zno_start], nrz);
383 |
384 | return 0;
385 | }
386 |
387 |
--------------------------------------------------------------------------------
/tools/viewer/gzbd_viewer.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-3.0-or-later */
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | */
7 | #ifndef __GZVIEWER_H__
8 | #define __GZVIEWER_H__
9 |
10 | #include
11 | #include
12 |
13 | #include
14 |
15 | /*
16 | * Device zone information.
17 | */
18 | struct gzv_zone {
19 | unsigned int zno;
20 | struct zbd_zone *zbdz;
21 | GtkWidget *da;
22 | };
23 |
24 | /*
25 | * GUI data.
26 | */
27 | struct gzv {
28 |
29 | /*
30 | * Parameters.
31 | */
32 | int refresh_interval;
33 | int block_size;
34 | int abort;
35 |
36 | /*
37 | * For handling timer and signals.
38 | */
39 | GSource *refresh_timer;
40 | unsigned long long last_refresh;
41 | int sig_pipe[2];
42 |
43 | /*
44 | * Interface stuff.
45 | */
46 | GdkRGBA color_conv;
47 | GdkRGBA color_seq;
48 | GdkRGBA color_seqw;
49 | GdkRGBA color_nonw;
50 | GdkRGBA color_text;
51 | GdkRGBA color_oi;
52 | GdkRGBA color_oe;
53 | GdkRGBA color_cl;
54 | GdkRGBA color_of;
55 | GtkWidget *window;
56 | GtkAdjustment *vadj;
57 |
58 | /*
59 | * Device information.
60 | */
61 | char *path;
62 | int dev_fd;
63 | struct zbd_info info;
64 | unsigned int nr_zones;
65 | unsigned int nr_conv_zones;
66 | struct zbd_zone *zones;
67 |
68 | /*
69 | * Drawn zones.
70 | */
71 | unsigned int nr_row;
72 | unsigned int nr_col;
73 | unsigned int nr_grid_zones;
74 | unsigned int max_row;
75 | struct gzv_zone *grid_zones;
76 | unsigned int grid_zno_first;
77 | };
78 |
79 | /**
80 | * System time in usecs.
81 | */
82 | static inline unsigned long long gzv_msec(void)
83 | {
84 | struct timeval tv;
85 |
86 | gettimeofday(&tv, NULL);
87 |
88 | return (unsigned long long) tv.tv_sec * 1000LL +
89 | (unsigned long long) tv.tv_usec / 1000;
90 | }
91 |
92 | extern struct gzv gzv;
93 |
94 | int gzv_report_zones(unsigned int zno_start, unsigned int nr_zones);
95 |
96 | void gzv_if_err(const char *msg, const char *fmt, ...);
97 | void gzv_if_create_window(void);
98 | void gzv_if_create(void);
99 | void gzv_if_destroy(void);
100 |
101 | #endif /* __GZWATCH_H__ */
102 |
--------------------------------------------------------------------------------
/tools/viewer/gzbd_viewer_if.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0-or-later
2 | /*
3 | * SPDX-FileCopyrightText: 2020 Western Digital Corporation or its affiliates.
4 | *
5 | * Authors: Damien Le Moal (damien.lemoal@wdc.com)
6 | */
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "gzbd_viewer.h"
13 |
14 | static void gzv_set_zone_tooltip(struct gzv_zone *zone)
15 | {
16 | struct zbd_zone *zbdz = zone->zbdz;
17 | char info[512];
18 | char bs[64];
19 |
20 | if (!zbdz) {
21 | gtk_widget_set_has_tooltip(GTK_WIDGET(zone->da), false);
22 | gtk_widget_set_tooltip_markup(GTK_WIDGET(zone->da), NULL);
23 | return;
24 | }
25 |
26 | if (gzv.block_size == 1)
27 | snprintf(bs, sizeof(bs) - 1, "B");
28 | else
29 | snprintf(bs, sizeof(bs) - 1,
30 | "%d-B blocks", gzv.block_size);
31 |
32 | if (zbd_zone_cnv(zbdz)) {
33 | snprintf(info, sizeof(info) - 1,
34 | "Zone %u:\n"
35 | " - Type: %s\n"
36 | " - Condition: %s\n"
37 | " - Start offset: %llu %s\n"
38 | " - Length: %llu %s\n"
39 | " - Capacity: %llu %s",
40 | zone->zno,
41 | zbd_zone_type_str(zbdz, false),
42 | zbd_zone_cond_str(zbdz, false),
43 | zbd_zone_start(zbdz), bs,
44 | zbd_zone_len(zbdz), bs,
45 | zbd_zone_capacity(zbdz), bs);
46 | } else {
47 | snprintf(info, sizeof(info) - 1,
48 | "Zone %u:\n"
49 | " - Type: %s\n"
50 | " - Condition: %s\n"
51 | " - Start offset: %llu %s\n"
52 | " - Length: %llu %s\n"
53 | " - Capacity: %llu %s\n"
54 | " - WP offset: +%llu %s",
55 | zone->zno,
56 | zbd_zone_type_str(zbdz, false),
57 | zbd_zone_cond_str(zbdz, false),
58 | zbd_zone_start(zbdz), bs,
59 | zbd_zone_len(zbdz), bs,
60 | zbd_zone_capacity(zbdz), bs,
61 | zbd_zone_wp(zbdz) - zbd_zone_start(zbdz), bs);
62 | }
63 |
64 | gtk_widget_set_tooltip_markup(GTK_WIDGET(zone->da), info);
65 | gtk_widget_set_has_tooltip(GTK_WIDGET(zone->da), true);
66 | }
67 |
68 | static void gzv_if_update(void)
69 | {
70 | struct zbd_zone *zbdz;
71 | unsigned int i, z;
72 |
73 | if (gzv.grid_zno_first >= gzv.nr_zones)
74 | gzv.grid_zno_first = gzv.nr_zones / gzv.nr_col;
75 |
76 | if (gzv_report_zones(gzv.grid_zno_first, gzv.nr_grid_zones))
77 | goto out;
78 |
79 | z = gzv.grid_zno_first;
80 | for (i = 0; i < gzv.nr_grid_zones; i++) {
81 | gzv.grid_zones[i].zno = z;
82 | zbdz = gzv.grid_zones[i].zbdz;
83 | if (z >= gzv.nr_zones) {
84 | if (zbdz)
85 | gtk_widget_hide(gzv.grid_zones[i].da);
86 | gzv.grid_zones[i].zbdz = NULL;
87 | } else {
88 | gzv.grid_zones[i].zbdz = &gzv.zones[z];
89 | if (!zbdz)
90 | gtk_widget_show(gzv.grid_zones[i].da);
91 | }
92 | gzv_set_zone_tooltip(&gzv.grid_zones[i]);
93 | gtk_widget_queue_draw(gzv.grid_zones[i].da);
94 | z++;
95 | }
96 |
97 | out:
98 | gzv.last_refresh = gzv_msec();
99 | }
100 |
101 | static gboolean gzv_if_timer_cb(gpointer user_data)
102 | {
103 | if (gzv.last_refresh + gzv.refresh_interval <= gzv_msec())
104 | gzv_if_update();
105 |
106 | return TRUE;
107 | }
108 |
109 | static gboolean gzv_if_resize_cb(GtkWidget *widget, GdkEvent *event,
110 | gpointer user_data)
111 | {
112 | gzv_if_update();
113 |
114 | return FALSE;
115 | }
116 |
117 | static void gzv_if_delete_cb(GtkWidget *widget, GdkEvent *event,
118 | gpointer user_data)
119 | {
120 | gzv.window = NULL;
121 | gtk_main_quit();
122 | }
123 |
124 | static void gzv_if_zone_draw_nonwritable(struct zbd_zone *zbdz, cairo_t *cr,
125 | GtkAllocation *allocation)
126 | {
127 | long long w;
128 |
129 | if (zbd_zone_capacity(zbdz) == zbd_zone_len(zbdz))
130 | return;
131 |
132 | /* Non-writable space in zone */
133 | w = (long long)allocation->width *
134 | (zbd_zone_len(zbdz) - zbd_zone_capacity(zbdz)) /
135 | zbd_zone_len(zbdz);
136 | if (w > allocation->width)
137 | w = allocation->width;
138 |
139 | gdk_cairo_set_source_rgba(cr, &gzv.color_nonw);
140 | cairo_rectangle(cr, allocation->width - w, 0, w, allocation->height);
141 | cairo_fill(cr);
142 | }
143 |
144 | static void gzv_if_zone_draw_written(struct zbd_zone *zbdz, cairo_t *cr,
145 | GtkAllocation *allocation)
146 | {
147 | long long w;
148 |
149 | if (zbd_zone_wp(zbdz) == zbd_zone_start(zbdz))
150 | return;
151 |
152 | /* Written space in zone */
153 | w = (long long)allocation->width *
154 | (zbd_zone_wp(zbdz) - zbd_zone_start(zbdz)) /
155 | zbd_zone_len(zbdz);
156 | if (w > allocation->width)
157 | w = allocation->width;
158 |
159 | gdk_cairo_set_source_rgba(cr, &gzv.color_seqw);
160 | cairo_rectangle(cr, 0, 0, w, allocation->height);
161 | cairo_fill(cr);
162 | }
163 |
164 | static void gzv_if_zone_draw_num(struct gzv_zone *zone, cairo_t *cr,
165 | GtkAllocation *allocation)
166 | {
167 | cairo_text_extents_t te;
168 | char str[16];
169 |
170 | /* Draw zone number */
171 | gdk_cairo_set_source_rgba(cr, &gzv.color_text);
172 | cairo_select_font_face(cr, "Monospace",
173 | CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
174 | cairo_set_font_size(cr, 10);
175 | sprintf(str, "%05d", zone->zno);
176 | cairo_text_extents(cr, str, &te);
177 | cairo_move_to(cr, allocation->width / 2 - te.width / 2 - te.x_bearing,
178 | (allocation->height + te.height) / 2);
179 | cairo_show_text(cr, str);
180 | }
181 |
182 | static gboolean gzv_if_zone_draw_cb(GtkWidget *widget, cairo_t *cr,
183 | gpointer user_data)
184 | {
185 | struct gzv_zone *zone = user_data;
186 | struct zbd_zone *zbdz = zone->zbdz;
187 | GtkAllocation allocation;
188 |
189 | /* Current size */
190 | gtk_widget_get_allocation(zone->da, &allocation);
191 |
192 | gtk_render_background(gtk_widget_get_style_context(widget),
193 | cr, 0, 0, allocation.width, allocation.height);
194 |
195 | if (!zbdz)
196 | return TRUE;
197 |
198 | /* Draw zone background */
199 | if (zbd_zone_cnv(zbdz)) {
200 | gdk_cairo_set_source_rgba(cr, &gzv.color_conv);
201 | goto out_fill;
202 | }
203 |
204 | if (zbd_zone_full(zbdz)) {
205 | gdk_cairo_set_source_rgba(cr, &gzv.color_seqw);
206 | goto out_fill;
207 | }
208 |
209 | if (zbd_zone_offline(zbdz)) {
210 | gdk_cairo_set_source_rgba(cr, &gzv.color_of);
211 | goto out_fill;
212 | }
213 |
214 | gdk_cairo_set_source_rgba(cr, &gzv.color_seq);
215 | if (zbd_zone_empty(zbdz))
216 | goto out_fill;
217 |
218 | /* Opened or closed zones */
219 | cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
220 | cairo_fill(cr);
221 |
222 | gzv_if_zone_draw_nonwritable(zbdz, cr, &allocation);
223 | gzv_if_zone_draw_written(zbdz, cr, &allocation);
224 |
225 | /* State highlight */
226 | if (zbd_zone_imp_open(zbdz))
227 | gdk_cairo_set_source_rgba(cr, &gzv.color_oi);
228 | else if (zbd_zone_exp_open(zbdz))
229 | gdk_cairo_set_source_rgba(cr, &gzv.color_oe);
230 | else
231 | gdk_cairo_set_source_rgba(cr, &gzv.color_cl);
232 | cairo_set_line_width(cr, 10);
233 | cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
234 | cairo_stroke(cr);
235 |
236 | /* Draw zone number */
237 | gzv_if_zone_draw_num(zone, cr, &allocation);
238 |
239 | return TRUE;
240 |
241 | out_fill:
242 | cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
243 | cairo_fill(cr);
244 |
245 | gzv_if_zone_draw_nonwritable(zbdz, cr, &allocation);
246 |
247 | /* Draw zone number */
248 | gzv_if_zone_draw_num(zone, cr, &allocation);
249 |
250 | return TRUE;
251 | }
252 |
253 | static gboolean gzv_if_scroll_cb(GtkWidget *widget, GdkEventScroll *scroll,
254 | gpointer user_data)
255 | {
256 | unsigned int row = gtk_adjustment_get_value(gzv.vadj);
257 | unsigned int new_row = row;
258 |
259 | switch (scroll->direction) {
260 | case GDK_SCROLL_UP:
261 | if (row > 0)
262 | new_row = row - 1;
263 | break;
264 | case GDK_SCROLL_DOWN:
265 | if (row < gzv.max_row)
266 | new_row = row + 1;
267 | break;
268 | case GDK_SCROLL_LEFT:
269 | case GDK_SCROLL_RIGHT:
270 | case GDK_SCROLL_SMOOTH:
271 | default:
272 | break;
273 | }
274 |
275 | if (new_row != row)
276 | gtk_adjustment_set_value(gzv.vadj, new_row);
277 |
278 | return TRUE;
279 | }
280 |
281 | static gboolean gzv_if_scroll_value_cb(GtkWidget *widget,
282 | GdkEvent *event, gpointer user_data)
283 | {
284 | unsigned int zno, row = gtk_adjustment_get_value(gzv.vadj);
285 |
286 | if (row >= gzv.max_row)
287 | row = gzv.max_row - 1;
288 |
289 | zno = row * gzv.nr_col;
290 | if (zno != gzv.grid_zno_first) {
291 | gzv.grid_zno_first = zno;
292 | gzv_if_update();
293 | }
294 |
295 | return TRUE;
296 | }
297 |
298 | static void gzv_if_draw_legend(char *str, const GdkRGBA *color, cairo_t *cr,
299 | gint *x, gint y)
300 | {
301 | cairo_text_extents_t te;
302 | GdkRGBA border_color;
303 | gint w = 10;
304 |
305 | gdk_rgba_parse(&border_color, "Black");
306 | gdk_cairo_set_source_rgba(cr, &border_color);
307 | cairo_set_line_width(cr, 2);
308 | cairo_rectangle(cr, *x, y - w / 2, w, w);
309 | cairo_stroke_preserve(cr);
310 | gdk_cairo_set_source_rgba(cr, color);
311 | cairo_fill(cr);
312 | *x += w;
313 |
314 | cairo_text_extents(cr, str, &te);
315 | cairo_move_to(cr,
316 | *x + 5 - te.x_bearing,
317 | y - te.height / 2 - te.y_bearing);
318 | cairo_show_text(cr, str);
319 | *x += te.x_advance + 20;
320 | }
321 |
322 | static gboolean gzv_if_draw_legend_cb(GtkWidget *widget, cairo_t *cr,
323 | gpointer data)
324 | {
325 | gint x = 10, y = 10;
326 |
327 | /* Set font */
328 | cairo_select_font_face(cr, "Monospace",
329 | CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
330 | cairo_set_font_size(cr, 10);
331 |
332 | /* Conv zone legend */
333 | gzv_if_draw_legend("Conventional zone", &gzv.color_conv, cr, &x, y);
334 |
335 | /* Seq zone legend */
336 | gzv_if_draw_legend("Sequential zone (unwritten)", &gzv.color_seq, cr,
337 | &x, y);
338 |
339 | /* Seq written zone legend */
340 | gzv_if_draw_legend("Sequential zone (written)", &gzv.color_seqw, cr,
341 | &x, y);
342 |
343 | /* Non-writable space legend */
344 | gzv_if_draw_legend("Non-writable space", &gzv.color_nonw, cr, &x, y);
345 |
346 | /* Second row */
347 | x = 10;
348 | y += 20;
349 |
350 | /* Offline zone legend */
351 | gzv_if_draw_legend("Offline zone", &gzv.color_of, cr, &x, y);
352 |
353 | /* Implicit open zone legend */
354 | gzv_if_draw_legend("Implicitly opened zone", &gzv.color_oi, cr, &x, y);
355 |
356 | /* Explicit open zone legend */
357 | gzv_if_draw_legend("Explicitly opened zone", &gzv.color_oe, cr, &x, y);
358 |
359 | /* Closed open zone legend */
360 | gzv_if_draw_legend("Closed zone", &gzv.color_cl, cr, &x, y);
361 |
362 | return FALSE;
363 | }
364 |
365 | static void gzv_if_get_da_size(unsigned int *w, unsigned int *h)
366 | {
367 | GdkDisplay *disp = gdk_display_get_default();
368 | GdkMonitor *mon = gdk_display_get_primary_monitor(disp);
369 | GdkRectangle geom;
370 |
371 | gdk_monitor_get_geometry(mon, &geom);
372 |
373 | *w = (geom.width - 200) / gzv.nr_col;
374 | if (*w > 150)
375 | *w = 150;
376 |
377 | *h = (geom.height - 200) / gzv.nr_row;
378 | if (*h > 60)
379 | *h = 60;
380 | }
381 |
382 | void gzv_if_create_window(void)
383 | {
384 | if (gzv.window)
385 | return;
386 |
387 | gzv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
388 | gtk_window_set_title(GTK_WINDOW(gzv.window),
389 | "Zoned Block Device Zone State");
390 | gtk_container_set_border_width(GTK_CONTAINER(gzv.window), 10);
391 |
392 | g_signal_connect((gpointer) gzv.window, "delete-event",
393 | G_CALLBACK(gzv_if_delete_cb),
394 | NULL);
395 | }
396 |
397 | void gzv_if_err(const char *msg, const char *fmt, ...)
398 | {
399 | GtkWidget *dialog;
400 |
401 | dialog = gtk_message_dialog_new(GTK_WINDOW(gzv.window),
402 | GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
403 | GTK_MESSAGE_ERROR,
404 | GTK_BUTTONS_OK,
405 | "%s", msg);
406 |
407 | if (fmt) {
408 | va_list args;
409 | char secondary[256];
410 |
411 | va_start(args, fmt);
412 | vsnprintf(secondary, 255, fmt, args);
413 | va_end(args);
414 |
415 | gtk_message_dialog_format_secondary_text
416 | (GTK_MESSAGE_DIALOG(dialog),
417 | "%s", secondary);
418 | }
419 |
420 | gtk_dialog_run(GTK_DIALOG(dialog));
421 | gtk_widget_destroy(dialog);
422 | }
423 |
424 | void gzv_if_create(void)
425 | {
426 | GtkWidget *top_vbox, *vbox, *frame, *hbox, *scrollbar, *grid, *da;
427 | GtkAdjustment *vadj;
428 | struct gzv_zone *zone;
429 | unsigned int r, c, z = 0;
430 | unsigned int da_w, da_h;
431 | char str[128];
432 |
433 | /* Get colors */
434 | gdk_rgba_parse(&gzv.color_conv, "Magenta"); /* Conv zones */
435 | gdk_rgba_parse(&gzv.color_seq, "Green"); /* Seq zones */
436 | gdk_rgba_parse(&gzv.color_seqw, "Red"); /* Seq written/full */
437 | gdk_rgba_parse(&gzv.color_nonw, "RosyBrown"); /* Non-writable */
438 | gdk_rgba_parse(&gzv.color_text, "Black");
439 | gdk_rgba_parse(&gzv.color_oe, "Blue"); /* Exp open zones */
440 | gdk_rgba_parse(&gzv.color_oi, "DeepSkyBlue"); /* Imp open zones */
441 | gdk_rgba_parse(&gzv.color_cl, "DarkOrange"); /* Closed zones */
442 | gdk_rgba_parse(&gzv.color_of, "Grey"); /* Offline zones */
443 |
444 | /* Top vbox */
445 | top_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
446 | gtk_widget_show(top_vbox);
447 | gtk_container_add(GTK_CONTAINER(gzv.window), top_vbox);
448 |
449 | /* Create a top frame */
450 | if (!gzv.nr_conv_zones)
451 | snprintf(str, sizeof(str) - 1,
452 | "%s: %u sequential zones",
453 | gzv.path, gzv.nr_zones);
454 | else
455 | snprintf(str, sizeof(str) - 1,
456 | "%s: %u zones (%u conventional + %u sequential)",
457 | gzv.path, gzv.nr_zones, gzv.nr_conv_zones,
458 | gzv.nr_zones - gzv.nr_conv_zones);
459 | frame = gtk_frame_new(str);
460 | gtk_box_pack_start(GTK_BOX(top_vbox), frame, TRUE, TRUE, 0);
461 | gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
462 | gtk_label_set_use_markup(GTK_LABEL(gtk_frame_get_label_widget(GTK_FRAME(frame))), TRUE);
463 | gtk_frame_set_label_align(GTK_FRAME(frame), 0.05, 0.5);
464 | gtk_widget_show(frame);
465 |
466 | /* hbox for grid and scrollbar */
467 | hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
468 | gtk_widget_show(hbox);
469 | gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
470 | gtk_container_add(GTK_CONTAINER(frame), hbox);
471 |
472 | /* Add a grid */
473 | grid = gtk_grid_new();
474 | gtk_container_set_border_width(GTK_CONTAINER(grid), 10);
475 | gtk_grid_set_row_homogeneous(GTK_GRID(grid), true);
476 | gtk_grid_set_row_spacing(GTK_GRID(grid), 2);
477 | gtk_grid_set_column_homogeneous(GTK_GRID(grid), true);
478 | gtk_grid_set_column_spacing(GTK_GRID(grid), 2);
479 | gtk_box_pack_start(GTK_BOX(hbox), grid, TRUE, TRUE, 0);
480 | gtk_widget_show(grid);
481 |
482 | gzv_if_get_da_size(&da_w, &da_h);
483 | for (r = 0; r < gzv.nr_row; r++) {
484 | for (c = 0; c < gzv.nr_col; c++) {
485 | zone = &gzv.grid_zones[z];
486 |
487 | da = gtk_drawing_area_new();
488 | gtk_widget_set_size_request(da, da_w, da_h);
489 | gtk_widget_set_hexpand(da, TRUE);
490 | gtk_widget_set_halign(da, GTK_ALIGN_FILL);
491 | gtk_widget_show(da);
492 |
493 | zone->da = da;
494 | gtk_grid_attach(GTK_GRID(grid), da, c, r, 1, 1);
495 | g_signal_connect(da, "draw",
496 | G_CALLBACK(gzv_if_zone_draw_cb), zone);
497 | z++;
498 | }
499 | }
500 |
501 | /* Add scrollbar */
502 | vadj = gtk_adjustment_new(0, 0, gzv.max_row, 1, 1, gzv.nr_row);
503 | gzv.vadj = vadj;
504 | g_signal_connect(vadj, "value-changed",
505 | G_CALLBACK(gzv_if_scroll_value_cb), NULL);
506 |
507 | scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadj);
508 | gtk_widget_show(scrollbar);
509 | gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, FALSE, 0);
510 | gtk_widget_add_events(scrollbar, GDK_SCROLL_MASK);
511 |
512 | gtk_widget_add_events(gzv.window, GDK_SCROLL_MASK);
513 | g_signal_connect(gzv.window, "scroll-event",
514 | G_CALLBACK(gzv_if_scroll_cb), NULL);
515 |
516 | /* Legend frame */
517 | frame = gtk_frame_new("Legend");
518 | gtk_box_pack_start(GTK_BOX(top_vbox), frame, FALSE, TRUE, 0);
519 | gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
520 | gtk_label_set_use_markup(GTK_LABEL(gtk_frame_get_label_widget(GTK_FRAME(frame))), TRUE);
521 | gtk_frame_set_label_align(GTK_FRAME(frame), 0.05, 0.5);
522 | gtk_widget_show(frame);
523 |
524 | vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
525 | gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
526 | gtk_container_add(GTK_CONTAINER(frame), vbox);
527 | gtk_widget_show(vbox);
528 |
529 | da = gtk_drawing_area_new();
530 | gtk_widget_set_size_request(da, -1, 40);
531 | gtk_widget_show(da);
532 | gtk_container_add(GTK_CONTAINER(vbox), da);
533 |
534 | g_signal_connect((gpointer) da, "draw",
535 | G_CALLBACK(gzv_if_draw_legend_cb), NULL);
536 |
537 | /* Finish setup */
538 | g_signal_connect(gzv.window, "configure-event",
539 | G_CALLBACK(gzv_if_resize_cb), NULL);
540 |
541 | /* Add timer for automatic refresh */
542 | gzv.refresh_timer = g_timeout_source_new(gzv.refresh_interval);
543 | g_source_set_name(gzv.refresh_timer, "refresh-timer");
544 | g_source_set_can_recurse(gzv.refresh_timer, FALSE);
545 | g_source_set_callback(gzv.refresh_timer, gzv_if_timer_cb,
546 | NULL, NULL);
547 | gzv.last_refresh = gzv_msec();
548 | g_source_attach(gzv.refresh_timer, NULL);
549 |
550 | gtk_widget_show_all(gzv.window);
551 | gzv_if_update();
552 | }
553 |
554 | void gzv_if_destroy(void)
555 | {
556 |
557 | if (gzv.refresh_timer) {
558 | g_source_destroy(gzv.refresh_timer);
559 | gzv.refresh_timer = NULL;
560 | }
561 |
562 | if (gzv.window) {
563 | gtk_widget_destroy(gzv.window);
564 | gzv.window = NULL;
565 | }
566 | }
567 |
--------------------------------------------------------------------------------
/tools/viewer/org.gnome.gzbd-viewer.policy.in:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | The libzbd Project
7 | https://github.com/westerndigitalcorporation/libzbd
8 | gzbd-viewer
9 |
10 |
11 | Run gzbd-viewer as root
12 | Authentication is required to run gzbd-viewer as root
13 |
14 | auth_admin
15 | auth_admin
16 | auth_admin
17 |
18 | @bindir@/gzbd-viewer
19 | true
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------