pax_global_header00006660000000000000000000000064145221357240014517gustar00rootroot0000000000000052 comment=31370119ef8e05b030952551c47656e9fa369fc2 golang-github-moby-sys-0.0~git20231105.a4e0878/000077500000000000000000000000001452213572400204045ustar00rootroot00000000000000golang-github-moby-sys-0.0~git20231105.a4e0878/.gitattributes000066400000000000000000000002251452213572400232760ustar00rootroot00000000000000# Default * text=auto # Go files should always have UNIX-style line endings # (see e.g. https://github.com/golang/go/issues/16355) *.go text eol=lf golang-github-moby-sys-0.0~git20231105.a4e0878/.github/000077500000000000000000000000001452213572400217445ustar00rootroot00000000000000golang-github-moby-sys-0.0~git20231105.a4e0878/.github/workflows/000077500000000000000000000000001452213572400240015ustar00rootroot00000000000000golang-github-moby-sys-0.0~git20231105.a4e0878/.github/workflows/test.yml000066400000000000000000000012451452213572400255050ustar00rootroot00000000000000on: [push, pull_request] name: Test jobs: test: strategy: matrix: go-version: [1.17.x, 1.18.x] platform: [ubuntu-20.04, ubuntu-22.04, windows-latest, macos-11] runs-on: ${{ matrix.platform }} steps: - name: Install Go uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v3 - name: go mod tidy run: | make tidy git diff --exit-code - name: Lint run: make lint - name: Cross build if: ${{ runner.os == 'Linux' }} run: make cross - name: Test run: | uname -a make test golang-github-moby-sys-0.0~git20231105.a4e0878/.gitignore000066400000000000000000000000331452213572400223700ustar00rootroot00000000000000/_build/ /mount/go-local.* golang-github-moby-sys-0.0~git20231105.a4e0878/.golangci.yml000066400000000000000000000001011452213572400227600ustar00rootroot00000000000000linters: enable: - unconvert - errorlint - gofumpt golang-github-moby-sys-0.0~git20231105.a4e0878/LICENSE000066400000000000000000000261361452213572400214210ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-moby-sys-0.0~git20231105.a4e0878/Makefile000066400000000000000000000033151452213572400220460ustar00rootroot00000000000000PACKAGES ?= mountinfo mount sequential signal symlink user BINDIR ?= _build/bin CROSS ?= linux/arm linux/arm64 linux/ppc64le linux/s390x \ freebsd/amd64 openbsd/amd64 darwin/amd64 darwin/arm64 windows/amd64 SUDO ?= sudo -n test test-local: RUN_VIA_SUDO = $(shell $(SUDO) true && echo -exec \"$(SUDO)\") .PHONY: all all: clean lint test cross .PHONY: clean clean: $(RM) mount/go-local.* .PHONY: test test: test-local set -eu; \ for p in $(PACKAGES); do \ (cd $$p; go test $(RUN_VIA_SUDO) -v .); \ done .PHONY: tidy tidy: set -eu; \ for p in $(PACKAGES); do \ (cd $$p; go mod tidy); \ done # Test the mount module against the local mountinfo source code instead of the # release specified in its go.mod. This allows catching regressions / breaking # changes in mountinfo. .PHONY: test-local test-local: MOD = -modfile=go-local.mod test-local: echo 'replace github.com/moby/sys/mountinfo => ../mountinfo' | cat mount/go.mod - > mount/go-local.mod # Run go mod tidy to make sure mountinfo dependency versions are met. cd mount && go mod tidy $(MOD) && go test $(MOD) $(RUN_VIA_SUDO) -v . $(RM) mount/go-local.* .PHONY: lint lint: $(BINDIR)/golangci-lint $(BINDIR)/golangci-lint version set -eu; \ for p in $(PACKAGES); do \ (cd $$p; \ go mod download; \ ../$(BINDIR)/golangci-lint run); \ done $(BINDIR)/golangci-lint: $(BINDIR) curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(BINDIR) v1.45.2 $(BINDIR): mkdir -p $(BINDIR) .PHONY: cross cross: set -eu; \ for osarch in $(CROSS); do \ export GOOS=$${osarch%/*} GOARCH=$${osarch#*/}; \ echo "# building for $$GOOS/$$GOARCH"; \ for p in $(PACKAGES); do \ (cd $$p; go build .); \ done; \ done golang-github-moby-sys-0.0~git20231105.a4e0878/mount/000077500000000000000000000000001452213572400215465ustar00rootroot00000000000000golang-github-moby-sys-0.0~git20231105.a4e0878/mount/doc.go000066400000000000000000000002731452213572400226440ustar00rootroot00000000000000// Package mount provides a set of functions to mount and unmount mounts. // // Currently it supports Linux. For historical reasons, there is also some support for FreeBSD. package mount golang-github-moby-sys-0.0~git20231105.a4e0878/mount/flags_bsd.go000066400000000000000000000017361452213572400240300ustar00rootroot00000000000000//go:build freebsd || openbsd // +build freebsd openbsd package mount import "golang.org/x/sys/unix" const ( // RDONLY will mount the filesystem as read-only. RDONLY = unix.MNT_RDONLY // NOSUID will not allow set-user-identifier or set-group-identifier bits to // take effect. NOSUID = unix.MNT_NOSUID // NOEXEC will not allow execution of any binaries on the mounted file system. NOEXEC = unix.MNT_NOEXEC // SYNCHRONOUS will allow any I/O to the file system to be done synchronously. SYNCHRONOUS = unix.MNT_SYNCHRONOUS // NOATIME will not update the file access time when reading from a file. NOATIME = unix.MNT_NOATIME ) // These flags are unsupported. const ( BIND = 0 DIRSYNC = 0 MANDLOCK = 0 NODEV = 0 NODIRATIME = 0 UNBINDABLE = 0 RUNBINDABLE = 0 PRIVATE = 0 RPRIVATE = 0 SHARED = 0 RSHARED = 0 SLAVE = 0 RSLAVE = 0 RBIND = 0 RELATIME = 0 REMOUNT = 0 STRICTATIME = 0 mntDetach = 0 ) golang-github-moby-sys-0.0~git20231105.a4e0878/mount/flags_linux.go000066400000000000000000000054131452213572400244130ustar00rootroot00000000000000package mount import ( "golang.org/x/sys/unix" ) const ( // RDONLY will mount the file system read-only. RDONLY = unix.MS_RDONLY // NOSUID will not allow set-user-identifier or set-group-identifier bits to // take effect. NOSUID = unix.MS_NOSUID // NODEV will not interpret character or block special devices on the file // system. NODEV = unix.MS_NODEV // NOEXEC will not allow execution of any binaries on the mounted file system. NOEXEC = unix.MS_NOEXEC // SYNCHRONOUS will allow I/O to the file system to be done synchronously. SYNCHRONOUS = unix.MS_SYNCHRONOUS // DIRSYNC will force all directory updates within the file system to be done // synchronously. This affects the following system calls: create, link, // unlink, symlink, mkdir, rmdir, mknod and rename. DIRSYNC = unix.MS_DIRSYNC // REMOUNT will attempt to remount an already-mounted file system. This is // commonly used to change the mount flags for a file system, especially to // make a readonly file system writeable. It does not change device or mount // point. REMOUNT = unix.MS_REMOUNT // MANDLOCK will force mandatory locks on a filesystem. MANDLOCK = unix.MS_MANDLOCK // NOATIME will not update the file access time when reading from a file. NOATIME = unix.MS_NOATIME // NODIRATIME will not update the directory access time. NODIRATIME = unix.MS_NODIRATIME // BIND remounts a subtree somewhere else. BIND = unix.MS_BIND // RBIND remounts a subtree and all possible submounts somewhere else. RBIND = unix.MS_BIND | unix.MS_REC // UNBINDABLE creates a mount which cannot be cloned through a bind operation. UNBINDABLE = unix.MS_UNBINDABLE // RUNBINDABLE marks the entire mount tree as UNBINDABLE. RUNBINDABLE = unix.MS_UNBINDABLE | unix.MS_REC // PRIVATE creates a mount which carries no propagation abilities. PRIVATE = unix.MS_PRIVATE // RPRIVATE marks the entire mount tree as PRIVATE. RPRIVATE = unix.MS_PRIVATE | unix.MS_REC // SLAVE creates a mount which receives propagation from its master, but not // vice versa. SLAVE = unix.MS_SLAVE // RSLAVE marks the entire mount tree as SLAVE. RSLAVE = unix.MS_SLAVE | unix.MS_REC // SHARED creates a mount which provides the ability to create mirrors of // that mount such that mounts and unmounts within any of the mirrors // propagate to the other mirrors. SHARED = unix.MS_SHARED // RSHARED marks the entire mount tree as SHARED. RSHARED = unix.MS_SHARED | unix.MS_REC // RELATIME updates inode access times relative to modify or change time. RELATIME = unix.MS_RELATIME // STRICTATIME allows to explicitly request full atime updates. This makes // it possible for the kernel to default to relatime or noatime but still // allow userspace to override it. STRICTATIME = unix.MS_STRICTATIME mntDetach = unix.MNT_DETACH ) golang-github-moby-sys-0.0~git20231105.a4e0878/mount/flags_unix.go000066400000000000000000000071521452213572400242410ustar00rootroot00000000000000//go:build !darwin && !windows // +build !darwin,!windows package mount import ( "fmt" "strings" ) var flags = map[string]struct { clear bool flag int }{ "defaults": {false, 0}, "ro": {false, RDONLY}, "rw": {true, RDONLY}, "suid": {true, NOSUID}, "nosuid": {false, NOSUID}, "dev": {true, NODEV}, "nodev": {false, NODEV}, "exec": {true, NOEXEC}, "noexec": {false, NOEXEC}, "sync": {false, SYNCHRONOUS}, "async": {true, SYNCHRONOUS}, "dirsync": {false, DIRSYNC}, "remount": {false, REMOUNT}, "mand": {false, MANDLOCK}, "nomand": {true, MANDLOCK}, "atime": {true, NOATIME}, "noatime": {false, NOATIME}, "diratime": {true, NODIRATIME}, "nodiratime": {false, NODIRATIME}, "bind": {false, BIND}, "rbind": {false, RBIND}, "unbindable": {false, UNBINDABLE}, "runbindable": {false, RUNBINDABLE}, "private": {false, PRIVATE}, "rprivate": {false, RPRIVATE}, "shared": {false, SHARED}, "rshared": {false, RSHARED}, "slave": {false, SLAVE}, "rslave": {false, RSLAVE}, "relatime": {false, RELATIME}, "norelatime": {true, RELATIME}, "strictatime": {false, STRICTATIME}, "nostrictatime": {true, STRICTATIME}, } var validFlags = map[string]bool{ "": true, "size": true, "mode": true, "uid": true, "gid": true, "nr_inodes": true, "nr_blocks": true, "mpol": true, } var propagationFlags = map[string]bool{ "bind": true, "rbind": true, "unbindable": true, "runbindable": true, "private": true, "rprivate": true, "shared": true, "rshared": true, "slave": true, "rslave": true, } // MergeTmpfsOptions merge mount options to make sure there is no duplicate. func MergeTmpfsOptions(options []string) ([]string, error) { // We use collisions maps to remove duplicates. // For flag, the key is the flag value (the key for propagation flag is -1) // For data=value, the key is the data flagCollisions := map[int]bool{} dataCollisions := map[string]bool{} var newOptions []string // We process in reverse order for i := len(options) - 1; i >= 0; i-- { option := options[i] if option == "defaults" { continue } if f, ok := flags[option]; ok && f.flag != 0 { // There is only one propagation mode key := f.flag if propagationFlags[option] { key = -1 } // Check to see if there is collision for flag if !flagCollisions[key] { // We prepend the option and add to collision map newOptions = append([]string{option}, newOptions...) flagCollisions[key] = true } continue } opt := strings.SplitN(option, "=", 2) if len(opt) != 2 || !validFlags[opt[0]] { return nil, fmt.Errorf("invalid tmpfs option %q", opt) } if !dataCollisions[opt[0]] { // We prepend the option and add to collision map newOptions = append([]string{option}, newOptions...) dataCollisions[opt[0]] = true } } return newOptions, nil } // Parse fstab type mount options into mount() flags // and device specific data func parseOptions(options string) (int, string) { var ( flag int data []string ) for _, o := range strings.Split(options, ",") { // If the option does not exist in the flags table or the flag // is not supported on the platform, // then it is a data value for a specific fs type if f, exists := flags[o]; exists && f.flag != 0 { if f.clear { flag &= ^f.flag } else { flag |= f.flag } } else { data = append(data, o) } } return flag, strings.Join(data, ",") } golang-github-moby-sys-0.0~git20231105.a4e0878/mount/go.mod000066400000000000000000000002221452213572400226500ustar00rootroot00000000000000module github.com/moby/sys/mount go 1.16 require ( github.com/moby/sys/mountinfo v0.6.2 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a ) golang-github-moby-sys-0.0~git20231105.a4e0878/mount/go.sum000066400000000000000000000006001452213572400226750ustar00rootroot00000000000000github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang-github-moby-sys-0.0~git20231105.a4e0878/mount/mount_errors.go000066400000000000000000000016471452213572400246430ustar00rootroot00000000000000//go:build !darwin && !windows // +build !darwin,!windows package mount import "strconv" // mountError records an error from mount or unmount operation type mountError struct { op string source, target string flags uintptr data string err error } func (e *mountError) Error() string { out := e.op + " " if e.source != "" { out += e.source + ":" + e.target } else { out += e.target } if e.flags != uintptr(0) { out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16) } if e.data != "" { out += ", data: " + e.data } out += ": " + e.err.Error() return out } // Cause returns the underlying cause of the error. // This is a convention used in github.com/pkg/errors func (e *mountError) Cause() error { return e.err } // Unwrap returns the underlying error. // This is a convention used in golang 1.13+ func (e *mountError) Unwrap() error { return e.err } golang-github-moby-sys-0.0~git20231105.a4e0878/mount/mount_unix.go000066400000000000000000000051241452213572400243040ustar00rootroot00000000000000//go:build !darwin && !windows // +build !darwin,!windows package mount import ( "fmt" "sort" "github.com/moby/sys/mountinfo" "golang.org/x/sys/unix" ) // Mount will mount filesystem according to the specified configuration. // Options must be specified like the mount or fstab unix commands: // "opt1=val1,opt2=val2". See flags.go for supported option flags. func Mount(device, target, mType, options string) error { flag, data := parseOptions(options) return mount(device, target, mType, uintptr(flag), data) } // Unmount lazily unmounts a filesystem on supported platforms, otherwise does // a normal unmount. If target is not a mount point, no error is returned. func Unmount(target string) error { err := unix.Unmount(target, mntDetach) if err == nil || err == unix.EINVAL { //nolint:errorlint // unix errors are bare // Ignore "not mounted" error here. Note the same error // can be returned if flags are invalid, so this code // assumes that the flags value is always correct. return nil } return &mountError{ op: "umount", target: target, flags: uintptr(mntDetach), err: err, } } // RecursiveUnmount unmounts the target and all mounts underneath, starting // with the deepest mount first. The argument does not have to be a mount // point itself. func RecursiveUnmount(target string) error { // Fast path, works if target is a mount point that can be unmounted. // On Linux, mntDetach flag ensures a recursive unmount. For other // platforms, if there are submounts, we'll get EBUSY (and fall back // to the slow path). NOTE we do not ignore EINVAL here as target might // not be a mount point itself (but there can be mounts underneath). if err := unix.Unmount(target, mntDetach); err == nil { return nil } // Slow path: get all submounts, sort, unmount one by one. mounts, err := mountinfo.GetMounts(mountinfo.PrefixFilter(target)) if err != nil { return err } // Make the deepest mount be first sort.Slice(mounts, func(i, j int) bool { return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint) }) var ( suberr error lastMount = len(mounts) - 1 ) for i, m := range mounts { err = Unmount(m.Mountpoint) if err != nil { if i == lastMount { if suberr != nil { return fmt.Errorf("%w (possible cause: %s)", err, suberr) } return err } // This is a submount, we can ignore the error for now, // the final unmount will fail if this is a real problem. // With that in mind, the _first_ failed unmount error // might be the real error cause, so let's keep it. if suberr == nil { suberr = err } } } return nil } golang-github-moby-sys-0.0~git20231105.a4e0878/mount/mount_unix_test.go000066400000000000000000000151771452213572400253540ustar00rootroot00000000000000//go:build !darwin && !windows // +build !darwin,!windows package mount import ( "errors" "os" "path" "strings" "testing" "github.com/moby/sys/mountinfo" "golang.org/x/sys/unix" ) func TestMountOptionsParsing(t *testing.T) { options := "noatime,ro,noexec,size=10k" flag, data := parseOptions(options) if data != "size=10k" { t.Fatalf("Expected size=10 got %s", data) } expected := NOATIME | RDONLY | NOEXEC if flag != expected { t.Fatalf("Expected %d got %d", expected, flag) } } func TestMounted(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } var ( tmp = t.TempDir() sourceDir = path.Join(tmp, "source") targetDir = path.Join(tmp, "target") sourcePath = path.Join(sourceDir, "file.txt") targetPath = path.Join(targetDir, "file.txt") ) if err := os.Mkdir(sourceDir, 0o777); err != nil { t.Fatal(err) } if err := os.Mkdir(targetDir, 0o777); err != nil { t.Fatal(err) } if err := os.WriteFile(sourcePath, []byte("hello"), 0o644); err != nil { t.Fatal(err) } if err := os.WriteFile(targetPath, nil, 0o644); err != nil { t.Fatal(err) } if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { t.Fatal(err) } defer func() { if err := Unmount(targetDir); err != nil { t.Fatal(err) } }() mounted, err := mountinfo.Mounted(targetDir) if err != nil { t.Fatal(err) } if !mounted { t.Fatalf("Expected %s to be mounted", targetDir) } if _, err := os.Stat(targetDir); err != nil { t.Fatal(err) } } func TestMountTmpfsOptions(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } testCases := []struct { opts string expected string unexpected string }{ { opts: "exec", unexpected: "noexec", }, { opts: "noexec", expected: "noexec", unexpected: "exec", }, } target := t.TempDir() for _, tc := range testCases { t.Run(tc.opts, func(t *testing.T) { if err := Mount("tmpfs", target, "tmpfs", tc.opts); err != nil { t.Fatal(err) } defer ensureUnmount(t, target) mounts, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(target)) if err != nil { t.Fatal(err) } if len(mounts) != 1 { t.Fatal("Mount point ", target, " not found") } entry := mounts[0] opts := "," + entry.Options + "," if tc.expected != "" && !strings.Contains(opts, ","+tc.expected+",") { t.Fatal("Expected option ", tc.expected, " missing from ", entry.Options) } if tc.unexpected != "" && strings.Contains(opts, ","+tc.unexpected+",") { t.Fatal("Unexpected option ", tc.unexpected, " in ", entry.Options) } }) } } func TestMountReadonly(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } var ( tmp = t.TempDir() sourceDir = path.Join(tmp, "source") targetDir = path.Join(tmp, "target") sourcePath = path.Join(sourceDir, "file.txt") targetPath = path.Join(targetDir, "file.txt") ) if err := os.Mkdir(sourceDir, 0o777); err != nil { t.Fatal(err) } if err := os.Mkdir(targetDir, 0o777); err != nil { t.Fatal(err) } if err := os.WriteFile(sourcePath, []byte("hello"), 0o644); err != nil { t.Fatal(err) } if err := os.WriteFile(targetPath, nil, 0o644); err != nil { t.Fatal(err) } if err := Mount(sourceDir, targetDir, "none", "bind,ro"); err != nil { t.Fatal(err) } defer func() { if err := Unmount(targetDir); err != nil { t.Fatal(err) } }() if err := os.WriteFile(targetPath, []byte("hello"), 0o644); err == nil { t.Fatal("Should not be able to open a ro file as rw") } } func TestMergeTmpfsOptions(t *testing.T) { options := []string{"noatime", "ro", "size=10k", "defaults", "noexec", "atime", "defaults", "rw", "rprivate", "size=1024k", "slave", "exec"} expected := []string{"atime", "rw", "size=1024k", "slave", "exec"} merged, err := MergeTmpfsOptions(options) if err != nil { t.Fatal(err) } if len(expected) != len(merged) { t.Fatalf("Expected %s got %s", expected, merged) } for index := range merged { if merged[index] != expected[index] { t.Fatalf("Expected %s for the %dth option, got %s", expected, index, merged) } } options = []string{"noatime", "ro", "size=10k", "atime", "rw", "rprivate", "size=1024k", "slave", "size", "exec"} _, err = MergeTmpfsOptions(options) if err == nil { t.Fatal("Expected error got nil") } } func TestRecursiveUnmountTooGreedy(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } tmp := t.TempDir() // Create a bunch of tmpfs mounts. Make sure "dir" itself is not // a mount point, or we'll hit the fast path in RecursiveUnmount. dirs := []string{"dir-other", "dir/subdir1", "dir/subdir1/subsub", "dir/subdir2/subsub"} for _, d := range dirs { dir := path.Join(tmp, d) if err := os.MkdirAll(dir, 0o700); err != nil { t.Fatal(err) } if err := Mount("tmpfs", dir, "tmpfs", ""); err != nil { t.Fatal(err) } //nolint:errcheck defer Unmount(dir) } // sanity check mounted, err := mountinfo.Mounted(path.Join(tmp, "dir-other")) if err != nil { t.Fatalf("[pre-check] error from mountinfo.mounted: %v", err) } if !mounted { t.Fatal("[pre-check] expected dir-other to be mounted, but it's not") } // Unmount dir, make sure dir-other is still mounted. if err := RecursiveUnmount(path.Join(tmp, "dir")); err != nil { t.Fatal(err) } mounted, err = mountinfo.Mounted(path.Join(tmp, "dir-other")) if err != nil { t.Fatalf("error from mountinfo.mounted: %v", err) } if !mounted { t.Fatal("expected dir-other to be mounted, but it's not") } } func TestRecursiveUnmount_SubMountFailsToUnmount(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } var ( tmp = t.TempDir() parent = tmp + "/sub1" child = tmp + "/sub1/sub2" grandChild = tmp + "/sub1/sub2/sub3" ) err := os.MkdirAll(grandChild, 0o700) if err != nil { t.Fatal(err) } // Create a set of mounts that should result in RecursiveUnmount failure, // caused by the fact that the grandchild mount is shadowed by the child mount, // and the child mount is shadowed by the parent mount. So. these two mounts // are listed in mountinfo, but since they are unreachable, unmount will fail. toMount := []string{grandChild, child, parent} for _, dir := range toMount { dir := dir if err := Mount("tmpfs", dir, "tmpfs", ""); err != nil { t.Fatal(err) } defer Unmount(dir) //nolint:errcheck } // unmount shadowed mounts shadowedMounts := []string{child, grandChild} for _, shadowedMount := range shadowedMounts { t.Run(shadowedMount, func(t *testing.T) { err := RecursiveUnmount(shadowedMount) if !errors.Is(err, unix.ENOENT) { t.Fatalf("expected submount(shadowed) %s to return unix.ENOENT, got %v", shadowedMount, err) } }) } } golang-github-moby-sys-0.0~git20231105.a4e0878/mount/mounter_freebsd.go000066400000000000000000000023501452213572400252600ustar00rootroot00000000000000//go:build freebsd && cgo // +build freebsd,cgo package mount /* #include #include #include #include #include #include */ import "C" import ( "strings" "syscall" "unsafe" ) func allocateIOVecs(options []string) []C.struct_iovec { out := make([]C.struct_iovec, len(options)) for i, option := range options { out[i].iov_base = unsafe.Pointer(C.CString(option)) out[i].iov_len = C.size_t(len(option) + 1) } return out } func mount(device, target, mType string, flag uintptr, data string) error { isNullFS := false xs := strings.Split(data, ",") for _, x := range xs { if x == "bind" { isNullFS = true } } options := []string{"fspath", target} if isNullFS { options = append(options, "fstype", "nullfs", "target", device) } else { options = append(options, "fstype", mType, "from", device) } rawOptions := allocateIOVecs(options) for _, rawOption := range rawOptions { defer C.free(rawOption.iov_base) } if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 { return &mountError{ op: "mount", source: device, target: target, flags: flag, err: syscall.Errno(errno), } } return nil } golang-github-moby-sys-0.0~git20231105.a4e0878/mount/mounter_linux.go000066400000000000000000000035341452213572400250120ustar00rootroot00000000000000package mount import ( "golang.org/x/sys/unix" ) const ( // ptypes is the set propagation types. ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE // pflags is the full set valid flags for a change propagation call. pflags = ptypes | unix.MS_REC | unix.MS_SILENT // broflags is the combination of bind and read only broflags = unix.MS_BIND | unix.MS_RDONLY ) // isremount returns true if either device name or flags identify a remount request, false otherwise. func isremount(device string, flags uintptr) bool { switch { // We treat device "" and "none" as a remount request to provide compatibility with // requests that don't explicitly set MS_REMOUNT such as those manipulating bind mounts. case flags&unix.MS_REMOUNT != 0, device == "", device == "none": return true default: return false } } func mount(device, target, mType string, flags uintptr, data string) error { oflags := flags &^ ptypes if !isremount(device, flags) || data != "" { // Initial call applying all non-propagation flags for mount // or remount with changed data if err := unix.Mount(device, target, mType, oflags, data); err != nil { return &mountError{ op: "mount", source: device, target: target, flags: oflags, data: data, err: err, } } } if flags&ptypes != 0 { // Change the propagation type. if err := unix.Mount("", target, "", flags&pflags, ""); err != nil { return &mountError{ op: "remount", target: target, flags: flags & pflags, err: err, } } } if oflags&broflags == broflags { // Remount the bind to apply read only. if err := unix.Mount("", target, "", oflags|unix.MS_REMOUNT, ""); err != nil { return &mountError{ op: "remount-ro", target: target, flags: oflags | unix.MS_REMOUNT, err: err, } } } return nil } golang-github-moby-sys-0.0~git20231105.a4e0878/mount/mounter_linux_test.go000066400000000000000000000132121452213572400260430ustar00rootroot00000000000000package mount import ( "fmt" "os" "strings" "testing" "github.com/moby/sys/mountinfo" ) func TestMount(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } source := t.TempDir() // Ensure we have a known start point by mounting tmpfs with given options if err := Mount("tmpfs", source, "tmpfs", "private"); err != nil { t.Fatal(err) } defer ensureUnmount(t, source) validateMount(t, source, "", "", "") if t.Failed() { t.FailNow() } target := t.TempDir() tests := []struct { source string ftype string options string expectedOpts string expectedOptional string expectedVFS string }{ // No options {"tmpfs", "tmpfs", "", "", "", ""}, // tmpfs mount with noexec set {"tmpfs", "tmpfs", "noexec", "noexec", "", ""}, // Default rw / ro test {source, "", "bind", "", "", ""}, {source, "", "bind,private", "", "", ""}, {source, "", "bind,shared", "", "shared", ""}, {source, "", "bind,slave", "", "master", ""}, {source, "", "bind,unbindable", "", "unbindable", ""}, // Read Write tests {source, "", "bind,rw", "rw", "", ""}, {source, "", "bind,rw,private", "rw", "", ""}, {source, "", "bind,rw,shared", "rw", "shared", ""}, {source, "", "bind,rw,slave", "rw", "master", ""}, {source, "", "bind,rw,unbindable", "rw", "unbindable", ""}, // Read Only tests {source, "", "bind,ro", "ro", "", ""}, {source, "", "bind,ro,private", "ro", "", ""}, {source, "", "bind,ro,shared", "ro", "shared", ""}, {source, "", "bind,ro,slave", "ro", "master", ""}, {source, "", "bind,ro,unbindable", "ro", "unbindable", ""}, // Remount tests to change per filesystem options {"", "", "remount,size=128k", "rw", "", "rw,size=128k"}, {"", "", "remount,ro,size=128k", "ro", "", "ro,size=128k"}, } for _, tc := range tests { ftype, options := tc.ftype, tc.options if tc.ftype == "" { ftype = "none" } if tc.options == "" { options = "none" } t.Run(fmt.Sprintf("%v-%v", ftype, options), func(t *testing.T) { if strings.Contains(tc.options, "slave") { // Slave requires a shared source if err := MakeShared(source); err != nil { t.Fatal(err) } defer func() { if err := MakePrivate(source); err != nil { t.Fatal(err) } }() } if strings.Contains(tc.options, "remount") { // create a new mount to remount first if err := Mount("tmpfs", target, "tmpfs", ""); err != nil { t.Fatal(err) } } if err := Mount(tc.source, target, tc.ftype, tc.options); err != nil { t.Fatal(err) } defer ensureUnmount(t, target) validateMount(t, target, tc.expectedOpts, tc.expectedOptional, tc.expectedVFS) }) } } // ensureUnmount umounts mnt checking for errors func ensureUnmount(t *testing.T, mnt string) { if err := Unmount(mnt); err != nil { t.Error(err) } } // validateMount checks that mnt has the given options func validateMount(t *testing.T, mnt string, opts, optional, vfs string) { info, err := mountinfo.GetMounts(nil) if err != nil { t.Fatal(err) } wantedOpts := make(map[string]struct{}) if opts != "" { for _, opt := range strings.Split(opts, ",") { wantedOpts[opt] = struct{}{} } } wantedOptional := make(map[string]struct{}) if optional != "" { for _, opt := range strings.Split(optional, ",") { wantedOptional[opt] = struct{}{} } } wantedVFS := make(map[string]struct{}) if vfs != "" { for _, opt := range strings.Split(vfs, ",") { wantedVFS[opt] = struct{}{} } } mnts := make(map[int]*mountinfo.Info, len(info)) for _, mi := range info { mnts[mi.ID] = mi } for _, mi := range info { if mi.Mountpoint != mnt { continue } // Use parent info as the defaults p := mnts[mi.Parent] pOpts := make(map[string]struct{}) if p.Options != "" { for _, opt := range strings.Split(p.Options, ",") { pOpts[clean(opt)] = struct{}{} } } pOptional := make(map[string]struct{}) if p.Optional != "" { for _, field := range strings.Split(p.Optional, ",") { pOptional[clean(field)] = struct{}{} } } // Validate Options if mi.Options != "" { for _, opt := range strings.Split(mi.Options, ",") { opt = clean(opt) if !has(wantedOpts, opt) && !has(pOpts, opt) && opt != "relatime" { t.Errorf("unexpected mount option %q, expected %q", opt, opts) } delete(wantedOpts, opt) } } for opt := range wantedOpts { t.Errorf("missing mount option %q, found %q", opt, mi.Options) } // Validate Optional if mi.Optional != "" { for _, field := range strings.Split(mi.Optional, ",") { field = clean(field) if !has(wantedOptional, field) && !has(pOptional, field) { t.Errorf("unexpected optional field %q, expected %q", field, optional) } delete(wantedOptional, field) } } for field := range wantedOptional { t.Errorf("missing optional field %q, found %q", field, mi.Optional) } // Validate VFS if set if vfs != "" { if mi.VFSOptions != "" { for _, opt := range strings.Split(mi.VFSOptions, ",") { opt = clean(opt) if !has(wantedVFS, opt) && opt != "seclabel" && // can be added by selinux opt != "inode64" && opt != "inode32" { // can be added by kernel 5.9+ t.Errorf("unexpected vfs option %q, expected %q", opt, vfs) } delete(wantedVFS, opt) } } for opt := range wantedVFS { t.Errorf("missing vfs option %q, found %q", opt, mi.VFSOptions) } } return } t.Errorf("failed to find mount %q", mnt) } // clean strips off any value param after the colon func clean(v string) string { return strings.SplitN(v, ":", 2)[0] } // has returns true if key is a member of m func has(m map[string]struct{}, key string) bool { _, ok := m[key] return ok } golang-github-moby-sys-0.0~git20231105.a4e0878/mount/mounter_openbsd.go000066400000000000000000000027631452213572400253100ustar00rootroot00000000000000//go:build openbsd && cgo // +build openbsd,cgo /* Due to how OpenBSD mount(2) works, filesystem types need to be supported explicitly since it uses separate structs to pass filesystem-specific arguments. For now only UFS/FFS is supported as it's the default fs on OpenBSD systems. See: https://man.openbsd.org/mount.2 */ package mount /* #include #include */ import "C" import ( "fmt" "syscall" "unsafe" ) func createExportInfo(readOnly bool) C.struct_export_args { exportFlags := C.int(0) if readOnly { exportFlags = C.MNT_EXRDONLY } out := C.struct_export_args{ ex_root: 0, ex_flags: exportFlags, } return out } func createUfsArgs(device string, readOnly bool) unsafe.Pointer { out := &C.struct_ufs_args{ fspec: C.CString(device), export_info: createExportInfo(readOnly), } return unsafe.Pointer(out) } func mount(device, target, mType string, flag uintptr, data string) error { readOnly := flag&RDONLY != 0 var fsArgs unsafe.Pointer switch mType { case "ffs": fsArgs = createUfsArgs(device, readOnly) default: return &mountError{ op: "mount", source: device, target: target, flags: flag, err: fmt.Errorf("unsupported file system type: %s", mType), } } if errno := C.mount(C.CString(mType), C.CString(target), C.int(flag), fsArgs); errno != 0 { return &mountError{ op: "mount", source: device, target: target, flags: flag, err: syscall.Errno(errno), } } return nil } golang-github-moby-sys-0.0~git20231105.a4e0878/mount/mounter_unsupported.go000066400000000000000000000005071452213572400262400ustar00rootroot00000000000000//go:build (!linux && !freebsd && !openbsd && !windows && !darwin) || (freebsd && !cgo) || (openbsd && !cgo) // +build !linux,!freebsd,!openbsd,!windows,!darwin freebsd,!cgo openbsd,!cgo package mount func mount(device, target, mType string, flag uintptr, data string) error { panic("cgo required on freebsd and openbsd") } golang-github-moby-sys-0.0~git20231105.a4e0878/mount/sharedsubtree_linux.go000066400000000000000000000046231452213572400261610ustar00rootroot00000000000000package mount import "github.com/moby/sys/mountinfo" // MakeShared ensures a mounted filesystem has the SHARED mount option enabled. // See the supported options in flags.go for further reference. func MakeShared(mountPoint string) error { return ensureMountedAs(mountPoint, SHARED) } // MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled. // See the supported options in flags.go for further reference. func MakeRShared(mountPoint string) error { return ensureMountedAs(mountPoint, RSHARED) } // MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled. // See the supported options in flags.go for further reference. func MakePrivate(mountPoint string) error { return ensureMountedAs(mountPoint, PRIVATE) } // MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option // enabled. See the supported options in flags.go for further reference. func MakeRPrivate(mountPoint string) error { return ensureMountedAs(mountPoint, RPRIVATE) } // MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled. // See the supported options in flags.go for further reference. func MakeSlave(mountPoint string) error { return ensureMountedAs(mountPoint, SLAVE) } // MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled. // See the supported options in flags.go for further reference. func MakeRSlave(mountPoint string) error { return ensureMountedAs(mountPoint, RSLAVE) } // MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option // enabled. See the supported options in flags.go for further reference. func MakeUnbindable(mountPoint string) error { return ensureMountedAs(mountPoint, UNBINDABLE) } // MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount // option enabled. See the supported options in flags.go for further reference. func MakeRUnbindable(mountPoint string) error { return ensureMountedAs(mountPoint, RUNBINDABLE) } // MakeMount ensures that the file or directory given is a mount point, // bind mounting it to itself it case it is not. func MakeMount(mnt string) error { mounted, err := mountinfo.Mounted(mnt) if err != nil { return err } if mounted { return nil } return mount(mnt, mnt, "none", uintptr(BIND), "") } func ensureMountedAs(mnt string, flags int) error { if err := MakeMount(mnt); err != nil { return err } return mount("", mnt, "none", uintptr(flags), "") } golang-github-moby-sys-0.0~git20231105.a4e0878/mount/sharedsubtree_linux_test.go000066400000000000000000000174001452213572400272150ustar00rootroot00000000000000package mount import ( "errors" "os" "path" "testing" "golang.org/x/sys/unix" ) // nothing is propagated in or out func TestSubtreePrivate(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } var ( tmp = t.TempDir() sourceDir = path.Join(tmp, "source") targetDir = path.Join(tmp, "target") outside1Dir = path.Join(tmp, "outside1") outside2Dir = path.Join(tmp, "outside2") outside1Path = path.Join(outside1Dir, "file.txt") outside2Path = path.Join(outside2Dir, "file.txt") outside1CheckPath = path.Join(targetDir, "a", "file.txt") outside2CheckPath = path.Join(sourceDir, "b", "file.txt") ) if err := os.MkdirAll(path.Join(sourceDir, "a"), 0o777); err != nil { t.Fatal(err) } if err := os.MkdirAll(path.Join(sourceDir, "b"), 0o777); err != nil { t.Fatal(err) } if err := os.Mkdir(targetDir, 0o777); err != nil { t.Fatal(err) } if err := os.Mkdir(outside1Dir, 0o777); err != nil { t.Fatal(err) } if err := os.Mkdir(outside2Dir, 0o777); err != nil { t.Fatal(err) } if err := os.WriteFile(outside1Path, []byte("hello"), 0o644); err != nil { t.Fatal(err) } if err := os.WriteFile(outside2Path, []byte("hello"), 0o644); err != nil { t.Fatal(err) } // mount the shared directory to a target if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { t.Fatal(err) } defer func() { if err := Unmount(targetDir); err != nil { t.Fatal(err) } }() // next, make the target private if err := MakePrivate(targetDir); err != nil { t.Fatal(err) } defer func() { if err := Unmount(targetDir); err != nil { t.Fatal(err) } }() // mount in an outside path to a mounted path inside the _source_ if err := Mount(outside1Dir, path.Join(sourceDir, "a"), "none", "bind,rw"); err != nil { t.Fatal(err) } defer func() { if err := Unmount(path.Join(sourceDir, "a")); err != nil { t.Fatal(err) } }() // check that this file _does_not_ show in the _target_ if _, err := os.Stat(outside1CheckPath); err != nil && !os.IsNotExist(err) { t.Fatal(err) } else if err == nil { t.Fatalf("%q should not be visible, but is", outside1CheckPath) } // next mount outside2Dir into the _target_ if err := Mount(outside2Dir, path.Join(targetDir, "b"), "none", "bind,rw"); err != nil { t.Fatal(err) } defer func() { if err := Unmount(path.Join(targetDir, "b")); err != nil { t.Fatal(err) } }() // check that this file _does_not_ show in the _source_ if _, err := os.Stat(outside2CheckPath); err != nil && !os.IsNotExist(err) { t.Fatal(err) } else if err == nil { t.Fatalf("%q should not be visible, but is", outside2CheckPath) } } // Testing that when a target is a shared mount, // then child mounts propagate to the source func TestSubtreeShared(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } var ( tmp = t.TempDir() sourceDir = path.Join(tmp, "source") targetDir = path.Join(tmp, "target") outsideDir = path.Join(tmp, "outside") outsidePath = path.Join(outsideDir, "file.txt") sourceCheckPath = path.Join(sourceDir, "a", "file.txt") ) if err := os.MkdirAll(path.Join(sourceDir, "a"), 0o777); err != nil { t.Fatal(err) } if err := os.Mkdir(targetDir, 0o777); err != nil { t.Fatal(err) } if err := os.Mkdir(outsideDir, 0o777); err != nil { t.Fatal(err) } if err := os.WriteFile(outsidePath, []byte("hello"), 0o644); err != nil { t.Fatal(err) } // mount the source as shared if err := MakeShared(sourceDir); err != nil { t.Fatal(err) } defer func() { if err := Unmount(sourceDir); err != nil { t.Fatal(err) } }() // mount the shared directory to a target if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { t.Fatal(err) } defer func() { if err := Unmount(targetDir); err != nil { t.Fatal(err) } }() // mount in an outside path to a mounted path inside the target if err := Mount(outsideDir, path.Join(targetDir, "a"), "none", "bind,rw"); err != nil { t.Fatal(err) } defer func() { if err := Unmount(path.Join(targetDir, "a")); err != nil { t.Fatal(err) } }() // NOW, check that the file from the outside directory is available in the source directory if _, err := os.Stat(sourceCheckPath); err != nil { t.Fatal(err) } } // testing that mounts to a shared source show up in the slave target, // and that mounts into a slave target do _not_ show up in the shared source func TestSubtreeSharedSlave(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } var ( tmp = t.TempDir() sourceDir = path.Join(tmp, "source") targetDir = path.Join(tmp, "target") outside1Dir = path.Join(tmp, "outside1") outside2Dir = path.Join(tmp, "outside2") outside1Path = path.Join(outside1Dir, "file.txt") outside2Path = path.Join(outside2Dir, "file.txt") outside1CheckPath = path.Join(targetDir, "a", "file.txt") outside2CheckPath = path.Join(sourceDir, "b", "file.txt") ) if err := os.MkdirAll(path.Join(sourceDir, "a"), 0o777); err != nil { t.Fatal(err) } if err := os.MkdirAll(path.Join(sourceDir, "b"), 0o777); err != nil { t.Fatal(err) } if err := os.Mkdir(targetDir, 0o777); err != nil { t.Fatal(err) } if err := os.Mkdir(outside1Dir, 0o777); err != nil { t.Fatal(err) } if err := os.Mkdir(outside2Dir, 0o777); err != nil { t.Fatal(err) } if err := os.WriteFile(outside1Path, []byte("hello"), 0o644); err != nil { t.Fatal(err) } if err := os.WriteFile(outside2Path, []byte("hello"), 0o644); err != nil { t.Fatal(err) } // mount the source as shared if err := MakeShared(sourceDir); err != nil { t.Fatal(err) } defer func() { if err := Unmount(sourceDir); err != nil { t.Fatal(err) } }() // mount the shared directory to a target if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { t.Fatal(err) } defer func() { if err := Unmount(targetDir); err != nil { t.Fatal(err) } }() // next, make the target slave if err := MakeSlave(targetDir); err != nil { t.Fatal(err) } defer func() { if err := Unmount(targetDir); err != nil { t.Fatal(err) } }() // mount in an outside path to a mounted path inside the _source_ if err := Mount(outside1Dir, path.Join(sourceDir, "a"), "none", "bind,rw"); err != nil { t.Fatal(err) } defer func() { if err := Unmount(path.Join(sourceDir, "a")); err != nil { t.Fatal(err) } }() // check that this file _does_ show in the _target_ if _, err := os.Stat(outside1CheckPath); err != nil { t.Fatal(err) } // next mount outside2Dir into the _target_ if err := Mount(outside2Dir, path.Join(targetDir, "b"), "none", "bind,rw"); err != nil { t.Fatal(err) } defer func() { if err := Unmount(path.Join(targetDir, "b")); err != nil { t.Fatal(err) } }() // check that this file _does_not_ show in the _source_ if _, err := os.Stat(outside2CheckPath); err != nil && !os.IsNotExist(err) { t.Fatal(err) } else if err == nil { t.Fatalf("%q should not be visible, but is", outside2CheckPath) } } func TestSubtreeUnbindable(t *testing.T) { if os.Getuid() != 0 { t.Skip("root required") } var ( tmp = t.TempDir() sourceDir = path.Join(tmp, "source") targetDir = path.Join(tmp, "target") ) if err := os.MkdirAll(sourceDir, 0o777); err != nil { t.Fatal(err) } if err := os.MkdirAll(targetDir, 0o777); err != nil { t.Fatal(err) } // next, make the source unbindable if err := MakeUnbindable(sourceDir); err != nil { t.Fatal(err) } defer func() { if err := Unmount(sourceDir); err != nil { t.Fatal(err) } }() // then attempt to mount it to target. It should fail if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil && !errors.Is(err, unix.EINVAL) { t.Fatal(err) } else if err == nil { t.Fatalf("%q should not have been bindable", sourceDir) } defer func() { if err := Unmount(targetDir); err != nil { t.Fatal(err) } }() } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/000077500000000000000000000000001452213572400224225ustar00rootroot00000000000000golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/doc.go000066400000000000000000000052711452213572400235230ustar00rootroot00000000000000// Package mountinfo provides a set of functions to retrieve information about OS mounts. // // Currently it supports Linux. For historical reasons, there is also some support for FreeBSD and OpenBSD, // and a shallow implementation for Windows, but in general this is Linux-only package, so // the rest of the document only applies to Linux, unless explicitly specified otherwise. // // In Linux, information about mounts seen by the current process is available from // /proc/self/mountinfo. Note that due to mount namespaces, different processes can // see different mounts. A per-process mountinfo table is available from /proc//mountinfo, // where is a numerical process identifier. // // In general, /proc is not a very efficient interface, and mountinfo is not an exception. // For example, there is no way to get information about a specific mount point (i.e. it // is all-or-nothing). This package tries to hide the /proc ineffectiveness by using // parse filters while reading mountinfo. A filter can skip some entries, or stop // processing the rest of the file once the needed information is found. // // For mountinfo filters that accept path as an argument, the path must be absolute, // having all symlinks resolved, and being cleaned (i.e. no extra slashes or dots). // One way to achieve all of the above is to employ filepath.Abs followed by // filepath.EvalSymlinks (the latter calls filepath.Clean on the result so // there is no need to explicitly call filepath.Clean). // // NOTE that in many cases there is no need to consult mountinfo at all. Here are some // of the cases where mountinfo should not be parsed: // // 1. Before performing a mount. Usually, this is not needed, but if required (say to // prevent over-mounts), to check whether a directory is mounted, call os.Lstat // on it and its parent directory, and compare their st.Sys().(*syscall.Stat_t).Dev // fields -- if they differ, then the directory is the mount point. NOTE this does // not work for bind mounts. Optionally, the filesystem type can also be checked // by calling unix.Statfs and checking the Type field (i.e. filesystem type). // // 2. After performing a mount. If there is no error returned, the mount succeeded; // checking the mount table for a new mount is redundant and expensive. // // 3. Before performing an unmount. It is more efficient to do an unmount and ignore // a specific error (EINVAL) which tells the directory is not mounted. // // 4. After performing an unmount. If there is no error returned, the unmount succeeded. // // 5. To find the mount point root of a specific directory. You can perform os.Stat() // on the directory and traverse up until the Dev field of a parent directory differs. package mountinfo golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/go.mod000066400000000000000000000001531452213572400235270ustar00rootroot00000000000000module github.com/moby/sys/mountinfo go 1.16 require golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/go.sum000066400000000000000000000003171452213572400235560ustar00rootroot00000000000000golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mounted_linux.go000066400000000000000000000057311452213572400256510ustar00rootroot00000000000000package mountinfo import ( "os" "path/filepath" "golang.org/x/sys/unix" ) // MountedFast is a method of detecting a mount point without reading // mountinfo from procfs. A caller can only trust the result if no error // and sure == true are returned. Otherwise, other methods (e.g. parsing // /proc/mounts) have to be used. If unsure, use Mounted instead (which // uses MountedFast, but falls back to parsing mountinfo if needed). // // If a non-existent path is specified, an appropriate error is returned. // In case the caller is not interested in this particular error, it should // be handled separately using e.g. errors.Is(err, fs.ErrNotExist). // // This function is only available on Linux. When available (since kernel // v5.6), openat2(2) syscall is used to reliably detect all mounts. Otherwise, // the implementation falls back to using stat(2), which can reliably detect // normal (but not bind) mounts. func MountedFast(path string) (mounted, sure bool, err error) { // Root is always mounted. if path == string(os.PathSeparator) { return true, true, nil } path, err = normalizePath(path) if err != nil { return false, false, err } mounted, sure, err = mountedFast(path) return } // mountedByOpenat2 is a method of detecting a mount that works for all kinds // of mounts (incl. bind mounts), but requires a recent (v5.6+) linux kernel. func mountedByOpenat2(path string) (bool, error) { dir, last := filepath.Split(path) dirfd, err := unix.Openat2(unix.AT_FDCWD, dir, &unix.OpenHow{ Flags: unix.O_PATH | unix.O_CLOEXEC, }) if err != nil { return false, &os.PathError{Op: "openat2", Path: dir, Err: err} } fd, err := unix.Openat2(dirfd, last, &unix.OpenHow{ Flags: unix.O_PATH | unix.O_CLOEXEC | unix.O_NOFOLLOW, Resolve: unix.RESOLVE_NO_XDEV, }) _ = unix.Close(dirfd) switch err { //nolint:errorlint // unix errors are bare case nil: // definitely not a mount _ = unix.Close(fd) return false, nil case unix.EXDEV: // definitely a mount return true, nil } // not sure return false, &os.PathError{Op: "openat2", Path: path, Err: err} } // mountedFast is similar to MountedFast, except it expects a normalized path. func mountedFast(path string) (mounted, sure bool, err error) { // Root is always mounted. if path == string(os.PathSeparator) { return true, true, nil } // Try a fast path, using openat2() with RESOLVE_NO_XDEV. mounted, err = mountedByOpenat2(path) if err == nil { return mounted, true, nil } // Another fast path: compare st.st_dev fields. mounted, err = mountedByStat(path) // This does not work for bind mounts, so false negative // is possible, therefore only trust if return is true. if mounted && err == nil { return true, true, nil } return } func mounted(path string) (bool, error) { path, err := normalizePath(path) if err != nil { return false, err } mounted, sure, err := mountedFast(path) if sure && err == nil { return mounted, nil } // Fallback to parsing mountinfo. return mountedByMountinfo(path) } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mounted_linux_test.go000066400000000000000000000252621452213572400267110ustar00rootroot00000000000000package mountinfo import ( "errors" "net" "os" "path/filepath" "reflect" "runtime" "strings" "testing" "golang.org/x/sys/unix" ) // tMount is a wrapper for unix.Mount which is used to prepare test cases. // It skips the test case if mounting is not possible (i.e. user is not root), // adds more context to mount error, if any, and installs a cleanup handler to // undo the mount. func tMount(t *testing.T, src, dst, fstype string, flags uintptr, options string) error { if os.Getuid() != 0 { t.Skip("root required for mounting") } err := unix.Mount(src, dst, fstype, flags, options) if err != nil { return &os.PathError{Path: dst, Op: "mount", Err: err} } t.Cleanup(func() { if err := unix.Unmount(dst, unix.MNT_DETACH); err != nil { t.Errorf("cleanup: unmount %q failed: %v", dst, err) } }) return nil } type testMount struct { desc string isNotExist bool isMount bool isBind bool // prepare returns a path that needs to be checked, and the error, if any. // // It is responsible for cleanup (by using t.Cleanup). // // It should not fail the test (i.e. no calls to t.Error/t.Fatal). // The only exception to this rule is some cases use t.TempDir() for // simplicity (no need to check for errors or call t.Cleanup()), and // it may call t.Fatal, but practically we don't expect it. prepare func(t *testing.T) (string, error) } var testMounts = []testMount{ { desc: "non-existent path", isNotExist: true, prepare: func(t *testing.T) (string, error) { return "/non/existent/path", nil }, }, { desc: "not mounted directory", prepare: func(t *testing.T) (dir string, err error) { dir = t.TempDir() return dir, err }, }, { desc: "tmpfs mount", isMount: true, prepare: func(t *testing.T) (mnt string, err error) { mnt = t.TempDir() err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "") return mnt, err }, }, { desc: "tmpfs mount ending with a slash", isMount: true, prepare: func(t *testing.T) (mnt string, err error) { mnt = t.TempDir() + "/" err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "") return mnt, err }, }, { desc: "broken symlink", isNotExist: true, prepare: func(t *testing.T) (link string, err error) { dir := t.TempDir() link = filepath.Join(dir, "broken-symlink") err = os.Symlink("/some/non/existent/dest", link) return link, err }, }, { desc: "symlink to not mounted directory", prepare: func(t *testing.T) (link string, err error) { tmp := t.TempDir() dir, err := os.MkdirTemp(tmp, "dir") if err != nil { return } link = filepath.Join(tmp, "symlink") err = os.Symlink(dir, link) return link, err }, }, { desc: "symlink to mounted directory", isMount: true, prepare: func(t *testing.T) (link string, err error) { tmp := t.TempDir() dir, err := os.MkdirTemp(tmp, "dir") if err != nil { return } err = tMount(t, "tmpfs", dir, "tmpfs", 0, "") if err != nil { return } link = filepath.Join(tmp, "symlink") err = os.Symlink(dir, link) return link, err }, }, { desc: "symlink to a file on a different filesystem", isMount: false, prepare: func(t *testing.T) (link string, err error) { tmp := t.TempDir() mnt, err := os.MkdirTemp(tmp, "dir") if err != nil { return } err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "") if err != nil { return } file, err := os.CreateTemp(mnt, "file") if err != nil { return } file.Close() link = filepath.Join(tmp, "link") err = os.Symlink(file.Name(), link) return link, err }, }, { desc: "path whose parent is a symlink to directory on another device", isMount: false, prepare: func(t *testing.T) (path string, err error) { tmp := t.TempDir() mnt, err := os.MkdirTemp(tmp, "dir") if err != nil { return } err = tMount(t, "tmpfs", mnt, "tmpfs", 0, "") if err != nil { return } file, err := os.CreateTemp(mnt, "file") if err != nil { return } file.Close() // Create link -> mnt under tmp dir. link := filepath.Join(tmp, "link") err = os.Symlink(filepath.Base(mnt), link) // Path to check is //link/file. path = filepath.Join(link, filepath.Base(file.Name())) return path, err }, }, { desc: "directory bind mounted to itself", isMount: true, isBind: true, prepare: func(t *testing.T) (mnt string, err error) { mnt = t.TempDir() err = tMount(t, mnt, mnt, "", unix.MS_BIND, "") return mnt, err }, }, { desc: "directory bind-mounted to other directory", isMount: true, isBind: true, prepare: func(t *testing.T) (mnt string, err error) { dir := t.TempDir() mnt = t.TempDir() err = tMount(t, dir, mnt, "", unix.MS_BIND, "") return mnt, err }, }, { desc: "not mounted file", prepare: func(t *testing.T) (path string, err error) { dir := t.TempDir() file, err := os.CreateTemp(dir, "file") if err != nil { return } return file.Name(), err }, }, { desc: "regular file bind-mounted to itself", isMount: true, isBind: true, prepare: func(t *testing.T) (path string, err error) { dir := t.TempDir() file, err := os.CreateTemp(dir, "file") if err != nil { return } file.Close() path = file.Name() err = tMount(t, path, path, "", unix.MS_BIND, "") return path, err }, }, { desc: "not mounted socket", prepare: func(t *testing.T) (path string, err error) { dir := t.TempDir() path = filepath.Join(dir, "sock") _, err = net.Listen("unix", path) return path, err }, }, { desc: "socket bind-mounted to itself", isMount: true, isBind: true, prepare: func(t *testing.T) (path string, err error) { dir := t.TempDir() path = filepath.Join(dir, "sock") _, err = net.Listen("unix", path) if err != nil { return } err = tMount(t, path, path, "", unix.MS_BIND, "") return path, err }, }, } func requireOpenat2(t *testing.T) { t.Helper() if err := tryOpenat2(); err != nil { t.Skipf("openat2: %v (old kernel? need Linux 5.6+)", err) } } func tryOpenat2() error { fd, err := unix.Openat2(unix.AT_FDCWD, ".", &unix.OpenHow{Flags: unix.O_RDONLY}) if err == nil { _ = unix.Close(fd) } return err } func testMountedFast(t *testing.T, path string, tc *testMount, openat2Supported bool) { mounted, sure, err := MountedFast(path) if err != nil { // Got an error; is it expected? if !(tc.isNotExist && errors.Is(err, os.ErrNotExist)) { t.Errorf("MountedFast: unexpected error: %v", err) } // In case of an error, sure and mounted must be false. if sure { t.Error("MountedFast: expected sure to be false on error") } if mounted { t.Error("MountedFast: expected mounted to be false on error") } // No more checks. return } if openat2Supported { if mounted != tc.isMount { t.Errorf("MountedFast: expected mounted to be %v, got %v", tc.isMount, mounted) } // No more checks. return } if tc.isBind { // For bind mounts, in case openat2 is not supported, // sure and mounted must be false. if sure { t.Error("MountedFast: expected sure to be false for a bind mount") } if mounted { t.Error("MountedFast: expected mounted to be false for a bind mount") } } else { if mounted != tc.isMount { t.Errorf("MountFast: expected mounted to be %v, got %v", tc.isMount, mounted) } if tc.isMount && !sure { t.Error("MountFast: expected sure to be true for normal mount") } if !tc.isMount && sure { t.Error("MountFast: expected sure to be false for non-mount") } } } func TestMountedBy(t *testing.T) { checked := false openat2Supported := false // List of individual implementations to check. toCheck := []func(string) (bool, error){mountedByMountinfo, mountedByStat} if tryOpenat2() == nil { openat2Supported = true toCheck = append(toCheck, mountedByOpenat2) } for _, tc := range testMounts { tc := tc t.Run(tc.desc, func(t *testing.T) { m, err := tc.prepare(t) if err != nil { t.Fatalf("prepare: %v", err) } // Check the public Mounted() function as a whole. mounted, err := Mounted(m) if err == nil { if mounted != tc.isMount { t.Errorf("Mounted: expected %v, got %v", tc.isMount, mounted) } } else { // Got an error; is it expected? if !(tc.isNotExist && errors.Is(err, os.ErrNotExist)) { t.Errorf("Mounted: unexpected error: %v", err) } // Check false is returned in error case. if mounted { t.Error("Mounted: expected false on error") } } // Check the public MountedFast() function as a whole. testMountedFast(t, m, &tc, openat2Supported) // Check individual mountedBy* implementations. // All mountedBy* functions should be called with normalized paths. m, err = normalizePath(m) if err != nil { if tc.isNotExist && errors.Is(err, os.ErrNotExist) { return } t.Fatalf("normalizePath: %v", err) } for _, fn := range toCheck { // Figure out function name. name := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() mounted, err = fn(m) if err != nil { t.Errorf("%s: %v", name, err) // Check false is returned in error case. if mounted { t.Errorf("%s: expected false on error", name) } } else if mounted != tc.isMount { if tc.isBind && strings.HasSuffix(name, "mountedByStat") { // mountedByStat can not detect bind mounts. } else { t.Errorf("%s: expected %v, got %v", name, tc.isMount, mounted) } } checked = true } }) } if !checked { t.Skip("no mounts to check") } } func TestMountedByOpenat2VsMountinfo(t *testing.T) { requireOpenat2(t) mounts, err := GetMounts(nil) if err != nil { t.Fatalf("GetMounts error: %v", err) } for _, mount := range mounts { m := mount.Mountpoint if m == "/" { // mountedBy*() won't work for /, so skip it // (this special case is handled by Mounted). continue } mounted, err := mountedByOpenat2(m) if err != nil { if !errors.Is(err, os.ErrPermission) { t.Errorf("mountedByOpenat2(%q) error: %+v", m, err) } } else if !mounted { t.Errorf("mountedByOpenat2(%q): expected true, got false", m) } } } // TestMountedRoot checks that Mounted* functions always return true for root // directory (since / is always mounted). func TestMountedRoot(t *testing.T) { for _, path := range []string{ "/", "/../../", "/tmp/..", strings.Repeat("../", unix.PathMax/3), // Hope $CWD is not too deep down. } { mounted, err := Mounted(path) if err != nil || !mounted { t.Errorf("Mounted(%q): expected true, ; got %v, %v", path, mounted, err) } mounted, sure, err := MountedFast(path) if err != nil || !mounted || !sure { t.Errorf("MountedFast(%q): expected true, true, ; got %v, %v, %v", path, mounted, sure, err) } } } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mounted_unix.go000066400000000000000000000022541452213572400254720ustar00rootroot00000000000000//go:build linux || freebsd || openbsd || darwin // +build linux freebsd openbsd darwin package mountinfo import ( "os" "path/filepath" "golang.org/x/sys/unix" ) func mountedByStat(path string) (bool, error) { var st unix.Stat_t if err := unix.Lstat(path, &st); err != nil { return false, &os.PathError{Op: "stat", Path: path, Err: err} } dev := st.Dev parent := filepath.Dir(path) if err := unix.Lstat(parent, &st); err != nil { return false, &os.PathError{Op: "stat", Path: parent, Err: err} } if dev != st.Dev { // Device differs from that of parent, // so definitely a mount point. return true, nil } // NB: this does not detect bind mounts on Linux. return false, nil } func normalizePath(path string) (realPath string, err error) { if realPath, err = filepath.Abs(path); err != nil { return "", err } if realPath, err = filepath.EvalSymlinks(realPath); err != nil { return "", err } if _, err := os.Stat(realPath); err != nil { return "", err } return realPath, nil } func mountedByMountinfo(path string) (bool, error) { entries, err := GetMounts(SingleEntryFilter(path)) if err != nil { return false, err } return len(entries) > 0, nil } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo.go000066400000000000000000000041111452213572400247640ustar00rootroot00000000000000package mountinfo import ( "os" ) // GetMounts retrieves a list of mounts for the current running process, // with an optional filter applied (use nil for no filter). func GetMounts(f FilterFunc) ([]*Info, error) { return parseMountTable(f) } // Mounted determines if a specified path is a mount point. In case of any // error, false (and an error) is returned. // // If a non-existent path is specified, an appropriate error is returned. // In case the caller is not interested in this particular error, it should // be handled separately using e.g. errors.Is(err, fs.ErrNotExist). func Mounted(path string) (bool, error) { // root is always mounted if path == string(os.PathSeparator) { return true, nil } return mounted(path) } // Info reveals information about a particular mounted filesystem. This // struct is populated from the content in the /proc//mountinfo file. type Info struct { // ID is a unique identifier of the mount (may be reused after umount). ID int // Parent is the ID of the parent mount (or of self for the root // of this mount namespace's mount tree). Parent int // Major and Minor are the major and the minor components of the Dev // field of unix.Stat_t structure returned by unix.*Stat calls for // files on this filesystem. Major, Minor int // Root is the pathname of the directory in the filesystem which forms // the root of this mount. Root string // Mountpoint is the pathname of the mount point relative to the // process's root directory. Mountpoint string // Options is a comma-separated list of mount options. Options string // Optional are zero or more fields of the form "tag[:value]", // separated by a space. Currently, the possible optional fields are // "shared", "master", "propagate_from", and "unbindable". For more // information, see mount_namespaces(7) Linux man page. Optional string // FSType is the filesystem type in the form "type[.subtype]". FSType string // Source is filesystem-specific information, or "none". Source string // VFSOptions is a comma-separated list of superblock options. VFSOptions string } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo_bsd.go000066400000000000000000000022021452213572400256130ustar00rootroot00000000000000//go:build freebsd || openbsd || darwin // +build freebsd openbsd darwin package mountinfo import "golang.org/x/sys/unix" // parseMountTable returns information about mounted filesystems func parseMountTable(filter FilterFunc) ([]*Info, error) { count, err := unix.Getfsstat(nil, unix.MNT_WAIT) if err != nil { return nil, err } entries := make([]unix.Statfs_t, count) _, err = unix.Getfsstat(entries, unix.MNT_WAIT) if err != nil { return nil, err } var out []*Info for _, entry := range entries { var skip, stop bool mountinfo := getMountinfo(&entry) if filter != nil { // filter out entries we're not interested in skip, stop = filter(mountinfo) if skip { continue } } out = append(out, mountinfo) if stop { break } } return out, nil } func mounted(path string) (bool, error) { path, err := normalizePath(path) if err != nil { return false, err } // Fast path: compare st.st_dev fields. // This should always work for FreeBSD and OpenBSD. mounted, err := mountedByStat(path) if err == nil { return mounted, nil } // Fallback to parsing mountinfo return mountedByMountinfo(path) } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo_filters.go000066400000000000000000000040561452213572400265240ustar00rootroot00000000000000package mountinfo import "strings" // FilterFunc is a type defining a callback function for GetMount(), // used to filter out mountinfo entries we're not interested in, // and/or stop further processing if we found what we wanted. // // It takes a pointer to the Info struct (fully populated with all available // fields on the GOOS platform), and returns two booleans: // // skip: true if the entry should be skipped; // // stop: true if parsing should be stopped after the entry. type FilterFunc func(*Info) (skip, stop bool) // PrefixFilter discards all entries whose mount points do not start with, or // are equal to the path specified in prefix. The prefix path must be absolute, // have all symlinks resolved, and cleaned (i.e. no extra slashes or dots). // // PrefixFilter treats prefix as a path, not a partial prefix, which means that // given "/foo", "/foo/bar" and "/foobar" entries, PrefixFilter("/foo") returns // "/foo" and "/foo/bar", and discards "/foobar". func PrefixFilter(prefix string) FilterFunc { return func(m *Info) (bool, bool) { skip := !strings.HasPrefix(m.Mountpoint+"/", prefix+"/") return skip, false } } // SingleEntryFilter looks for a specific entry. func SingleEntryFilter(mp string) FilterFunc { return func(m *Info) (bool, bool) { if m.Mountpoint == mp { return false, true // don't skip, stop now } return true, false // skip, keep going } } // ParentsFilter returns all entries whose mount points // can be parents of a path specified, discarding others. // // For example, given /var/lib/docker/something, entries // like /var/lib/docker, /var and / are returned. func ParentsFilter(path string) FilterFunc { return func(m *Info) (bool, bool) { skip := !strings.HasPrefix(path, m.Mountpoint) return skip, false } } // FSTypeFilter returns all entries that match provided fstype(s). func FSTypeFilter(fstype ...string) FilterFunc { return func(m *Info) (bool, bool) { for _, t := range fstype { if m.FSType == t { return false, false // don't skip, keep going } } return true, false // skip, keep going } } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo_filters_test.go000066400000000000000000000016431452213572400275620ustar00rootroot00000000000000package mountinfo import "testing" func TestPrefixFilter(t *testing.T) { tests := []struct { prefix string mountPoint string shouldSkip bool }{ {prefix: "/a", mountPoint: "/a", shouldSkip: false}, {prefix: "/a", mountPoint: "/a/b", shouldSkip: false}, {prefix: "/a", mountPoint: "/aa", shouldSkip: true}, {prefix: "/a", mountPoint: "/aa/b", shouldSkip: true}, // invalid prefix: prefix path must be cleaned and have no trailing slash {prefix: "/a/", mountPoint: "/a", shouldSkip: true}, {prefix: "/a/", mountPoint: "/a/b", shouldSkip: true}, } for _, tc := range tests { filter := PrefixFilter(tc.prefix) skip, _ := filter(&Info{Mountpoint: tc.mountPoint}) if skip != tc.shouldSkip { if tc.shouldSkip { t.Errorf("prefix %q: expected %q to be skipped", tc.prefix, tc.mountPoint) } else { t.Errorf("prefix %q: expected %q not to be skipped", tc.prefix, tc.mountPoint) } } } } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo_freebsdlike.go000066400000000000000000000005371452213572400273330ustar00rootroot00000000000000//go:build freebsd || darwin // +build freebsd darwin package mountinfo import "golang.org/x/sys/unix" func getMountinfo(entry *unix.Statfs_t) *Info { return &Info{ Mountpoint: unix.ByteSliceToString(entry.Mntonname[:]), FSType: unix.ByteSliceToString(entry.Fstypename[:]), Source: unix.ByteSliceToString(entry.Mntfromname[:]), } } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo_linux.go000066400000000000000000000155151452213572400262150ustar00rootroot00000000000000package mountinfo import ( "bufio" "fmt" "io" "os" "runtime" "strconv" "strings" "sync" "golang.org/x/sys/unix" ) // GetMountsFromReader retrieves a list of mounts from the // reader provided, with an optional filter applied (use nil // for no filter). This can be useful in tests or benchmarks // that provide fake mountinfo data, or when a source other // than /proc/thread-self/mountinfo needs to be read from. // // This function is Linux-specific. func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) { s := bufio.NewScanner(r) out := []*Info{} for s.Scan() { var err error /* See http://man7.org/linux/man-pages/man5/proc.5.html 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) (1) mount ID: unique identifier of the mount (may be reused after umount) (2) parent ID: ID of parent (or of self for the top of the mount tree) (3) major:minor: value of st_dev for files on filesystem (4) root: root of the mount within the filesystem (5) mount point: mount point relative to the process's root (6) mount options: per mount options (7) optional fields: zero or more fields of the form "tag[:value]" (8) separator: marks the end of the optional fields (9) filesystem type: name of filesystem of the form "type[.subtype]" (10) mount source: filesystem specific information or "none" (11) super options: per super block options In other words, we have: * 6 mandatory fields (1)..(6) * 0 or more optional fields (7) * a separator field (8) * 3 mandatory fields (9)..(11) */ text := s.Text() fields := strings.Split(text, " ") numFields := len(fields) if numFields < 10 { // should be at least 10 fields return nil, fmt.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields) } // separator field sepIdx := numFields - 4 // In Linux <= 3.9 mounting a cifs with spaces in a share // name (like "//srv/My Docs") _may_ end up having a space // in the last field of mountinfo (like "unc=//serv/My Docs"). // Since kernel 3.10-rc1, cifs option "unc=" is ignored, // so spaces should not appear. // // Check for a separator, and work around the spaces bug for fields[sepIdx] != "-" { sepIdx-- if sepIdx == 5 { return nil, fmt.Errorf("parsing '%s' failed: missing - separator", text) } } p := &Info{} p.Mountpoint, err = unescape(fields[4]) if err != nil { return nil, fmt.Errorf("parsing '%s' failed: mount point: %w", fields[4], err) } p.FSType, err = unescape(fields[sepIdx+1]) if err != nil { return nil, fmt.Errorf("parsing '%s' failed: fstype: %w", fields[sepIdx+1], err) } p.Source, err = unescape(fields[sepIdx+2]) if err != nil { return nil, fmt.Errorf("parsing '%s' failed: source: %w", fields[sepIdx+2], err) } p.VFSOptions = fields[sepIdx+3] // ignore any numbers parsing errors, as there should not be any p.ID, _ = strconv.Atoi(fields[0]) p.Parent, _ = strconv.Atoi(fields[1]) mm := strings.SplitN(fields[2], ":", 3) if len(mm) != 2 { return nil, fmt.Errorf("parsing '%s' failed: unexpected major:minor pair %s", text, mm) } p.Major, _ = strconv.Atoi(mm[0]) p.Minor, _ = strconv.Atoi(mm[1]) p.Root, err = unescape(fields[3]) if err != nil { return nil, fmt.Errorf("parsing '%s' failed: root: %w", fields[3], err) } p.Options = fields[5] // zero or more optional fields p.Optional = strings.Join(fields[6:sepIdx], " ") // Run the filter after parsing all fields. var skip, stop bool if filter != nil { skip, stop = filter(p) if skip { continue } } out = append(out, p) if stop { break } } if err := s.Err(); err != nil { return nil, err } return out, nil } var ( haveProcThreadSelf bool haveProcThreadSelfOnce sync.Once ) func parseMountTable(filter FilterFunc) (_ []*Info, err error) { haveProcThreadSelfOnce.Do(func() { _, err := os.Stat("/proc/thread-self/mountinfo") haveProcThreadSelf = err == nil }) // We need to lock ourselves to the current OS thread in order to make sure // that the thread referenced by /proc/thread-self stays alive until we // finish parsing the file. runtime.LockOSThread() defer runtime.UnlockOSThread() var f *os.File if haveProcThreadSelf { f, err = os.Open("/proc/thread-self/mountinfo") } else { // On pre-3.17 kernels (such as CentOS 7), we don't have // /proc/thread-self/ so we need to manually construct // /proc/self/task// as a fallback. f, err = os.Open("/proc/self/task/" + strconv.Itoa(unix.Gettid()) + "/mountinfo") } if err != nil { return nil, err } defer f.Close() return GetMountsFromReader(f, filter) } // PidMountInfo retrieves the list of mounts from a given process' mount // namespace. Unless there is a need to get mounts from a mount namespace // different from that of a calling process, use GetMounts. // // This function is Linux-specific. // // Deprecated: this will be removed before v1; use GetMountsFromReader with // opened /proc//mountinfo as an argument instead. func PidMountInfo(pid int) ([]*Info, error) { f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) if err != nil { return nil, err } defer f.Close() return GetMountsFromReader(f, nil) } // A few specific characters in mountinfo path entries (root and mountpoint) // are escaped using a backslash followed by a character's ascii code in octal. // // space -- as \040 // tab (aka \t) -- as \011 // newline (aka \n) -- as \012 // backslash (aka \\) -- as \134 // // This function converts path from mountinfo back, i.e. it unescapes the above sequences. func unescape(path string) (string, error) { // try to avoid copying if strings.IndexByte(path, '\\') == -1 { return path, nil } // The following code is UTF-8 transparent as it only looks for some // specific characters (backslash and 0..7) with values < utf8.RuneSelf, // and everything else is passed through as is. buf := make([]byte, len(path)) bufLen := 0 for i := 0; i < len(path); i++ { if path[i] != '\\' { buf[bufLen] = path[i] bufLen++ continue } s := path[i:] if len(s) < 4 { // too short return "", fmt.Errorf("bad escape sequence %q: too short", s) } c := s[1] switch c { case '0', '1', '2', '3', '4', '5', '6', '7': v := c - '0' for j := 2; j < 4; j++ { // one digit already; two more if s[j] < '0' || s[j] > '7' { return "", fmt.Errorf("bad escape sequence %q: not a digit", s[:3]) } x := s[j] - '0' v = (v << 3) | x } if v > 255 { return "", fmt.Errorf("bad escape sequence %q: out of range" + s[:3]) } buf[bufLen] = v bufLen++ i += 3 continue default: return "", fmt.Errorf("bad escape sequence %q: not a digit" + s[:3]) } } return string(buf[:bufLen]), nil } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo_linux_test.go000066400000000000000000002106641452213572400272560ustar00rootroot00000000000000package mountinfo import ( "bytes" "testing" ) const ( fedoraMountinfo = `15 35 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw 16 35 0:14 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel 17 35 0:5 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=8056484k,nr_inodes=2014121,mode=755 18 16 0:15 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:7 - securityfs securityfs rw 19 16 0:13 / /sys/fs/selinux rw,relatime shared:8 - selinuxfs selinuxfs rw 20 17 0:16 / /dev/shm rw,nosuid,nodev shared:3 - tmpfs tmpfs rw,seclabel 21 17 0:10 / /dev/pts rw,nosuid,noexec,relatime shared:4 - devpts devpts rw,seclabel,gid=5,mode=620,ptmxmode=000 22 35 0:17 / /run rw,nosuid,nodev shared:21 - tmpfs tmpfs rw,seclabel,mode=755 23 16 0:18 / /sys/fs/cgroup rw,nosuid,nodev,noexec shared:9 - tmpfs tmpfs rw,seclabel,mode=755 24 23 0:19 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd 25 16 0:20 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:20 - pstore pstore rw 26 23 0:21 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,cpuset,clone_children 27 23 0:22 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:12 - cgroup cgroup rw,cpuacct,cpu,clone_children 28 23 0:23 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,memory,clone_children 29 23 0:24 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,devices,clone_children 30 23 0:25 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,freezer,clone_children 31 23 0:26 / /sys/fs/cgroup/net_cls rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,net_cls,clone_children 32 23 0:27 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,blkio,clone_children 33 23 0:28 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,perf_event,clone_children 34 23 0:29 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,hugetlb,clone_children 35 1 253:2 / / rw,relatime shared:1 - ext4 /dev/mapper/ssd-root--f20 rw,seclabel,data=ordered 36 15 0:30 / /proc/sys/fs/binfmt_misc rw,relatime shared:22 - autofs systemd-1 rw,fd=38,pgrp=1,timeout=300,minproto=5,maxproto=5,direct 37 17 0:12 / /dev/mqueue rw,relatime shared:23 - mqueue mqueue rw,seclabel 38 35 0:31 / /tmp rw shared:24 - tmpfs tmpfs rw,seclabel 39 17 0:32 / /dev/hugepages rw,relatime shared:25 - hugetlbfs hugetlbfs rw,seclabel 40 16 0:7 / /sys/kernel/debug rw,relatime shared:26 - debugfs debugfs rw 41 16 0:33 / /sys/kernel/config rw,relatime shared:27 - configfs configfs rw 42 35 0:34 / /var/lib/nfs/rpc_pipefs rw,relatime shared:28 - rpc_pipefs sunrpc rw 43 15 0:35 / /proc/fs/nfsd rw,relatime shared:29 - nfsd sunrpc rw 45 35 8:17 / /boot rw,relatime shared:30 - ext4 /dev/sdb1 rw,seclabel,data=ordered 46 35 253:4 / /home rw,relatime shared:31 - ext4 /dev/mapper/ssd-home rw,seclabel,data=ordered 47 35 253:5 / /var/lib/libvirt/images rw,noatime,nodiratime shared:32 - ext4 /dev/mapper/ssd-virt rw,seclabel,discard,data=ordered 48 35 253:12 / /mnt/old rw,relatime shared:33 - ext4 /dev/mapper/HelpDeskRHEL6-FedoraRoot rw,seclabel,data=ordered 121 22 0:36 / /run/user/1000/gvfs rw,nosuid,nodev,relatime shared:104 - fuse.gvfsd-fuse gvfsd-fuse rw,user_id=1000,group_id=1000 124 16 0:37 / /sys/fs/fuse/connections rw,relatime shared:107 - fusectl fusectl rw 165 38 253:3 / /tmp/mnt rw,relatime shared:147 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered 167 35 253:15 / /var/lib/docker/devicemapper/mnt/aae4076022f0e2b80a2afbf8fc6df450c52080191fcef7fb679a73e6f073e5c2 rw,relatime shared:149 - ext4 /dev/mapper/docker-253:2-425882-aae4076022f0e2b80a2afbf8fc6df450c52080191fcef7fb679a73e6f073e5c2 rw,seclabel,discard,stripe=16,data=ordered 171 35 253:16 / /var/lib/docker/devicemapper/mnt/c71be651f114db95180e472f7871b74fa597ee70a58ccc35cb87139ddea15373 rw,relatime shared:153 - ext4 /dev/mapper/docker-253:2-425882-c71be651f114db95180e472f7871b74fa597ee70a58ccc35cb87139ddea15373 rw,seclabel,discard,stripe=16,data=ordered 175 35 253:17 / /var/lib/docker/devicemapper/mnt/1bac6ab72862d2d5626560df6197cf12036b82e258c53d981fa29adce6f06c3c rw,relatime shared:157 - ext4 /dev/mapper/docker-253:2-425882-1bac6ab72862d2d5626560df6197cf12036b82e258c53d981fa29adce6f06c3c rw,seclabel,discard,stripe=16,data=ordered 179 35 253:18 / /var/lib/docker/devicemapper/mnt/d710a357d77158e80d5b2c55710ae07c94e76d34d21ee7bae65ce5418f739b09 rw,relatime shared:161 - ext4 /dev/mapper/docker-253:2-425882-d710a357d77158e80d5b2c55710ae07c94e76d34d21ee7bae65ce5418f739b09 rw,seclabel,discard,stripe=16,data=ordered 183 35 253:19 / /var/lib/docker/devicemapper/mnt/6479f52366114d5f518db6837254baab48fab39f2ac38d5099250e9a6ceae6c7 rw,relatime shared:165 - ext4 /dev/mapper/docker-253:2-425882-6479f52366114d5f518db6837254baab48fab39f2ac38d5099250e9a6ceae6c7 rw,seclabel,discard,stripe=16,data=ordered 187 35 253:20 / /var/lib/docker/devicemapper/mnt/8d9df91c4cca5aef49eeb2725292aab324646f723a7feab56be34c2ad08268e1 rw,relatime shared:169 - ext4 /dev/mapper/docker-253:2-425882-8d9df91c4cca5aef49eeb2725292aab324646f723a7feab56be34c2ad08268e1 rw,seclabel,discard,stripe=16,data=ordered 191 35 253:21 / /var/lib/docker/devicemapper/mnt/c8240b768603d32e920d365dc9d1dc2a6af46cd23e7ae819947f969e1b4ec661 rw,relatime shared:173 - ext4 /dev/mapper/docker-253:2-425882-c8240b768603d32e920d365dc9d1dc2a6af46cd23e7ae819947f969e1b4ec661 rw,seclabel,discard,stripe=16,data=ordered 195 35 253:22 / /var/lib/docker/devicemapper/mnt/2eb3a01278380bbf3ed12d86ac629eaa70a4351301ee307a5cabe7b5f3b1615f rw,relatime shared:177 - ext4 /dev/mapper/docker-253:2-425882-2eb3a01278380bbf3ed12d86ac629eaa70a4351301ee307a5cabe7b5f3b1615f rw,seclabel,discard,stripe=16,data=ordered 199 35 253:23 / /var/lib/docker/devicemapper/mnt/37a17fb7c9d9b80821235d5f2662879bd3483915f245f9b49cdaa0e38779b70b rw,relatime shared:181 - ext4 /dev/mapper/docker-253:2-425882-37a17fb7c9d9b80821235d5f2662879bd3483915f245f9b49cdaa0e38779b70b rw,seclabel,discard,stripe=16,data=ordered 203 35 253:24 / /var/lib/docker/devicemapper/mnt/aea459ae930bf1de913e2f29428fd80ee678a1e962d4080019d9f9774331ee2b rw,relatime shared:185 - ext4 /dev/mapper/docker-253:2-425882-aea459ae930bf1de913e2f29428fd80ee678a1e962d4080019d9f9774331ee2b rw,seclabel,discard,stripe=16,data=ordered 207 35 253:25 / /var/lib/docker/devicemapper/mnt/928ead0bc06c454bd9f269e8585aeae0a6bd697f46dc8754c2a91309bc810882 rw,relatime shared:189 - ext4 /dev/mapper/docker-253:2-425882-928ead0bc06c454bd9f269e8585aeae0a6bd697f46dc8754c2a91309bc810882 rw,seclabel,discard,stripe=16,data=ordered 211 35 253:26 / /var/lib/docker/devicemapper/mnt/0f284d18481d671644706e7a7244cbcf63d590d634cc882cb8721821929d0420 rw,relatime shared:193 - ext4 /dev/mapper/docker-253:2-425882-0f284d18481d671644706e7a7244cbcf63d590d634cc882cb8721821929d0420 rw,seclabel,discard,stripe=16,data=ordered 215 35 253:27 / /var/lib/docker/devicemapper/mnt/d9dd16722ab34c38db2733e23f69e8f4803ce59658250dd63e98adff95d04919 rw,relatime shared:197 - ext4 /dev/mapper/docker-253:2-425882-d9dd16722ab34c38db2733e23f69e8f4803ce59658250dd63e98adff95d04919 rw,seclabel,discard,stripe=16,data=ordered 219 35 253:28 / /var/lib/docker/devicemapper/mnt/bc4500479f18c2c08c21ad5282e5f826a016a386177d9874c2764751c031d634 rw,relatime shared:201 - ext4 /dev/mapper/docker-253:2-425882-bc4500479f18c2c08c21ad5282e5f826a016a386177d9874c2764751c031d634 rw,seclabel,discard,stripe=16,data=ordered 223 35 253:29 / /var/lib/docker/devicemapper/mnt/7770c8b24eb3d5cc159a065910076938910d307ab2f5d94e1dc3b24c06ee2c8a rw,relatime shared:205 - ext4 /dev/mapper/docker-253:2-425882-7770c8b24eb3d5cc159a065910076938910d307ab2f5d94e1dc3b24c06ee2c8a rw,seclabel,discard,stripe=16,data=ordered 227 35 253:30 / /var/lib/docker/devicemapper/mnt/c280cd3d0bf0aa36b478b292279671624cceafc1a67eaa920fa1082601297adf rw,relatime shared:209 - ext4 /dev/mapper/docker-253:2-425882-c280cd3d0bf0aa36b478b292279671624cceafc1a67eaa920fa1082601297adf rw,seclabel,discard,stripe=16,data=ordered 231 35 253:31 / /var/lib/docker/devicemapper/mnt/8b59a7d9340279f09fea67fd6ad89ddef711e9e7050eb647984f8b5ef006335f rw,relatime shared:213 - ext4 /dev/mapper/docker-253:2-425882-8b59a7d9340279f09fea67fd6ad89ddef711e9e7050eb647984f8b5ef006335f rw,seclabel,discard,stripe=16,data=ordered 235 35 253:32 / /var/lib/docker/devicemapper/mnt/1a28059f29eda821578b1bb27a60cc71f76f846a551abefabce6efd0146dce9f rw,relatime shared:217 - ext4 /dev/mapper/docker-253:2-425882-1a28059f29eda821578b1bb27a60cc71f76f846a551abefabce6efd0146dce9f rw,seclabel,discard,stripe=16,data=ordered 239 35 253:33 / /var/lib/docker/devicemapper/mnt/e9aa60c60128cad1 rw,relatime shared:221 - ext4 /dev/mapper/docker-253:2-425882-e9aa60c60128cad1 rw,seclabel,discard,stripe=16,data=ordered 243 35 253:34 / /var/lib/docker/devicemapper/mnt/5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d-init rw,relatime shared:225 - ext4 /dev/mapper/docker-253:2-425882-5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d-init rw,seclabel,discard,stripe=16,data=ordered 247 35 253:35 / /var/lib/docker/devicemapper/mnt/5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d rw,relatime shared:229 - ext4 /dev/mapper/docker-253:2-425882-5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d rw,seclabel,discard,stripe=16,data=ordered 31 21 0:23 / /DATA/foo_bla_bla rw,relatime - cifs //foo/BLA\040BLA\040BLA/ rw,sec=ntlm,cache=loose,unc=\\foo\BLA BLA BLA,username=my_login,domain=mydomain.com,uid=12345678,forceuid,gid=12345678,forcegid,addr=10.1.30.10,file_mode=0755,dir_mode=0755,nounix,rsize=61440,wsize=65536,actimeo=1` ubuntuMountinfo = `15 20 0:14 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw 16 20 0:3 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw 17 20 0:5 / /dev rw,relatime - devtmpfs udev rw,size=1015140k,nr_inodes=253785,mode=755 18 17 0:11 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000 19 20 0:15 / /run rw,nosuid,noexec,relatime - tmpfs tmpfs rw,size=205044k,mode=755 20 1 253:0 / / rw,relatime - ext4 /dev/disk/by-label/DOROOT rw,errors=remount-ro,data=ordered 21 15 0:16 / /sys/fs/cgroup rw,relatime - tmpfs none rw,size=4k,mode=755 22 15 0:17 / /sys/fs/fuse/connections rw,relatime - fusectl none rw 23 15 0:6 / /sys/kernel/debug rw,relatime - debugfs none rw 24 15 0:10 / /sys/kernel/security rw,relatime - securityfs none rw 25 19 0:18 / /run/lock rw,nosuid,nodev,noexec,relatime - tmpfs none rw,size=5120k 26 21 0:19 / /sys/fs/cgroup/cpuset rw,relatime - cgroup cgroup rw,cpuset,clone_children 27 19 0:20 / /run/shm rw,nosuid,nodev,relatime - tmpfs none rw 28 21 0:21 / /sys/fs/cgroup/cpu rw,relatime - cgroup cgroup rw,cpu 29 19 0:22 / /run/user rw,nosuid,nodev,noexec,relatime - tmpfs none rw,size=102400k,mode=755 30 15 0:23 / /sys/fs/pstore rw,relatime - pstore none rw 31 21 0:24 / /sys/fs/cgroup/cpuacct rw,relatime - cgroup cgroup rw,cpuacct 32 21 0:25 / /sys/fs/cgroup/memory rw,relatime - cgroup cgroup rw,memory 33 21 0:26 / /sys/fs/cgroup/devices rw,relatime - cgroup cgroup rw,devices 34 21 0:27 / /sys/fs/cgroup/freezer rw,relatime - cgroup cgroup rw,freezer 35 21 0:28 / /sys/fs/cgroup/blkio rw,relatime - cgroup cgroup rw,blkio 36 21 0:29 / /sys/fs/cgroup/perf_event rw,relatime - cgroup cgroup rw,perf_event 37 21 0:30 / /sys/fs/cgroup/hugetlb rw,relatime - cgroup cgroup rw,hugetlb 38 21 0:31 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime - cgroup systemd rw,name=systemd 39 20 0:32 / /var/lib/docker/aufs/mnt/b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc rw,relatime - aufs none rw,si=caafa54fdc06525 40 20 0:33 / /var/lib/docker/aufs/mnt/2eed44ac7ce7c75af04f088ed6cb4ce9d164801e91d78c6db65d7ef6d572bba8-init rw,relatime - aufs none rw,si=caafa54f882b525 41 20 0:34 / /var/lib/docker/aufs/mnt/2eed44ac7ce7c75af04f088ed6cb4ce9d164801e91d78c6db65d7ef6d572bba8 rw,relatime - aufs none rw,si=caafa54f8829525 42 20 0:35 / /var/lib/docker/aufs/mnt/16f4d7e96dd612903f425bfe856762f291ff2e36a8ecd55a2209b7d7cd81c30b rw,relatime - aufs none rw,si=caafa54f882d525 43 20 0:36 / /var/lib/docker/aufs/mnt/63ca08b75d7438a9469a5954e003f48ffede73541f6286ce1cb4d7dd4811da7e-init rw,relatime - aufs none rw,si=caafa54f882f525 44 20 0:37 / /var/lib/docker/aufs/mnt/63ca08b75d7438a9469a5954e003f48ffede73541f6286ce1cb4d7dd4811da7e rw,relatime - aufs none rw,si=caafa54f88ba525 45 20 0:38 / /var/lib/docker/aufs/mnt/283f35a910233c756409313be71ecd8fcfef0df57108b8d740b61b3e88860452 rw,relatime - aufs none rw,si=caafa54f88b8525 46 20 0:39 / /var/lib/docker/aufs/mnt/2c6c7253d4090faa3886871fb21bd660609daeb0206588c0602007f7d0f254b1-init rw,relatime - aufs none rw,si=caafa54f88be525 47 20 0:40 / /var/lib/docker/aufs/mnt/2c6c7253d4090faa3886871fb21bd660609daeb0206588c0602007f7d0f254b1 rw,relatime - aufs none rw,si=caafa54f882c525 48 20 0:41 / /var/lib/docker/aufs/mnt/de2b538c97d6366cc80e8658547c923ea1d042f85580df379846f36a4df7049d rw,relatime - aufs none rw,si=caafa54f85bb525 49 20 0:42 / /var/lib/docker/aufs/mnt/94a3d8ed7c27e5b0aa71eba46c736bfb2742afda038e74f2dd6035fb28415b49-init rw,relatime - aufs none rw,si=caafa54fdc00525 50 20 0:43 / /var/lib/docker/aufs/mnt/94a3d8ed7c27e5b0aa71eba46c736bfb2742afda038e74f2dd6035fb28415b49 rw,relatime - aufs none rw,si=caafa54fbaec525 51 20 0:44 / /var/lib/docker/aufs/mnt/6ac1cace985c9fc9bea32234de8b36dba49bdd5e29a2972b327ff939d78a6274 rw,relatime - aufs none rw,si=caafa54f8e1a525 52 20 0:45 / /var/lib/docker/aufs/mnt/dff147033e3a0ef061e1de1ad34256b523d4a8c1fa6bba71a0ab538e8628ff0b-init rw,relatime - aufs none rw,si=caafa54f8e1d525 53 20 0:46 / /var/lib/docker/aufs/mnt/dff147033e3a0ef061e1de1ad34256b523d4a8c1fa6bba71a0ab538e8628ff0b rw,relatime - aufs none rw,si=caafa54f8e1b525 54 20 0:47 / /var/lib/docker/aufs/mnt/cabb117d997f0f93519185aea58389a9762770b7496ed0b74a3e4a083fa45902 rw,relatime - aufs none rw,si=caafa54f810a525 55 20 0:48 / /var/lib/docker/aufs/mnt/e1c8a94ffaa9d532bbbdc6ef771ce8a6c2c06757806ecaf8b68e9108fec65f33-init rw,relatime - aufs none rw,si=caafa54f8529525 56 20 0:49 / /var/lib/docker/aufs/mnt/e1c8a94ffaa9d532bbbdc6ef771ce8a6c2c06757806ecaf8b68e9108fec65f33 rw,relatime - aufs none rw,si=caafa54f852f525 57 20 0:50 / /var/lib/docker/aufs/mnt/16a1526fa445b84ce84f89506d219e87fa488a814063baf045d88b02f21166b3 rw,relatime - aufs none rw,si=caafa54f9e1d525 58 20 0:51 / /var/lib/docker/aufs/mnt/57b9c92e1e368fa7dbe5079f7462e917777829caae732828b003c355fe49da9f-init rw,relatime - aufs none rw,si=caafa54f854d525 59 20 0:52 / /var/lib/docker/aufs/mnt/57b9c92e1e368fa7dbe5079f7462e917777829caae732828b003c355fe49da9f rw,relatime - aufs none rw,si=caafa54f854e525 60 20 0:53 / /var/lib/docker/aufs/mnt/e370c3e286bea027917baa0e4d251262681a472a87056e880dfd0513516dffd9 rw,relatime - aufs none rw,si=caafa54f840a525 61 20 0:54 / /var/lib/docker/aufs/mnt/6b00d3b4f32b41997ec07412b5e18204f82fbe643e7122251cdeb3582abd424e-init rw,relatime - aufs none rw,si=caafa54f8408525 62 20 0:55 / /var/lib/docker/aufs/mnt/6b00d3b4f32b41997ec07412b5e18204f82fbe643e7122251cdeb3582abd424e rw,relatime - aufs none rw,si=caafa54f8409525 63 20 0:56 / /var/lib/docker/aufs/mnt/abd0b5ea5d355a67f911475e271924a5388ee60c27185fcd60d095afc4a09dc7 rw,relatime - aufs none rw,si=caafa54f9eb1525 64 20 0:57 / /var/lib/docker/aufs/mnt/336222effc3f7b89867bb39ff7792ae5412c35c749f127c29159d046b6feedd2-init rw,relatime - aufs none rw,si=caafa54f85bf525 65 20 0:58 / /var/lib/docker/aufs/mnt/336222effc3f7b89867bb39ff7792ae5412c35c749f127c29159d046b6feedd2 rw,relatime - aufs none rw,si=caafa54f85b8525 66 20 0:59 / /var/lib/docker/aufs/mnt/912e1bf28b80a09644503924a8a1a4fb8ed10b808ca847bda27a369919aa52fa rw,relatime - aufs none rw,si=caafa54fbaea525 67 20 0:60 / /var/lib/docker/aufs/mnt/386f722875013b4a875118367abc783fc6617a3cb7cf08b2b4dcf550b4b9c576-init rw,relatime - aufs none rw,si=caafa54f8472525 68 20 0:61 / /var/lib/docker/aufs/mnt/386f722875013b4a875118367abc783fc6617a3cb7cf08b2b4dcf550b4b9c576 rw,relatime - aufs none rw,si=caafa54f8474525 69 20 0:62 / /var/lib/docker/aufs/mnt/5aaebb79ef3097dfca377889aeb61a0c9d5e3795117d2b08d0751473c671dfb2 rw,relatime - aufs none rw,si=caafa54f8c5e525 70 20 0:63 / /var/lib/docker/aufs/mnt/5ba3e493279d01277d583600b81c7c079e691b73c3a2bdea8e4b12a35a418be2-init rw,relatime - aufs none rw,si=caafa54f8c3b525 71 20 0:64 / /var/lib/docker/aufs/mnt/5ba3e493279d01277d583600b81c7c079e691b73c3a2bdea8e4b12a35a418be2 rw,relatime - aufs none rw,si=caafa54f8c3d525 72 20 0:65 / /var/lib/docker/aufs/mnt/2777f0763da4de93f8bebbe1595cc77f739806a158657b033eca06f827b6028a rw,relatime - aufs none rw,si=caafa54f8c3e525 73 20 0:66 / /var/lib/docker/aufs/mnt/5d7445562acf73c6f0ae34c3dd0921d7457de1ba92a587d9e06a44fa209eeb3e-init rw,relatime - aufs none rw,si=caafa54f8c39525 74 20 0:67 / /var/lib/docker/aufs/mnt/5d7445562acf73c6f0ae34c3dd0921d7457de1ba92a587d9e06a44fa209eeb3e rw,relatime - aufs none rw,si=caafa54f854f525 75 20 0:68 / /var/lib/docker/aufs/mnt/06400b526ec18b66639c96efc41a84f4ae0b117cb28dafd56be420651b4084a0 rw,relatime - aufs none rw,si=caafa54f840b525 76 20 0:69 / /var/lib/docker/aufs/mnt/e051d45ec42d8e3e1cc57bb39871a40de486dc123522e9c067fbf2ca6a357785-init rw,relatime - aufs none rw,si=caafa54fdddf525 77 20 0:70 / /var/lib/docker/aufs/mnt/e051d45ec42d8e3e1cc57bb39871a40de486dc123522e9c067fbf2ca6a357785 rw,relatime - aufs none rw,si=caafa54f854b525 78 20 0:71 / /var/lib/docker/aufs/mnt/1ff414fa93fd61ec81b0ab7b365a841ff6545accae03cceac702833aaeaf718f rw,relatime - aufs none rw,si=caafa54f8d85525 79 20 0:72 / /var/lib/docker/aufs/mnt/c661b2f871dd5360e46a2aebf8f970f6d39a2ff64e06979aa0361227c88128b8-init rw,relatime - aufs none rw,si=caafa54f8da3525 80 20 0:73 / /var/lib/docker/aufs/mnt/c661b2f871dd5360e46a2aebf8f970f6d39a2ff64e06979aa0361227c88128b8 rw,relatime - aufs none rw,si=caafa54f8da2525 81 20 0:74 / /var/lib/docker/aufs/mnt/b68b1d4fe4d30016c552398e78b379a39f651661d8e1fa5f2460c24a5e723420 rw,relatime - aufs none rw,si=caafa54f8d81525 82 20 0:75 / /var/lib/docker/aufs/mnt/c5c5979c936cd0153a4c626fa9d69ce4fce7d924cc74fa68b025d2f585031739-init rw,relatime - aufs none rw,si=caafa54f8da1525 83 20 0:76 / /var/lib/docker/aufs/mnt/c5c5979c936cd0153a4c626fa9d69ce4fce7d924cc74fa68b025d2f585031739 rw,relatime - aufs none rw,si=caafa54f8da0525 84 20 0:77 / /var/lib/docker/aufs/mnt/53e10b0329afc0e0d3322d31efaed4064139dc7027fe6ae445cffd7104bcc94f rw,relatime - aufs none rw,si=caafa54f8c35525 85 20 0:78 / /var/lib/docker/aufs/mnt/3bfafd09ff2603e2165efacc2215c1f51afabba6c42d04a68cc2df0e8cc31494-init rw,relatime - aufs none rw,si=caafa54f8db8525 86 20 0:79 / /var/lib/docker/aufs/mnt/3bfafd09ff2603e2165efacc2215c1f51afabba6c42d04a68cc2df0e8cc31494 rw,relatime - aufs none rw,si=caafa54f8dba525 87 20 0:80 / /var/lib/docker/aufs/mnt/90fdd2c03eeaf65311f88f4200e18aef6d2772482712d9aea01cd793c64781b5 rw,relatime - aufs none rw,si=caafa54f8315525 88 20 0:81 / /var/lib/docker/aufs/mnt/7bdf2591c06c154ceb23f5e74b1d03b18fbf6fe96e35fbf539b82d446922442f-init rw,relatime - aufs none rw,si=caafa54f8fc6525 89 20 0:82 / /var/lib/docker/aufs/mnt/7bdf2591c06c154ceb23f5e74b1d03b18fbf6fe96e35fbf539b82d446922442f rw,relatime - aufs none rw,si=caafa54f8468525 90 20 0:83 / /var/lib/docker/aufs/mnt/8cf9a993f50f3305abad3da268c0fc44ff78a1e7bba595ef9de963497496c3f9 rw,relatime - aufs none rw,si=caafa54f8c59525 91 20 0:84 / /var/lib/docker/aufs/mnt/ecc896fd74b21840a8d35e8316b92a08b1b9c83d722a12acff847e9f0ff17173-init rw,relatime - aufs none rw,si=caafa54f846a525 92 20 0:85 / /var/lib/docker/aufs/mnt/ecc896fd74b21840a8d35e8316b92a08b1b9c83d722a12acff847e9f0ff17173 rw,relatime - aufs none rw,si=caafa54f846b525 93 20 0:86 / /var/lib/docker/aufs/mnt/d8c8288ec920439a48b5796bab5883ee47a019240da65e8d8f33400c31bac5df rw,relatime - aufs none rw,si=caafa54f8dbf525 94 20 0:87 / /var/lib/docker/aufs/mnt/ecba66710bcd03199b9398e46c005cd6b68d0266ec81dc8b722a29cc417997c6-init rw,relatime - aufs none rw,si=caafa54f810f525 95 20 0:88 / /var/lib/docker/aufs/mnt/ecba66710bcd03199b9398e46c005cd6b68d0266ec81dc8b722a29cc417997c6 rw,relatime - aufs none rw,si=caafa54fbae9525 96 20 0:89 / /var/lib/docker/aufs/mnt/befc1c67600df449dddbe796c0d06da7caff1d2bbff64cde1f0ba82d224996b5 rw,relatime - aufs none rw,si=caafa54f8dab525 97 20 0:90 / /var/lib/docker/aufs/mnt/c9f470e73d2742629cdc4084a1b2c1a8302914f2aa0d0ec4542371df9a050562-init rw,relatime - aufs none rw,si=caafa54fdc02525 98 20 0:91 / /var/lib/docker/aufs/mnt/c9f470e73d2742629cdc4084a1b2c1a8302914f2aa0d0ec4542371df9a050562 rw,relatime - aufs none rw,si=caafa54f9eb0525 99 20 0:92 / /var/lib/docker/aufs/mnt/2a31f10029f04ff9d4381167a9b739609853d7220d55a56cb654779a700ee246 rw,relatime - aufs none rw,si=caafa54f8c37525 100 20 0:93 / /var/lib/docker/aufs/mnt/8c4261b8e3e4b21ebba60389bd64b6261217e7e6b9fd09e201d5a7f6760f6927-init rw,relatime - aufs none rw,si=caafa54fd173525 101 20 0:94 / /var/lib/docker/aufs/mnt/8c4261b8e3e4b21ebba60389bd64b6261217e7e6b9fd09e201d5a7f6760f6927 rw,relatime - aufs none rw,si=caafa54f8108525 102 20 0:95 / /var/lib/docker/aufs/mnt/eaa0f57403a3dc685268f91df3fbcd7a8423cee50e1a9ee5c3e1688d9d676bb4 rw,relatime - aufs none rw,si=caafa54f852d525 103 20 0:96 / /var/lib/docker/aufs/mnt/9cfe69a2cbffd9bfc7f396d4754f6fe5cc457ef417b277797be3762dfe955a6b-init rw,relatime - aufs none rw,si=caafa54f8d80525 104 20 0:97 / /var/lib/docker/aufs/mnt/9cfe69a2cbffd9bfc7f396d4754f6fe5cc457ef417b277797be3762dfe955a6b rw,relatime - aufs none rw,si=caafa54f8fc3525 105 20 0:98 / /var/lib/docker/aufs/mnt/d1b322ae17613c6adee84e709641a9244ac56675244a89a64dc0075075fcbb83 rw,relatime - aufs none rw,si=caafa54f8c58525 106 20 0:99 / /var/lib/docker/aufs/mnt/d46c2a8e9da7e91ab34fd9c192851c246a4e770a46720bda09e55c7554b9dbbd-init rw,relatime - aufs none rw,si=caafa54f8c63525 107 20 0:100 / /var/lib/docker/aufs/mnt/d46c2a8e9da7e91ab34fd9c192851c246a4e770a46720bda09e55c7554b9dbbd rw,relatime - aufs none rw,si=caafa54f8c67525 108 20 0:101 / /var/lib/docker/aufs/mnt/bc9d2a264158f83a617a069bf17cbbf2a2ba453db7d3951d9dc63cc1558b1c2b rw,relatime - aufs none rw,si=caafa54f8dbe525 109 20 0:102 / /var/lib/docker/aufs/mnt/9e6abb8d72bbeb4d5cf24b96018528015ba830ce42b4859965bd482cbd034e99-init rw,relatime - aufs none rw,si=caafa54f9e0d525 110 20 0:103 / /var/lib/docker/aufs/mnt/9e6abb8d72bbeb4d5cf24b96018528015ba830ce42b4859965bd482cbd034e99 rw,relatime - aufs none rw,si=caafa54f9e1b525 111 20 0:104 / /var/lib/docker/aufs/mnt/d4dca7b02569c732e740071e1c654d4ad282de5c41edb619af1f0aafa618be26 rw,relatime - aufs none rw,si=caafa54f8dae525 112 20 0:105 / /var/lib/docker/aufs/mnt/fea63da40fa1c5ffbad430dde0bc64a8fc2edab09a051fff55b673c40a08f6b7-init rw,relatime - aufs none rw,si=caafa54f8c5c525 113 20 0:106 / /var/lib/docker/aufs/mnt/fea63da40fa1c5ffbad430dde0bc64a8fc2edab09a051fff55b673c40a08f6b7 rw,relatime - aufs none rw,si=caafa54fd172525 114 20 0:107 / /var/lib/docker/aufs/mnt/e60c57499c0b198a6734f77f660cdbbd950a5b78aa23f470ca4f0cfcc376abef rw,relatime - aufs none rw,si=caafa54909c4525 115 20 0:108 / /var/lib/docker/aufs/mnt/099c78e7ccd9c8717471bb1bbfff838c0a9913321ba2f214fbeaf92c678e5b35-init rw,relatime - aufs none rw,si=caafa54909c3525 116 20 0:109 / /var/lib/docker/aufs/mnt/099c78e7ccd9c8717471bb1bbfff838c0a9913321ba2f214fbeaf92c678e5b35 rw,relatime - aufs none rw,si=caafa54909c7525 117 20 0:110 / /var/lib/docker/aufs/mnt/2997be666d58b9e71469759bcb8bd9608dad0e533a1a7570a896919ba3388825 rw,relatime - aufs none rw,si=caafa54f8557525 118 20 0:111 / /var/lib/docker/aufs/mnt/730694eff438ef20569df38dfb38a920969d7ff2170cc9aa7cb32a7ed8147a93-init rw,relatime - aufs none rw,si=caafa54c6e88525 119 20 0:112 / /var/lib/docker/aufs/mnt/730694eff438ef20569df38dfb38a920969d7ff2170cc9aa7cb32a7ed8147a93 rw,relatime - aufs none rw,si=caafa54c6e8e525 120 20 0:113 / /var/lib/docker/aufs/mnt/a672a1e2f2f051f6e19ed1dfbe80860a2d774174c49f7c476695f5dd1d5b2f67 rw,relatime - aufs none rw,si=caafa54c6e15525 121 20 0:114 / /var/lib/docker/aufs/mnt/aba3570e17859f76cf29d282d0d150659c6bd80780fdc52a465ba05245c2a420-init rw,relatime - aufs none rw,si=caafa54f8dad525 122 20 0:115 / /var/lib/docker/aufs/mnt/aba3570e17859f76cf29d282d0d150659c6bd80780fdc52a465ba05245c2a420 rw,relatime - aufs none rw,si=caafa54f8d84525 123 20 0:116 / /var/lib/docker/aufs/mnt/2abc86007aca46fb4a817a033e2a05ccacae40b78ea4b03f8ea616b9ada40e2e rw,relatime - aufs none rw,si=caafa54c6e8b525 124 20 0:117 / /var/lib/docker/aufs/mnt/36352f27f7878e648367a135bd1ec3ed497adcb8ac13577ee892a0bd921d2374-init rw,relatime - aufs none rw,si=caafa54c6e8d525 125 20 0:118 / /var/lib/docker/aufs/mnt/36352f27f7878e648367a135bd1ec3ed497adcb8ac13577ee892a0bd921d2374 rw,relatime - aufs none rw,si=caafa54f8c34525 126 20 0:119 / /var/lib/docker/aufs/mnt/2f95ca1a629cea8363b829faa727dd52896d5561f2c96ddee4f697ea2fc872c2 rw,relatime - aufs none rw,si=caafa54c6e8a525 127 20 0:120 / /var/lib/docker/aufs/mnt/f108c8291654f179ef143a3e07de2b5a34adbc0b28194a0ab17742b6db9a7fb2-init rw,relatime - aufs none rw,si=caafa54f8e19525 128 20 0:121 / /var/lib/docker/aufs/mnt/f108c8291654f179ef143a3e07de2b5a34adbc0b28194a0ab17742b6db9a7fb2 rw,relatime - aufs none rw,si=caafa54fa8c6525 129 20 0:122 / /var/lib/docker/aufs/mnt/c1d04dfdf8cccb3676d5a91e84e9b0781ce40623d127d038bcfbe4c761b27401 rw,relatime - aufs none rw,si=caafa54f8c30525 130 20 0:123 / /var/lib/docker/aufs/mnt/3f4898ffd0e1239aeebf1d1412590cdb7254207fa3883663e2c40cf772e5f05a-init rw,relatime - aufs none rw,si=caafa54c6e1a525 131 20 0:124 / /var/lib/docker/aufs/mnt/3f4898ffd0e1239aeebf1d1412590cdb7254207fa3883663e2c40cf772e5f05a rw,relatime - aufs none rw,si=caafa54c6e1c525 132 20 0:125 / /var/lib/docker/aufs/mnt/5ae3b6fccb1539fc02d420e86f3e9637bef5b711fed2ca31a2f426c8f5deddbf rw,relatime - aufs none rw,si=caafa54c4fea525 133 20 0:126 / /var/lib/docker/aufs/mnt/310bfaf80d57020f2e73b06aeffb0b9b0ca2f54895f88bf5e4d1529ccac58fe0-init rw,relatime - aufs none rw,si=caafa54c6e1e525 134 20 0:127 / /var/lib/docker/aufs/mnt/310bfaf80d57020f2e73b06aeffb0b9b0ca2f54895f88bf5e4d1529ccac58fe0 rw,relatime - aufs none rw,si=caafa54fa8c0525 135 20 0:128 / /var/lib/docker/aufs/mnt/f382bd5aaccaf2d04a59089ac7cb12ec87efd769fd0c14d623358fbfd2a3f896 rw,relatime - aufs none rw,si=caafa54c4fec525 136 20 0:129 / /var/lib/docker/aufs/mnt/50d45e9bb2d779bc6362824085564c7578c231af5ae3b3da116acf7e17d00735-init rw,relatime - aufs none rw,si=caafa54c4fef525 137 20 0:130 / /var/lib/docker/aufs/mnt/50d45e9bb2d779bc6362824085564c7578c231af5ae3b3da116acf7e17d00735 rw,relatime - aufs none rw,si=caafa54c4feb525 138 20 0:131 / /var/lib/docker/aufs/mnt/a9c5ee0854dc083b6bf62b7eb1e5291aefbb10702289a446471ce73aba0d5d7d rw,relatime - aufs none rw,si=caafa54909c6525 139 20 0:134 / /var/lib/docker/aufs/mnt/03a613e7bd5078819d1fd92df4e671c0127559a5e0b5a885cc8d5616875162f0-init rw,relatime - aufs none rw,si=caafa54804fe525 140 20 0:135 / /var/lib/docker/aufs/mnt/03a613e7bd5078819d1fd92df4e671c0127559a5e0b5a885cc8d5616875162f0 rw,relatime - aufs none rw,si=caafa54804fa525 141 20 0:136 / /var/lib/docker/aufs/mnt/7ec3277e5c04c907051caf9c9c35889f5fcd6463e5485971b25404566830bb70 rw,relatime - aufs none rw,si=caafa54804f9525 142 20 0:139 / /var/lib/docker/aufs/mnt/26b5b5d71d79a5b2bfcf8bc4b2280ee829f261eb886745dd90997ed410f7e8b8-init rw,relatime - aufs none rw,si=caafa54c6ef6525 143 20 0:140 / /var/lib/docker/aufs/mnt/26b5b5d71d79a5b2bfcf8bc4b2280ee829f261eb886745dd90997ed410f7e8b8 rw,relatime - aufs none rw,si=caafa54c6ef5525 144 20 0:356 / /var/lib/docker/aufs/mnt/e6ecde9e2c18cd3c75f424c67b6d89685cfee0fc67abf2cb6bdc0867eb998026 rw,relatime - aufs none rw,si=caafa548068e525` gentooMountinfo = `15 1 8:6 / / rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered 16 15 0:3 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw 17 15 0:14 / /run rw,nosuid,nodev,relatime - tmpfs tmpfs rw,size=3292172k,mode=755 18 15 0:5 / /dev rw,nosuid,relatime - devtmpfs udev rw,size=10240k,nr_inodes=4106451,mode=755 19 18 0:12 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw 20 18 0:10 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000 21 18 0:15 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw 22 15 0:16 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw 23 22 0:7 / /sys/kernel/debug rw,nosuid,nodev,noexec,relatime - debugfs debugfs rw 24 22 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs cgroup_root rw,size=10240k,mode=755 25 24 0:18 / /sys/fs/cgroup/openrc rw,nosuid,nodev,noexec,relatime - cgroup openrc rw,release_agent=/lib64/rc/sh/cgroup-release-agent.sh,name=openrc 26 24 0:19 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime - cgroup cpuset rw,cpuset,clone_children 27 24 0:20 / /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime - cgroup cpu rw,cpu,clone_children 28 24 0:21 / /sys/fs/cgroup/cpuacct rw,nosuid,nodev,noexec,relatime - cgroup cpuacct rw,cpuacct,clone_children 29 24 0:22 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup memory rw,memory,clone_children 30 24 0:23 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup devices rw,devices,clone_children 31 24 0:24 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup freezer rw,freezer,clone_children 32 24 0:25 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime - cgroup blkio rw,blkio,clone_children 33 15 8:1 / /boot rw,noatime,nodiratime - vfat /dev/sda1 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro 34 15 8:18 / /mnt/xfs rw,noatime,nodiratime - xfs /dev/sdb2 rw,attr2,inode64,noquota 35 15 0:26 / /tmp rw,relatime - tmpfs tmpfs rw 36 16 0:27 / /proc/sys/fs/binfmt_misc rw,nosuid,nodev,noexec,relatime - binfmt_misc binfmt_misc rw 42 15 0:33 / /var/lib/nfs/rpc_pipefs rw,relatime - rpc_pipefs rpc_pipefs rw 43 16 0:34 / /proc/fs/nfsd rw,nosuid,nodev,noexec,relatime - nfsd nfsd rw 44 15 0:35 / /home/tianon/.gvfs rw,nosuid,nodev,relatime - fuse.gvfs-fuse-daemon gvfs-fuse-daemon rw,user_id=1000,group_id=1000 68 15 0:3336 / /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd rw,relatime - aufs none rw,si=9b4a7640128db39c 86 68 8:6 /var/lib/docker/containers/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/config.env /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/.dockerenv rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered 87 68 8:6 /etc/resolv.conf /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/etc/resolv.conf rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered 88 68 8:6 /var/lib/docker/containers/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/hostname /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/etc/hostname rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered 89 68 8:6 /var/lib/docker/containers/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/hosts /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/etc/hosts rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered 38 15 0:3384 / /var/lib/docker/aufs/mnt/0292005a9292401bb5197657f2b682d97d8edcb3b72b5e390d2a680139985b55 rw,relatime - aufs none rw,si=9b4a7642b584939c 39 15 0:3385 / /var/lib/docker/aufs/mnt/59db98c889de5f71b70cfb82c40cbe47b64332f0f56042a2987a9e5df6e5e3aa rw,relatime - aufs none rw,si=9b4a7642b584e39c 40 15 0:3386 / /var/lib/docker/aufs/mnt/0545f0f2b6548eb9601d08f35a08f5a0a385407d36027a28f58e06e9f61e0278 rw,relatime - aufs none rw,si=9b4a7642b584b39c 41 15 0:3387 / /var/lib/docker/aufs/mnt/d882cfa16d1aa8fe0331a36e79be3d80b151e49f24fc39a39c3fed1735d5feb5 rw,relatime - aufs none rw,si=9b4a76453040039c 45 15 0:3388 / /var/lib/docker/aufs/mnt/055ca3befcb1626e74f5344b3398724ff05c0de0e20021683d04305c9e70a3f6 rw,relatime - aufs none rw,si=9b4a76453040739c 46 15 0:3389 / /var/lib/docker/aufs/mnt/b899e4567a351745d4285e7f1c18fdece75d877deb3041981cd290be348b7aa6 rw,relatime - aufs none rw,si=9b4a7647def4039c 47 15 0:3390 / /var/lib/docker/aufs/mnt/067ca040292c58954c5129f953219accfae0d40faca26b4d05e76ca76a998f16 rw,relatime - aufs none rw,si=9b4a7647def4239c 48 15 0:3391 / /var/lib/docker/aufs/mnt/8c995e7cb6e5082742daeea720e340b021d288d25d92e0412c03d200df308a11 rw,relatime - aufs none rw,si=9b4a764479c1639c 49 15 0:3392 / /var/lib/docker/aufs/mnt/07cc54dfae5b45300efdacdd53cc72c01b9044956a86ce7bff42d087e426096d rw,relatime - aufs none rw,si=9b4a764479c1739c 50 15 0:3393 / /var/lib/docker/aufs/mnt/0a9c95cf4c589c05b06baa79150b0cc1d8e7102759fe3ce4afaabb8247ca4f85 rw,relatime - aufs none rw,si=9b4a7644059c839c 51 15 0:3394 / /var/lib/docker/aufs/mnt/468fa98cececcf4e226e8370f18f4f848d63faf287fb8321a07f73086441a3a0 rw,relatime - aufs none rw,si=9b4a7644059ca39c 52 15 0:3395 / /var/lib/docker/aufs/mnt/0b826192231c5ce066fffb5beff4397337b5fc19a377aa7c6282c7c0ce7f111f rw,relatime - aufs none rw,si=9b4a764479c1339c 53 15 0:3396 / /var/lib/docker/aufs/mnt/93b8ba1b772fbe79709b909c43ea4b2c30d712e53548f467db1ffdc7a384f196 rw,relatime - aufs none rw,si=9b4a7640798a739c 54 15 0:3397 / /var/lib/docker/aufs/mnt/0c0d0acfb506859b12ef18cdfef9ebed0b43a611482403564224bde9149d373c rw,relatime - aufs none rw,si=9b4a7640798a039c 55 15 0:3398 / /var/lib/docker/aufs/mnt/33648c39ab6c7c74af0243d6d6a81b052e9e25ad1e04b19892eb2dde013e358b rw,relatime - aufs none rw,si=9b4a7644b439b39c 56 15 0:3399 / /var/lib/docker/aufs/mnt/0c12bea97a1c958a3c739fb148536c1c89351d48e885ecda8f0499b5cc44407e rw,relatime - aufs none rw,si=9b4a7640798a239c 57 15 0:3400 / /var/lib/docker/aufs/mnt/ed443988ce125f172d7512e84a4de2627405990fd767a16adefa8ce700c19ce8 rw,relatime - aufs none rw,si=9b4a7644c8ed339c 59 15 0:3402 / /var/lib/docker/aufs/mnt/f61612c324ff3c924d3f7a82fb00a0f8d8f73c248c41897061949e9f5ab7e3b1 rw,relatime - aufs none rw,si=9b4a76442810c39c 60 15 0:3403 / /var/lib/docker/aufs/mnt/0f1ee55c6c4e25027b80de8e64b8b6fb542b3b41aa0caab9261da75752e22bfd rw,relatime - aufs none rw,si=9b4a76442810e39c 61 15 0:3404 / /var/lib/docker/aufs/mnt/956f6cc4af5785cb3ee6963dcbca668219437d9b28f513290b1453ac64a34f97 rw,relatime - aufs none rw,si=9b4a7644303ec39c 62 15 0:3405 / /var/lib/docker/aufs/mnt/1099769158c4b4773e2569e38024e8717e400f87a002c41d8cf47cb81b051ba6 rw,relatime - aufs none rw,si=9b4a7644303ee39c 63 15 0:3406 / /var/lib/docker/aufs/mnt/11890ceb98d4442595b676085cd7b21550ab85c5df841e0fba997ff54e3d522d rw,relatime - aufs none rw,si=9b4a7644303ed39c 64 15 0:3407 / /var/lib/docker/aufs/mnt/acdb90dc378e8ed2420b43a6d291f1c789a081cd1904018780cc038fcd7aae53 rw,relatime - aufs none rw,si=9b4a76434be2139c 65 15 0:3408 / /var/lib/docker/aufs/mnt/120e716f19d4714fbe63cc1ed246204f2c1106eefebc6537ba2587d7e7711959 rw,relatime - aufs none rw,si=9b4a76434be2339c 66 15 0:3409 / /var/lib/docker/aufs/mnt/b197b7fffb61d89e0ba1c40de9a9fc0d912e778b3c1bd828cf981ff37c1963bc rw,relatime - aufs none rw,si=9b4a76434be2039c 70 15 0:3412 / /var/lib/docker/aufs/mnt/1434b69d2e1bb18a9f0b96b9cdac30132b2688f5d1379f68a39a5e120c2f93eb rw,relatime - aufs none rw,si=9b4a76434be2639c 71 15 0:3413 / /var/lib/docker/aufs/mnt/16006e83caf33ab5eb0cd6afc92ea2ee8edeff897496b0bb3ec3a75b767374b3 rw,relatime - aufs none rw,si=9b4a7644d790439c 72 15 0:3414 / /var/lib/docker/aufs/mnt/55bfa5f44e94d27f91f79ba901b118b15098449165c87abf1b53ffff147ff164 rw,relatime - aufs none rw,si=9b4a7644d790239c 73 15 0:3415 / /var/lib/docker/aufs/mnt/1912b97a07ab21ccd98a2a27bc779bf3cf364a3138afa3c3e6f7f169a3c3eab5 rw,relatime - aufs none rw,si=9b4a76441822739c 76 15 0:3418 / /var/lib/docker/aufs/mnt/1a7c3292e8879bd91ffd9282e954f643b1db5683093574c248ff14a9609f2f56 rw,relatime - aufs none rw,si=9b4a76438cb7239c 77 15 0:3419 / /var/lib/docker/aufs/mnt/bb1faaf0d076ddba82c2318305a85f490dafa4e8a8640a8db8ed657c439120cc rw,relatime - aufs none rw,si=9b4a76438cb7339c 78 15 0:3420 / /var/lib/docker/aufs/mnt/1ab869f21d2241a73ac840c7f988490313f909ac642eba71d092204fec66dd7c rw,relatime - aufs none rw,si=9b4a76438cb7639c 79 15 0:3421 / /var/lib/docker/aufs/mnt/fd7245b2cfe3890fa5f5b452260e4edf9e7fb7746532ed9d83f7a0d7dbaa610e rw,relatime - aufs none rw,si=9b4a7644bdc0139c 80 15 0:3422 / /var/lib/docker/aufs/mnt/1e5686c5301f26b9b3cd24e322c608913465cc6c5d0dcd7c5e498d1314747d61 rw,relatime - aufs none rw,si=9b4a7644bdc0639c 81 15 0:3423 / /var/lib/docker/aufs/mnt/52edf6ee6e40bfec1e9301a4d4a92ab83d144e2ae4ce5099e99df6138cb844bf rw,relatime - aufs none rw,si=9b4a7644bdc0239c 82 15 0:3424 / /var/lib/docker/aufs/mnt/1ea10fb7085d28cda4904657dff0454e52598d28e1d77e4f2965bbc3666e808f rw,relatime - aufs none rw,si=9b4a76438cb7139c 83 15 0:3425 / /var/lib/docker/aufs/mnt/9c03e98c3593946dbd4087f8d83f9ca262f4a2efdc952ce60690838b9ba6c526 rw,relatime - aufs none rw,si=9b4a76443020639c 84 15 0:3426 / /var/lib/docker/aufs/mnt/220a2344d67437602c6d2cee9a98c46be13f82c2a8063919dd2fad52bf2fb7dd rw,relatime - aufs none rw,si=9b4a76434bff339c 94 15 0:3427 / /var/lib/docker/aufs/mnt/3b32876c5b200312c50baa476ff342248e88c8ea96e6a1032cd53a88738a1cf2 rw,relatime - aufs none rw,si=9b4a76434bff139c 95 15 0:3428 / /var/lib/docker/aufs/mnt/23ee2b8b0d4ae8db6f6d1e168e2c6f79f8a18f953b09f65e0d22cc1e67a3a6fa rw,relatime - aufs none rw,si=9b4a7646c305c39c 96 15 0:3429 / /var/lib/docker/aufs/mnt/e86e6daa70b61b57945fa178222615f3c3d6bcef12c9f28e9f8623d44dc2d429 rw,relatime - aufs none rw,si=9b4a7646c305f39c 97 15 0:3430 / /var/lib/docker/aufs/mnt/2413d07623e80860bb2e9e306fbdee699afd07525785c025c591231e864aa162 rw,relatime - aufs none rw,si=9b4a76434bff039c 98 15 0:3431 / /var/lib/docker/aufs/mnt/adfd622eb22340fc80b429e5564b125668e260bf9068096c46dd59f1386a4b7d rw,relatime - aufs none rw,si=9b4a7646a7a1039c 102 15 0:3435 / /var/lib/docker/aufs/mnt/27cd92e7a91d02e2d6b44d16679a00fb6d169b19b88822891084e7fd1a84882d rw,relatime - aufs none rw,si=9b4a7646f25ec39c 103 15 0:3436 / /var/lib/docker/aufs/mnt/27dfdaf94cfbf45055c748293c37dd68d9140240bff4c646cb09216015914a88 rw,relatime - aufs none rw,si=9b4a7646732f939c 104 15 0:3437 / /var/lib/docker/aufs/mnt/5ed7524aff68dfbf0fc601cbaeac01bab14391850a973dabf3653282a627920f rw,relatime - aufs none rw,si=9b4a7646732f839c 105 15 0:3438 / /var/lib/docker/aufs/mnt/2a0d4767e536beb5785b60e071e3ac8e5e812613ab143a9627bee77d0c9ab062 rw,relatime - aufs none rw,si=9b4a7646732fe39c 106 15 0:3439 / /var/lib/docker/aufs/mnt/dea3fc045d9f4ae51ba952450b948a822cf85c39411489ca5224f6d9a8d02bad rw,relatime - aufs none rw,si=9b4a764012ad839c 107 15 0:3440 / /var/lib/docker/aufs/mnt/2d140a787160798da60cb67c21b1210054ad4dafecdcf832f015995b9aa99cfd rw,relatime - aufs none rw,si=9b4a764012add39c 108 15 0:3441 / /var/lib/docker/aufs/mnt/cb190b2a8e984475914430fbad2382e0d20b9b659f8ef83ae8d170cc672e519c rw,relatime - aufs none rw,si=9b4a76454d9c239c 109 15 0:3442 / /var/lib/docker/aufs/mnt/2f4a012d5a7ffd90256a6e9aa479054b3dddbc3c6a343f26dafbf3196890223b rw,relatime - aufs none rw,si=9b4a76454d9c439c 110 15 0:3443 / /var/lib/docker/aufs/mnt/63cc77904b80c4ffbf49cb974c5d8733dc52ad7640d3ae87554b325d7312d87f rw,relatime - aufs none rw,si=9b4a76454d9c339c 111 15 0:3444 / /var/lib/docker/aufs/mnt/30333e872c451482ea2d235ff2192e875bd234006b238ae2bdde3b91a86d7522 rw,relatime - aufs none rw,si=9b4a76422cebf39c 112 15 0:3445 / /var/lib/docker/aufs/mnt/6c54fc1125da3925cae65b5c9a98f3be55b0a2c2666082e5094a4ba71beb5bff rw,relatime - aufs none rw,si=9b4a7646dd5a439c 113 15 0:3446 / /var/lib/docker/aufs/mnt/3087d48cb01cda9d0a83a9ca301e6ea40e8593d18c4921be4794c91a420ab9a3 rw,relatime - aufs none rw,si=9b4a7646dd5a739c 114 15 0:3447 / /var/lib/docker/aufs/mnt/cc2607462a8f55b179a749b144c3fdbb50678e1a4f3065ea04e283e9b1f1d8e2 rw,relatime - aufs none rw,si=9b4a7646dd5a239c 117 15 0:3450 / /var/lib/docker/aufs/mnt/310c5e8392b29e8658a22e08d96d63936633b7e2c38e8d220047928b00a03d24 rw,relatime - aufs none rw,si=9b4a7647932d739c 118 15 0:3451 / /var/lib/docker/aufs/mnt/38a1f0029406ba9c3b6058f2f406d8a1d23c855046cf355c91d87d446fcc1460 rw,relatime - aufs none rw,si=9b4a76445abc939c 119 15 0:3452 / /var/lib/docker/aufs/mnt/42e109ab7914ae997a11ccd860fd18e4d488c50c044c3240423ce15774b8b62e rw,relatime - aufs none rw,si=9b4a76445abca39c 120 15 0:3453 / /var/lib/docker/aufs/mnt/365d832af0402d052b389c1e9c0d353b48487533d20cd4351df8e24ec4e4f9d8 rw,relatime - aufs none rw,si=9b4a7644066aa39c 121 15 0:3454 / /var/lib/docker/aufs/mnt/d3fa8a24d695b6cda9b64f96188f701963d28bef0473343f8b212df1a2cf1d2b rw,relatime - aufs none rw,si=9b4a7644066af39c 122 15 0:3455 / /var/lib/docker/aufs/mnt/37d4f491919abc49a15d0c7a7cc8383f087573525d7d288accd14f0b4af9eae0 rw,relatime - aufs none rw,si=9b4a7644066ad39c 123 15 0:3456 / /var/lib/docker/aufs/mnt/93902707fe12cbdd0068ce73f2baad4b3a299189b1b19cb5f8a2025e106ae3f5 rw,relatime - aufs none rw,si=9b4a76444445f39c 126 15 0:3459 / /var/lib/docker/aufs/mnt/3b49291670a625b9bbb329ffba99bf7fa7abff80cefef040f8b89e2b3aad4f9f rw,relatime - aufs none rw,si=9b4a7640798a339c 127 15 0:3460 / /var/lib/docker/aufs/mnt/8d9c7b943cc8f854f4d0d4ec19f7c16c13b0cc4f67a41472a072648610cecb59 rw,relatime - aufs none rw,si=9b4a76427383039c 128 15 0:3461 / /var/lib/docker/aufs/mnt/3b6c90036526c376307df71d49c9f5fce334c01b926faa6a78186842de74beac rw,relatime - aufs none rw,si=9b4a7644badd439c 130 15 0:3463 / /var/lib/docker/aufs/mnt/7b24158eeddfb5d31b7e932e406ea4899fd728344335ff8e0765e89ddeb351dd rw,relatime - aufs none rw,si=9b4a7644badd539c 131 15 0:3464 / /var/lib/docker/aufs/mnt/3ead6dd5773765c74850cf6c769f21fe65c29d622ffa712664f9f5b80364ce27 rw,relatime - aufs none rw,si=9b4a7642f469939c 132 15 0:3465 / /var/lib/docker/aufs/mnt/3f825573b29547744a37b65597a9d6d15a8350be4429b7038d126a4c9a8e178f rw,relatime - aufs none rw,si=9b4a7642f469c39c 133 15 0:3466 / /var/lib/docker/aufs/mnt/f67aaaeb3681e5dcb99a41f847087370bd1c206680cb8c7b6a9819fd6c97a331 rw,relatime - aufs none rw,si=9b4a7647cc25939c 134 15 0:3467 / /var/lib/docker/aufs/mnt/41afe6cfb3c1fc2280b869db07699da88552786e28793f0bc048a265c01bd942 rw,relatime - aufs none rw,si=9b4a7647cc25c39c 135 15 0:3468 / /var/lib/docker/aufs/mnt/b8092ea59da34a40b120e8718c3ae9fa8436996edc4fc50e4b99c72dfd81e1af rw,relatime - aufs none rw,si=9b4a76445abc439c 136 15 0:3469 / /var/lib/docker/aufs/mnt/42c69d2cc179e2684458bb8596a9da6dad182c08eae9b74d5f0e615b399f75a5 rw,relatime - aufs none rw,si=9b4a76455ddbe39c 137 15 0:3470 / /var/lib/docker/aufs/mnt/ea0871954acd2d62a211ac60e05969622044d4c74597870c4f818fbb0c56b09b rw,relatime - aufs none rw,si=9b4a76455ddbf39c 138 15 0:3471 / /var/lib/docker/aufs/mnt/4307906b275ab3fc971786b3841ae3217ac85b6756ddeb7ad4ba09cd044c2597 rw,relatime - aufs none rw,si=9b4a76455ddb839c 139 15 0:3472 / /var/lib/docker/aufs/mnt/4390b872928c53500a5035634f3421622ed6299dc1472b631fc45de9f56dc180 rw,relatime - aufs none rw,si=9b4a76402f2fd39c 140 15 0:3473 / /var/lib/docker/aufs/mnt/6bb41e78863b85e4aa7da89455314855c8c3bda64e52a583bab15dc1fa2e80c2 rw,relatime - aufs none rw,si=9b4a76402f2fa39c 141 15 0:3474 / /var/lib/docker/aufs/mnt/4444f583c2a79c66608f4673a32c9c812154f027045fbd558c2d69920c53f835 rw,relatime - aufs none rw,si=9b4a764479dbd39c 142 15 0:3475 / /var/lib/docker/aufs/mnt/6f11883af4a05ea362e0c54df89058da4859f977efd07b6f539e1f55c1d2a668 rw,relatime - aufs none rw,si=9b4a76402f30b39c 143 15 0:3476 / /var/lib/docker/aufs/mnt/453490dd32e7c2e9ef906f995d8fb3c2753923d1a5e0ba3fd3296e2e4dc238e7 rw,relatime - aufs none rw,si=9b4a76402f30c39c 144 15 0:3477 / /var/lib/docker/aufs/mnt/45e5945735ee102b5e891c91650c57ec4b52bb53017d68f02d50ea8a6e230610 rw,relatime - aufs none rw,si=9b4a76423260739c 147 15 0:3480 / /var/lib/docker/aufs/mnt/4727a64a5553a1125f315b96bed10d3073d6988225a292cce732617c925b56ab rw,relatime - aufs none rw,si=9b4a76443030339c 150 15 0:3483 / /var/lib/docker/aufs/mnt/4e348b5187b9a567059306afc72d42e0ec5c893b0d4abd547526d5f9b6fb4590 rw,relatime - aufs none rw,si=9b4a7644f5d8c39c 151 15 0:3484 / /var/lib/docker/aufs/mnt/4efc616bfbc3f906718b052da22e4335f8e9f91ee9b15866ed3a8029645189ef rw,relatime - aufs none rw,si=9b4a7644f5d8939c 152 15 0:3485 / /var/lib/docker/aufs/mnt/83e730ae9754d5adb853b64735472d98dfa17136b8812ac9cfcd1eba7f4e7d2d rw,relatime - aufs none rw,si=9b4a76469aa7139c 153 15 0:3486 / /var/lib/docker/aufs/mnt/4fc5ba8a5b333be2b7eefacccb626772eeec0ae8a6975112b56c9fb36c0d342f rw,relatime - aufs none rw,si=9b4a7640128dc39c 154 15 0:3487 / /var/lib/docker/aufs/mnt/50200d5edff5dfe8d1ef3c78b0bbd709793ac6e936aa16d74ff66f7ea577b6f9 rw,relatime - aufs none rw,si=9b4a7640128da39c 155 15 0:3488 / /var/lib/docker/aufs/mnt/51e5e51604361448f0b9777f38329f414bc5ba9cf238f26d465ff479bd574b61 rw,relatime - aufs none rw,si=9b4a76444f68939c 156 15 0:3489 / /var/lib/docker/aufs/mnt/52a142149aa98bba83df8766bbb1c629a97b9799944ead90dd206c4bdf0b8385 rw,relatime - aufs none rw,si=9b4a76444f68b39c 157 15 0:3490 / /var/lib/docker/aufs/mnt/52dd21a94a00f58a1ed489312fcfffb91578089c76c5650364476f1d5de031bc rw,relatime - aufs none rw,si=9b4a76444f68f39c 158 15 0:3491 / /var/lib/docker/aufs/mnt/ee562415ddaad353ed22c88d0ca768a0c74bfba6333b6e25c46849ee22d990da rw,relatime - aufs none rw,si=9b4a7640128d839c 159 15 0:3492 / /var/lib/docker/aufs/mnt/db47a9e87173f7554f550c8a01891de79cf12acdd32e01f95c1a527a08bdfb2c rw,relatime - aufs none rw,si=9b4a764405a1d39c 160 15 0:3493 / /var/lib/docker/aufs/mnt/55e827bf6d44d930ec0b827c98356eb8b68c3301e2d60d1429aa72e05b4c17df rw,relatime - aufs none rw,si=9b4a764405a1a39c 162 15 0:3495 / /var/lib/docker/aufs/mnt/578dc4e0a87fc37ec081ca098430499a59639c09f6f12a8f48de29828a091aa6 rw,relatime - aufs none rw,si=9b4a76406d7d439c 163 15 0:3496 / /var/lib/docker/aufs/mnt/728cc1cb04fa4bc6f7bf7a90980beda6d8fc0beb71630874c0747b994efb0798 rw,relatime - aufs none rw,si=9b4a76444f20e39c 164 15 0:3497 / /var/lib/docker/aufs/mnt/5850cc4bd9b55aea46c7ad598f1785117607974084ea643580f58ce3222e683a rw,relatime - aufs none rw,si=9b4a7644a824239c 165 15 0:3498 / /var/lib/docker/aufs/mnt/89443b3f766d5a37bc8b84e29da8b84e6a3ea8486d3cf154e2aae1816516e4a8 rw,relatime - aufs none rw,si=9b4a7644a824139c 166 15 0:3499 / /var/lib/docker/aufs/mnt/f5ae8fd5a41a337907d16515bc3162525154b59c32314c695ecd092c3b47943d rw,relatime - aufs none rw,si=9b4a7644a824439c 167 15 0:3500 / /var/lib/docker/aufs/mnt/5a430854f2a03a9e5f7cbc9f3fb46a8ebca526a5b3f435236d8295e5998798f5 rw,relatime - aufs none rw,si=9b4a7647fc82439c 168 15 0:3501 / /var/lib/docker/aufs/mnt/eda16901ae4cead35070c39845cbf1e10bd6b8cb0ffa7879ae2d8a186e460f91 rw,relatime - aufs none rw,si=9b4a76441e0df39c 169 15 0:3502 / /var/lib/docker/aufs/mnt/5a593721430c2a51b119ff86a7e06ea2b37e3b4131f8f1344d402b61b0c8d868 rw,relatime - aufs none rw,si=9b4a764248bad39c 170 15 0:3503 / /var/lib/docker/aufs/mnt/d662ad0a30fbfa902e0962108685b9330597e1ee2abb16dc9462eb5a67fdd23f rw,relatime - aufs none rw,si=9b4a764248bae39c 171 15 0:3504 / /var/lib/docker/aufs/mnt/5bc9de5c79812843fb36eee96bef1ddba812407861f572e33242f4ee10da2c15 rw,relatime - aufs none rw,si=9b4a764248ba839c 172 15 0:3505 / /var/lib/docker/aufs/mnt/5e763de8e9b0f7d58d2e12a341e029ab4efb3b99788b175090d8209e971156c1 rw,relatime - aufs none rw,si=9b4a764248baa39c 173 15 0:3506 / /var/lib/docker/aufs/mnt/b4431dc2739936f1df6387e337f5a0c99cf051900c896bd7fd46a870ce61c873 rw,relatime - aufs none rw,si=9b4a76401263539c 174 15 0:3507 / /var/lib/docker/aufs/mnt/5f37830e5a02561ab8c67ea3113137ba69f67a60e41c05cb0e7a0edaa1925b24 rw,relatime - aufs none rw,si=9b4a76401263639c 184 15 0:3508 / /var/lib/docker/aufs/mnt/62ea10b957e6533538a4633a1e1d678502f50ddcdd354b2ca275c54dd7a7793a rw,relatime - aufs none rw,si=9b4a76401263039c 187 15 0:3509 / /var/lib/docker/aufs/mnt/d56ee9d44195fe390e042fda75ec15af5132adb6d5c69468fa8792f4e54a6953 rw,relatime - aufs none rw,si=9b4a76401263239c 188 15 0:3510 / /var/lib/docker/aufs/mnt/6a300930673174549c2b62f36c933f0332a20735978c007c805a301f897146c5 rw,relatime - aufs none rw,si=9b4a76455d4c539c 189 15 0:3511 / /var/lib/docker/aufs/mnt/64496c45c84d348c24d410015456d101601c30cab4d1998c395591caf7e57a70 rw,relatime - aufs none rw,si=9b4a76455d4c639c 190 15 0:3512 / /var/lib/docker/aufs/mnt/65a6a645883fe97a7422cd5e71ebe0bc17c8e6302a5361edf52e89747387e908 rw,relatime - aufs none rw,si=9b4a76455d4c039c 191 15 0:3513 / /var/lib/docker/aufs/mnt/672be40695f7b6e13b0a3ed9fc996c73727dede3481f58155950fcfad57ed616 rw,relatime - aufs none rw,si=9b4a76455d4c239c 192 15 0:3514 / /var/lib/docker/aufs/mnt/d42438acb2bfb2169e1c0d8e917fc824f7c85d336dadb0b0af36dfe0f001b3ba rw,relatime - aufs none rw,si=9b4a7642bfded39c 193 15 0:3515 / /var/lib/docker/aufs/mnt/b48a54abf26d01cb2ddd908b1ed6034d17397c1341bf0eb2b251a3e5b79be854 rw,relatime - aufs none rw,si=9b4a7642bfdee39c 194 15 0:3516 / /var/lib/docker/aufs/mnt/76f27134491f052bfb87f59092126e53ef875d6851990e59195a9da16a9412f8 rw,relatime - aufs none rw,si=9b4a7642bfde839c 195 15 0:3517 / /var/lib/docker/aufs/mnt/6bd626a5462b4f8a8e1cc7d10351326dca97a59b2758e5ea549a4f6350ce8a90 rw,relatime - aufs none rw,si=9b4a7642bfdea39c 196 15 0:3518 / /var/lib/docker/aufs/mnt/f1fe3549dbd6f5ca615e9139d9b53f0c83a3b825565df37628eacc13e70cbd6d rw,relatime - aufs none rw,si=9b4a7642bfdf539c 197 15 0:3519 / /var/lib/docker/aufs/mnt/6d0458c8426a9e93d58d0625737e6122e725c9408488ed9e3e649a9984e15c34 rw,relatime - aufs none rw,si=9b4a7642bfdf639c 198 15 0:3520 / /var/lib/docker/aufs/mnt/6e4c97db83aa82145c9cf2bafc20d500c0b5389643b689e3ae84188c270a48c5 rw,relatime - aufs none rw,si=9b4a7642bfdf039c 199 15 0:3521 / /var/lib/docker/aufs/mnt/eb94d6498f2c5969eaa9fa11ac2934f1ab90ef88e2d002258dca08e5ba74ea27 rw,relatime - aufs none rw,si=9b4a7642bfdf239c 200 15 0:3522 / /var/lib/docker/aufs/mnt/fe3f88f0c511608a2eec5f13a98703aa16e55dbf930309723d8a37101f539fe1 rw,relatime - aufs none rw,si=9b4a7642bfc3539c 201 15 0:3523 / /var/lib/docker/aufs/mnt/6f40c229fb9cad85fabf4b64a2640a5403ec03fe5ac1a57d0609fb8b606b9c83 rw,relatime - aufs none rw,si=9b4a7642bfc3639c 202 15 0:3524 / /var/lib/docker/aufs/mnt/7513e9131f7a8acf58ff15248237feb767c78732ca46e159f4d791e6ef031dbc rw,relatime - aufs none rw,si=9b4a7642bfc3039c 203 15 0:3525 / /var/lib/docker/aufs/mnt/79f48b00aa713cdf809c6bb7c7cb911b66e9a8076c81d6c9d2504139984ea2da rw,relatime - aufs none rw,si=9b4a7642bfc3239c 204 15 0:3526 / /var/lib/docker/aufs/mnt/c3680418350d11358f0a96c676bc5aa74fa00a7c89e629ef5909d3557b060300 rw,relatime - aufs none rw,si=9b4a7642f47cd39c 205 15 0:3527 / /var/lib/docker/aufs/mnt/7a1744dd350d7fcc0cccb6f1757ca4cbe5453f203a5888b0f1014d96ad5a5ef9 rw,relatime - aufs none rw,si=9b4a7642f47ce39c 206 15 0:3528 / /var/lib/docker/aufs/mnt/7fa99662db046be9f03c33c35251afda9ccdc0085636bbba1d90592cec3ff68d rw,relatime - aufs none rw,si=9b4a7642f47c839c 207 15 0:3529 / /var/lib/docker/aufs/mnt/f815021ef20da9c9b056bd1d52d8aaf6e2c0c19f11122fc793eb2b04eb995e35 rw,relatime - aufs none rw,si=9b4a7642f47ca39c 208 15 0:3530 / /var/lib/docker/aufs/mnt/801086ae3110192d601dfcebdba2db92e86ce6b6a9dba6678ea04488e4513669 rw,relatime - aufs none rw,si=9b4a7642dc6dd39c 209 15 0:3531 / /var/lib/docker/aufs/mnt/822ba7db69f21daddda87c01cfbfbf73013fc03a879daf96d16cdde6f9b1fbd6 rw,relatime - aufs none rw,si=9b4a7642dc6de39c 210 15 0:3532 / /var/lib/docker/aufs/mnt/834227c1a950fef8cae3827489129d0dd220541e60c6b731caaa765bf2e6a199 rw,relatime - aufs none rw,si=9b4a7642dc6d839c 211 15 0:3533 / /var/lib/docker/aufs/mnt/83dccbc385299bd1c7cf19326e791b33a544eea7b4cdfb6db70ea94eed4389fb rw,relatime - aufs none rw,si=9b4a7642dc6da39c 212 15 0:3534 / /var/lib/docker/aufs/mnt/f1b8e6f0e7c8928b5dcdab944db89306ebcae3e0b32f9ff40d2daa8329f21600 rw,relatime - aufs none rw,si=9b4a7645a126039c 213 15 0:3535 / /var/lib/docker/aufs/mnt/970efb262c7a020c2404cbcc5b3259efba0d110a786079faeef05bc2952abf3a rw,relatime - aufs none rw,si=9b4a7644c8ed139c 214 15 0:3536 / /var/lib/docker/aufs/mnt/84b6d73af7450f3117a77e15a5ca1255871fea6182cd8e8a7be6bc744be18c2c rw,relatime - aufs none rw,si=9b4a76406559139c 215 15 0:3537 / /var/lib/docker/aufs/mnt/88be2716e026bc681b5e63fe7942068773efbd0b6e901ca7ba441412006a96b6 rw,relatime - aufs none rw,si=9b4a76406559339c 216 15 0:3538 / /var/lib/docker/aufs/mnt/c81939aa166ce50cd8bca5cfbbcc420a78e0318dd5cd7c755209b9166a00a752 rw,relatime - aufs none rw,si=9b4a76406559239c 217 15 0:3539 / /var/lib/docker/aufs/mnt/e0f241645d64b7dc5ff6a8414087cca226be08fb54ce987d1d1f6350c57083aa rw,relatime - aufs none rw,si=9b4a7647cfc0f39c 218 15 0:3540 / /var/lib/docker/aufs/mnt/e10e2bf75234ed51d8a6a4bb39e465404fecbe318e54400d3879cdb2b0679c78 rw,relatime - aufs none rw,si=9b4a7647cfc0939c 219 15 0:3541 / /var/lib/docker/aufs/mnt/8f71d74c8cfc3228b82564aa9f09b2e576cff0083ddfb6aa5cb350346063f080 rw,relatime - aufs none rw,si=9b4a7647cfc0a39c 220 15 0:3542 / /var/lib/docker/aufs/mnt/9159f1eba2aef7f5205cc18d015cda7f5933cd29bba3b1b8aed5ccb5824c69ee rw,relatime - aufs none rw,si=9b4a76468cedd39c 221 15 0:3543 / /var/lib/docker/aufs/mnt/932cad71e652e048e500d9fbb5b8ea4fc9a269d42a3134ce527ceef42a2be56b rw,relatime - aufs none rw,si=9b4a76468cede39c 222 15 0:3544 / /var/lib/docker/aufs/mnt/bf1e1b5f529e8943cc0144ee86dbaaa37885c1ddffcef29537e0078ee7dd316a rw,relatime - aufs none rw,si=9b4a76468ced839c 223 15 0:3545 / /var/lib/docker/aufs/mnt/949d93ecf3322e09f858ce81d5f4b434068ec44ff84c375de03104f7b45ee955 rw,relatime - aufs none rw,si=9b4a76468ceda39c 224 15 0:3546 / /var/lib/docker/aufs/mnt/d65c6087f92dc2a3841b5251d2fe9ca07d4c6e5b021597692479740816e4e2a1 rw,relatime - aufs none rw,si=9b4a7645a126239c 225 15 0:3547 / /var/lib/docker/aufs/mnt/98a0153119d0651c193d053d254f6e16a68345a141baa80c87ae487e9d33f290 rw,relatime - aufs none rw,si=9b4a7640787cf39c 226 15 0:3548 / /var/lib/docker/aufs/mnt/99daf7fe5847c017392f6e59aa9706b3dfdd9e6d1ba11dae0f7fffde0a60b5e5 rw,relatime - aufs none rw,si=9b4a7640787c839c 227 15 0:3549 / /var/lib/docker/aufs/mnt/9ad1f2fe8a5599d4e10c5a6effa7f03d932d4e92ee13149031a372087a359079 rw,relatime - aufs none rw,si=9b4a7640787ca39c 228 15 0:3550 / /var/lib/docker/aufs/mnt/c26d64494da782ddac26f8370d86ac93e7c1666d88a7b99110fc86b35ea6a85d rw,relatime - aufs none rw,si=9b4a7642fc6b539c 229 15 0:3551 / /var/lib/docker/aufs/mnt/a49e4a8275133c230ec640997f35f172312eb0ea5bd2bbe10abf34aae98f30eb rw,relatime - aufs none rw,si=9b4a7642fc6b639c 230 15 0:3552 / /var/lib/docker/aufs/mnt/b5e2740c867ed843025f49d84e8d769de9e8e6039b3c8cb0735b5bf358994bc7 rw,relatime - aufs none rw,si=9b4a7642fc6b039c 231 15 0:3553 / /var/lib/docker/aufs/mnt/a826fdcf3a7039b30570054579b65763db605a314275d7aef31b872c13311b4b rw,relatime - aufs none rw,si=9b4a7642fc6b239c 232 15 0:3554 / /var/lib/docker/aufs/mnt/addf3025babf5e43b5a3f4a0da7ad863dda3c01fb8365c58fd8d28bb61dc11bc rw,relatime - aufs none rw,si=9b4a76407871d39c 233 15 0:3555 / /var/lib/docker/aufs/mnt/c5b6c6813ab3e5ebdc6d22cb2a3d3106a62095f2c298be52b07a3b0fa20ff690 rw,relatime - aufs none rw,si=9b4a76407871e39c 234 15 0:3556 / /var/lib/docker/aufs/mnt/af0609eaaf64e2392060cb46f5a9f3d681a219bb4c651d4f015bf573fbe6c4cf rw,relatime - aufs none rw,si=9b4a76407871839c 235 15 0:3557 / /var/lib/docker/aufs/mnt/e7f20e3c37ecad39cd90a97cd3549466d0d106ce4f0a930b8495442634fa4a1f rw,relatime - aufs none rw,si=9b4a76407871a39c 237 15 0:3559 / /var/lib/docker/aufs/mnt/b57a53d440ffd0c1295804fa68cdde35d2fed5409484627e71b9c37e4249fd5c rw,relatime - aufs none rw,si=9b4a76444445a39c 238 15 0:3560 / /var/lib/docker/aufs/mnt/b5e7d7b8f35e47efbba3d80c5d722f5e7bd43e54c824e54b4a4b351714d36d42 rw,relatime - aufs none rw,si=9b4a7647932d439c 239 15 0:3561 / /var/lib/docker/aufs/mnt/f1b136def157e9465640658f277f3347de593c6ae76412a2e79f7002f091cae2 rw,relatime - aufs none rw,si=9b4a76445abcd39c 240 15 0:3562 / /var/lib/docker/aufs/mnt/b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc rw,relatime - aufs none rw,si=9b4a7644403b339c 241 15 0:3563 / /var/lib/docker/aufs/mnt/b89b140cdbc95063761864e0a23346207fa27ee4c5c63a1ae85c9069a9d9cf1d rw,relatime - aufs none rw,si=9b4a7644aa19739c 242 15 0:3564 / /var/lib/docker/aufs/mnt/bc6a69ed51c07f5228f6b4f161c892e6a949c0e7e86a9c3432049d4c0e5cd298 rw,relatime - aufs none rw,si=9b4a7644aa19139c 243 15 0:3565 / /var/lib/docker/aufs/mnt/be4e2ba3f136933e239f7cf3d136f484fb9004f1fbdfee24a62a2c7b0ab30670 rw,relatime - aufs none rw,si=9b4a7644aa19339c 244 15 0:3566 / /var/lib/docker/aufs/mnt/e04ca1a4a5171e30d20f0c92f90a50b8b6f8600af5459c4b4fb25e42e864dfe1 rw,relatime - aufs none rw,si=9b4a7647932d139c 245 15 0:3567 / /var/lib/docker/aufs/mnt/be61576b31db893129aaffcd3dcb5ce35e49c4b71b30c392a78609a45c7323d8 rw,relatime - aufs none rw,si=9b4a7642d85f739c 246 15 0:3568 / /var/lib/docker/aufs/mnt/dda42c191e56becf672327658ab84fcb563322db3764b91c2fefe4aaef04c624 rw,relatime - aufs none rw,si=9b4a7642d85f139c 247 15 0:3569 / /var/lib/docker/aufs/mnt/c0a7995053330f3d88969247a2e72b07e2dd692133f5668a4a35ea3905561072 rw,relatime - aufs none rw,si=9b4a7642d85f339c 249 15 0:3571 / /var/lib/docker/aufs/mnt/c3594b2e5f08c59ff5ed338a1ba1eceeeb1f7fc5d180068338110c00b1eb8502 rw,relatime - aufs none rw,si=9b4a7642738c739c 250 15 0:3572 / /var/lib/docker/aufs/mnt/c58dce03a0ab0a7588393880379dc3bce9f96ec08ed3f99cf1555260ff0031e8 rw,relatime - aufs none rw,si=9b4a7642738c139c 251 15 0:3573 / /var/lib/docker/aufs/mnt/c73e9f1d109c9d14cb36e1c7489df85649be3911116d76c2fd3648ec8fd94e23 rw,relatime - aufs none rw,si=9b4a7642738c339c 252 15 0:3574 / /var/lib/docker/aufs/mnt/c9eef28c344877cd68aa09e543c0710ab2b305a0ff96dbb859bfa7808c3e8d01 rw,relatime - aufs none rw,si=9b4a7642d85f439c 253 15 0:3575 / /var/lib/docker/aufs/mnt/feb67148f548d70cb7484f2aaad2a86051cd6867a561741a2f13b552457d666e rw,relatime - aufs none rw,si=9b4a76468c55739c 254 15 0:3576 / /var/lib/docker/aufs/mnt/cdf1f96c36d35a96041a896bf398ec0f7dc3b0fb0643612a0f4b6ff96e04e1bb rw,relatime - aufs none rw,si=9b4a76468c55139c 255 15 0:3577 / /var/lib/docker/aufs/mnt/ec6e505872353268451ac4bc034c1df00f3bae4a3ea2261c6e48f7bd5417c1b3 rw,relatime - aufs none rw,si=9b4a76468c55339c 256 15 0:3578 / /var/lib/docker/aufs/mnt/d6dc8aca64efd90e0bc10274001882d0efb310d42ccbf5712b99b169053b8b1a rw,relatime - aufs none rw,si=9b4a7642738c439c 257 15 0:3579 / /var/lib/docker/aufs/mnt/d712594e2ff6eaeb895bfd150d694bd1305fb927e7a186b2dab7df2ea95f8f81 rw,relatime - aufs none rw,si=9b4a76401268f39c 259 15 0:3581 / /var/lib/docker/aufs/mnt/dbfa1174cd78cde2d7410eae442af0b416c4a0e6f87ed4ff1e9f169a0029abc0 rw,relatime - aufs none rw,si=9b4a76401268b39c 260 15 0:3582 / /var/lib/docker/aufs/mnt/e883f5a82316d7856fbe93ee8c0af5a920b7079619dd95c4ffd88bbd309d28dd rw,relatime - aufs none rw,si=9b4a76468c55439c 261 15 0:3583 / /var/lib/docker/aufs/mnt/fdec3eff581c4fc2b09f87befa2fa021f3f2d373bea636a87f1fb5b367d6347a rw,relatime - aufs none rw,si=9b4a7644aa1af39c 262 15 0:3584 / /var/lib/docker/aufs/mnt/ef764e26712184653067ecf7afea18a80854c41331ca0f0ef03e1bacf90a6ffc rw,relatime - aufs none rw,si=9b4a7644aa1a939c 263 15 0:3585 / /var/lib/docker/aufs/mnt/f3176b40c41fce8ce6942936359a2001a6f1b5c1bb40ee224186db0789ec2f76 rw,relatime - aufs none rw,si=9b4a7644aa1ab39c 264 15 0:3586 / /var/lib/docker/aufs/mnt/f5daf06785d3565c6dd18ea7d953d9a8b9606107781e63270fe0514508736e6a rw,relatime - aufs none rw,si=9b4a76401268c39c 58 15 0:3587 / /var/lib/docker/aufs/mnt/cde8c40f6524b7361af4f5ad05bb857dc9ee247c20852ba666195c0739e3a2b8-init rw,relatime - aufs none rw,si=9b4a76444445839c 67 15 0:3588 / /var/lib/docker/aufs/mnt/cde8c40f6524b7361af4f5ad05bb857dc9ee247c20852ba666195c0739e3a2b8 rw,relatime - aufs none rw,si=9b4a7644badd339c 265 15 0:3610 / /var/lib/docker/aufs/mnt/e812472cd2c8c4748d1ef71fac4e77e50d661b9349abe66ce3e23511ed44f414 rw,relatime - aufs none rw,si=9b4a76427937d39c 270 15 0:3615 / /var/lib/docker/aufs/mnt/997636e7c5c9d0d1376a217e295c14c205350b62bc12052804fb5f90abe6f183 rw,relatime - aufs none rw,si=9b4a76406540739c 273 15 0:3618 / /var/lib/docker/aufs/mnt/d5794d080417b6e52e69227c3873e0e4c1ff0d5a845ebe3860ec2f89a47a2a1e rw,relatime - aufs none rw,si=9b4a76454814039c 278 15 0:3623 / /var/lib/docker/aufs/mnt/586bdd48baced671bb19bc4d294ec325f26c55545ae267db426424f157d59c48 rw,relatime - aufs none rw,si=9b4a7644b439f39c 281 15 0:3626 / /var/lib/docker/aufs/mnt/69739d022f89f8586908bbd5edbbdd95ea5256356f177f9ffcc6ef9c0ea752d2 rw,relatime - aufs none rw,si=9b4a7644a0f1b39c 286 15 0:3631 / /var/lib/docker/aufs/mnt/ff28c27d5f894363993622de26d5dd352dba072f219e4691d6498c19bbbc15a9 rw,relatime - aufs none rw,si=9b4a7642265b339c 289 15 0:3634 / /var/lib/docker/aufs/mnt/aa128fe0e64fdede333aa48fd9de39530c91a9244a0f0649a3c411c61e372daa rw,relatime - aufs none rw,si=9b4a764012ada39c 99 15 8:33 / /media/REMOVE\040ME rw,nosuid,nodev,relatime - fuseblk /dev/sdc1 rw,user_id=0,group_id=0,allow_other,blksize=4096` mountInfoWithSpaces = `486 28 252:1 / /mnt/foo\040bar rw,relatime shared:243 - ext4 /dev/vda1 rw,data=ordered 31 21 0:23 / /DATA/foo_bla_bla rw,relatime - cifs //foo/BLA\040BLA\040BLA/ rw,sec=ntlm,cache=loose,unc=\\foo\BLA BLA BLA,username=my_login,domain=mydomain.com,uid=12345678,forceuid,gid=12345678,forcegid,addr=10.1.30.10,file_mode=0755,dir_mode=0755,nounix,rsize=61440,wsize=65536,actimeo=1 649 94 259:5 /tmp/newline\012tab\011space\040backslash\134quote1'quote2" /tmp/newline\012tab\011space\040backslash\134quote1'quote2" rw,relatime shared:47 - ext4 /dev/nvme0n1p5 rw,seclabel` ) func TestParseMountInfo(t *testing.T) { testCases := []struct { name string info string expectedLength int mi *Info }{ { name: "fedora", info: fedoraMountinfo, expectedLength: 58, mi: &Info{ ID: 15, Parent: 35, Major: 0, Minor: 3, Root: "/", Mountpoint: "/proc", Options: "rw,nosuid,nodev,noexec,relatime", Optional: "shared:5", FSType: "proc", Source: "proc", VFSOptions: "rw", }, }, { name: "gentoo", info: gentooMountinfo, expectedLength: 222, mi: &Info{ ID: 15, Parent: 1, Major: 8, Minor: 6, Root: "/", Mountpoint: "/", Options: "rw,noatime,nodiratime", Optional: "", FSType: "ext4", Source: "/dev/sda6", VFSOptions: "rw,data=ordered", }, }, { name: "ubuntu", info: ubuntuMountinfo, expectedLength: 130, mi: &Info{ ID: 15, Parent: 20, Major: 0, Minor: 14, Root: "/", Mountpoint: "/sys", Options: "rw,nosuid,nodev,noexec,relatime", Optional: "", FSType: "sysfs", Source: "sysfs", VFSOptions: "rw", }, }, } for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { r := bytes.NewBuffer([]byte(tc.info)) infos, err := GetMountsFromReader(r, nil) if err != nil { t.Fatal(err) } if len(infos) != tc.expectedLength { t.Errorf("Expected %d entries, got %d", tc.expectedLength, len(infos)) } if tc.mi != nil && *infos[0] != *tc.mi { t.Fatalf("expected %#v, got %#v", tc.mi, infos[0]) } }) } } func TestParseFedoraMountinfoFilterFields(t *testing.T) { r := bytes.NewBuffer([]byte(fedoraMountinfo)) _, err := GetMountsFromReader(r, func(info *Info) (skip bool, stop bool) { mi := Info{ ID: 15, Parent: 35, Major: 0, Minor: 3, Root: "/", Mountpoint: "/proc", Options: "rw,nosuid,nodev,noexec,relatime", Optional: "shared:5", FSType: "proc", Source: "proc", VFSOptions: "rw", } if *info != mi { t.Fatalf("expected %#v, got %#v", mi, *info) } // Only match the first entry as in TestParseFedoraMountinfoFields. return false, true }) if err != nil { t.Fatal(err) } } func TestParseMountinfoWithSpaces(t *testing.T) { r := bytes.NewBuffer([]byte(mountInfoWithSpaces)) infos, err := GetMountsFromReader(r, nil) if err != nil { t.Fatal(err) } expected := []Info{ { ID: 486, Parent: 28, Major: 252, Minor: 1, Root: "/", Mountpoint: "/mnt/foo bar", Options: "rw,relatime", Optional: "shared:243", FSType: "ext4", Source: "/dev/vda1", VFSOptions: "rw,data=ordered", }, { ID: 31, Parent: 21, Major: 0, Minor: 23, Root: "/", Mountpoint: "/DATA/foo_bla_bla", Options: "rw,relatime", Optional: "", FSType: "cifs", Source: `//foo/BLA BLA BLA/`, VFSOptions: `rw,sec=ntlm,cache=loose,unc=\\foo\BLA`, }, { ID: 649, Parent: 94, Major: 259, Minor: 5, Root: `/tmp/newline tab space backslash\quote1'quote2"`, Mountpoint: `/tmp/newline tab space backslash\quote1'quote2"`, Options: "rw,relatime", Optional: "shared:47", FSType: "ext4", Source: `/dev/nvme0n1p5`, VFSOptions: `rw,seclabel`, }, } if len(infos) != len(expected) { t.Fatalf("expected %d entries, got %d", len(expected), len(infos)) } for i, mi := range expected { if *infos[i] != mi { t.Fatalf("expected %#v, got %#v", mi, infos[i]) } } } func TestParseMountinfoFilters(t *testing.T) { cases := []struct { filter FilterFunc expLen int }{ {SingleEntryFilter("/sys/fs/cgroup"), 1}, {SingleEntryFilter("nonexistent"), 0}, // 18 entries with /sys prefix in fedoraMountinfo {PrefixFilter("/sys"), 18}, {PrefixFilter("nonexistent"), 0}, // 4 entries: /sys/fs/cgroup/cpu,cpuacct /sys/fs/cgroup /sys / {ParentsFilter("/sys/fs/cgroup/cpu,cpuacct"), 4}, {FSTypeFilter("pstore"), 1}, {FSTypeFilter("proc", "sysfs"), 2}, } var r bytes.Reader for _, tc := range cases { r.Reset([]byte(fedoraMountinfo)) infos, err := GetMountsFromReader(&r, tc.filter) if err != nil { t.Error(err) continue } if len(infos) != tc.expLen { t.Errorf("unexpected len %d, expected %d", len(infos), tc.expLen) } } } func BenchmarkParseMountinfo(b *testing.B) { buf := []byte(fedoraMountinfo + "\n" + ubuntuMountinfo + "\n" + gentooMountinfo) b.ResetTimer() for i := 0; i < b.N; i++ { r := bytes.NewReader(buf) _, err := GetMountsFromReader(r, PrefixFilter("/sys")) if err != nil { b.Fatal(err) } } } func TestParseMountinfoExtraCases(t *testing.T) { testcases := []struct { name string entry string valid bool exp *Info }{ { name: "perfectly normal", entry: `251 15 0:3573 / /mnt/point rw,relatime - aufs none rw`, valid: true, }, { name: "not enough fields 1", entry: `251 15 0:3573 / /mnt/point rw,relatime aufs none rw`, valid: false, }, { name: "not enough fields 2", entry: `251 15 0:3573 / /mnt/point rw,relatime - aufs none`, valid: false, }, { name: "no separator 1", entry: `251 15 0:3573 / /mnt/point rw,relatime shared:1 aufs none rw`, valid: false, }, { name: "not enough post-separator fields", entry: `251 15 0:3573 / /mnt/point rw,relatime some extra fields - aufs none`, valid: false, }, { name: "extra fields at the end", // which we currently discard entry: `251 15 0:3573 / /mnt/point rw,relatime - aufs none rw,unc=buggy but we cope`, valid: true, exp: &Info{Mountpoint: "/mnt/point", FSType: "aufs", Source: "none"}, }, { name: "one optional field", entry: `251 15 0:3573 / /mnt/point rw,relatime shared:123 - aufs none rw`, valid: true, exp: &Info{Mountpoint: "/mnt/point", FSType: "aufs", Source: "none", Optional: "shared:123"}, }, { name: "extra optional fields", // which we carefully gather entry: `251 15 0:3573 / /mnt/point rw,relatime shared:123 extra:tag what:ever key:value - aufs none rw`, valid: true, exp: &Info{Mountpoint: "/mnt/point", FSType: "aufs", Source: "none", Optional: "shared:123 extra:tag what:ever key:value"}, }, { name: "empty source field (kernel < 5.1 bug)", entry: `279 23 0:108 / /tmp/bb rw,relatime - tmpfs rw`, valid: true, exp: &Info{Mountpoint: "/tmp/bb", FSType: "tmpfs", Source: "", VFSOptions: "rw"}, }, } for _, tc := range testcases { tc := tc t.Run(tc.name, func(t *testing.T) { r := bytes.NewBufferString(tc.entry) info, err := GetMountsFromReader(r, nil) if !tc.valid { if err == nil { t.Errorf("expected error, got nil") } return } if err != nil { t.Errorf("expected no error, got %v", err) return } if len(info) != 1 { t.Errorf("expected 1 result, got %d", len(info)) } if tc.exp == nil { return } i := info[0] if tc.exp.Mountpoint != "" && tc.exp.Mountpoint != i.Mountpoint { t.Errorf("expected mp %s, got %s", tc.exp.Mountpoint, i.Mountpoint) } if tc.exp.FSType != "" && tc.exp.FSType != i.FSType { t.Errorf("expected fs %s, got %s", tc.exp.FSType, i.FSType) } if tc.exp.Source != "" && tc.exp.Source != i.Source { t.Errorf("expected src %s, got %s", tc.exp.Source, i.Source) } if tc.exp.Optional != "" && tc.exp.Optional != i.Optional { t.Errorf("expected optional %s, got %s", tc.exp.Optional, i.Optional) } }) } } func TestUnescape(t *testing.T) { testCases := []struct { input, output string isErr bool }{ {"", "", false}, {"/", "/", false}, {"/some/longer/path", "/some/longer/path", false}, {"/path\\040with\\040spaces", "/path with spaces", false}, {"/path/with\\134backslash", "/path/with\\backslash", false}, {"/tab\\011in/path", "/tab\tin/path", false}, {`/path/"with'quotes`, `/path/"with'quotes`, false}, {`/path/"with'quotes,\040space,\011tab`, `/path/"with'quotes, space, tab`, false}, {`\12`, "", true}, {`\134`, `\`, false}, {`"'"'"'`, `"'"'"'`, false}, {`/\1345`, `/\5`, false}, {`/\12x`, "", true}, {`\0`, "", true}, {`\x`, "", true}, {"\\\\", "", true}, } for _, tc := range testCases { res, err := unescape(tc.input) if tc.isErr == true { if err == nil { t.Errorf("Input %q, want error, got nil", tc.input) } // no more checks continue } if res != tc.output { t.Errorf("Input %q, want %q, got %q", tc.input, tc.output, res) } if err != nil { t.Errorf("Input %q, want nil, got error %v", tc.input, err) continue } } } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo_openbsd.go000066400000000000000000000004561452213572400265060ustar00rootroot00000000000000package mountinfo import "golang.org/x/sys/unix" func getMountinfo(entry *unix.Statfs_t) *Info { return &Info{ Mountpoint: unix.ByteSliceToString(entry.F_mntonname[:]), FSType: unix.ByteSliceToString(entry.F_fstypename[:]), Source: unix.ByteSliceToString(entry.F_mntfromname[:]), } } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo_test.go000066400000000000000000000004361452213572400260310ustar00rootroot00000000000000//go:build !windows // +build !windows package mountinfo import "testing" func TestGetMounts(t *testing.T) { mounts, err := GetMounts(nil) if err != nil { t.Fatal(err) } if len(mounts) < 2 { t.Fatalf("should have at least two mounts, got %d: %+v", len(mounts), mounts) } } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo_unsupported.go000066400000000000000000000006521452213572400274420ustar00rootroot00000000000000//go:build !windows && !linux && !freebsd && !openbsd && !darwin // +build !windows,!linux,!freebsd,!openbsd,!darwin package mountinfo import ( "fmt" "runtime" ) var errNotImplemented = fmt.Errorf("not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) func parseMountTable(_ FilterFunc) ([]*Info, error) { return nil, errNotImplemented } func mounted(path string) (bool, error) { return false, errNotImplemented } golang-github-moby-sys-0.0~git20231105.a4e0878/mountinfo/mountinfo_windows.go000066400000000000000000000002651452213572400265440ustar00rootroot00000000000000package mountinfo func parseMountTable(_ FilterFunc) ([]*Info, error) { // Do NOT return an error! return nil, nil } func mounted(_ string) (bool, error) { return false, nil } golang-github-moby-sys-0.0~git20231105.a4e0878/sequential/000077500000000000000000000000001452213572400225565ustar00rootroot00000000000000golang-github-moby-sys-0.0~git20231105.a4e0878/sequential/doc.go000066400000000000000000000012601452213572400236510ustar00rootroot00000000000000// Package sequential provides a set of functions for managing sequential // files on Windows. // // The origin of these functions are the golang OS and windows packages, // slightly modified to only cope with files, not directories due to the // specific use case. // // The alteration is to allow a file on Windows to be opened with // FILE_FLAG_SEQUENTIAL_SCAN (particular for docker load), to avoid eating // the standby list, particularly when accessing large files such as layer.tar. // // For non-Windows platforms, the package provides wrappers for the equivalents // in the os packages. They are passthrough on Unix platforms, and only relevant // on Windows. package sequential golang-github-moby-sys-0.0~git20231105.a4e0878/sequential/go.mod000066400000000000000000000001541452213572400236640ustar00rootroot00000000000000module github.com/moby/sys/sequential go 1.17 require golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang-github-moby-sys-0.0~git20231105.a4e0878/sequential/go.sum000066400000000000000000000003171452213572400237120ustar00rootroot00000000000000golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang-github-moby-sys-0.0~git20231105.a4e0878/sequential/sequential_unix.go000066400000000000000000000012641452213572400263250ustar00rootroot00000000000000//go:build !windows // +build !windows package sequential import "os" // Create is an alias for [os.Create] on non-Windows platforms. func Create(name string) (*os.File, error) { return os.Create(name) } // Open is an alias for [os.Open] on non-Windows platforms. func Open(name string) (*os.File, error) { return os.Open(name) } // OpenFile is an alias for [os.OpenFile] on non-Windows platforms. func OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) { return os.OpenFile(name, flag, perm) } // CreateTemp is an alias for [os.CreateTemp] on non-Windows platforms. func CreateTemp(dir, prefix string) (f *os.File, err error) { return os.CreateTemp(dir, prefix) } golang-github-moby-sys-0.0~git20231105.a4e0878/sequential/sequential_windows.go000066400000000000000000000121321452213572400270300ustar00rootroot00000000000000package sequential import ( "os" "path/filepath" "strconv" "sync" "time" "unsafe" "golang.org/x/sys/windows" ) // Create is a copy of [os.Create], modified to use sequential file access. // // It uses [windows.FILE_FLAG_SEQUENTIAL_SCAN] rather than [windows.FILE_ATTRIBUTE_NORMAL] // as implemented in golang. Refer to the [Win32 API documentation] for details // on sequential file access. // // [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN func Create(name string) (*os.File, error) { return openFileSequential(name, windows.O_RDWR|windows.O_CREAT|windows.O_TRUNC) } // Open is a copy of [os.Open], modified to use sequential file access. // // It uses [windows.FILE_FLAG_SEQUENTIAL_SCAN] rather than [windows.FILE_ATTRIBUTE_NORMAL] // as implemented in golang. Refer to the [Win32 API documentation] for details // on sequential file access. // // [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN func Open(name string) (*os.File, error) { return openFileSequential(name, windows.O_RDONLY) } // OpenFile is a copy of [os.OpenFile], modified to use sequential file access. // // It uses [windows.FILE_FLAG_SEQUENTIAL_SCAN] rather than [windows.FILE_ATTRIBUTE_NORMAL] // as implemented in golang. Refer to the [Win32 API documentation] for details // on sequential file access. // // [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN func OpenFile(name string, flag int, _ os.FileMode) (*os.File, error) { return openFileSequential(name, flag) } func openFileSequential(name string, flag int) (file *os.File, err error) { if name == "" { return nil, &os.PathError{Op: "open", Path: name, Err: windows.ERROR_FILE_NOT_FOUND} } r, e := openSequential(name, flag|windows.O_CLOEXEC) if e != nil { return nil, &os.PathError{Op: "open", Path: name, Err: e} } return os.NewFile(uintptr(r), name), nil } func makeInheritSa() *windows.SecurityAttributes { var sa windows.SecurityAttributes sa.Length = uint32(unsafe.Sizeof(sa)) sa.InheritHandle = 1 return &sa } func openSequential(path string, mode int) (fd windows.Handle, err error) { if len(path) == 0 { return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND } pathp, err := windows.UTF16PtrFromString(path) if err != nil { return windows.InvalidHandle, err } var access uint32 switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) { case windows.O_RDONLY: access = windows.GENERIC_READ case windows.O_WRONLY: access = windows.GENERIC_WRITE case windows.O_RDWR: access = windows.GENERIC_READ | windows.GENERIC_WRITE } if mode&windows.O_CREAT != 0 { access |= windows.GENERIC_WRITE } if mode&windows.O_APPEND != 0 { access &^= windows.GENERIC_WRITE access |= windows.FILE_APPEND_DATA } sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE) var sa *windows.SecurityAttributes if mode&windows.O_CLOEXEC == 0 { sa = makeInheritSa() } var createmode uint32 switch { case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL): createmode = windows.CREATE_NEW case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC): createmode = windows.CREATE_ALWAYS case mode&windows.O_CREAT == windows.O_CREAT: createmode = windows.OPEN_ALWAYS case mode&windows.O_TRUNC == windows.O_TRUNC: createmode = windows.TRUNCATE_EXISTING default: createmode = windows.OPEN_EXISTING } // Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang. // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, windows.FILE_FLAG_SEQUENTIAL_SCAN, 0) return h, e } // Helpers for CreateTemp var rand uint32 var randmu sync.Mutex func reseed() uint32 { return uint32(time.Now().UnixNano() + int64(os.Getpid())) } func nextSuffix() string { randmu.Lock() r := rand if r == 0 { r = reseed() } r = r*1664525 + 1013904223 // constants from Numerical Recipes rand = r randmu.Unlock() return strconv.Itoa(int(1e9 + r%1e9))[1:] } // CreateTemp is a copy of [os.CreateTemp], modified to use sequential file access. // // It uses [windows.FILE_FLAG_SEQUENTIAL_SCAN] rather than [windows.FILE_ATTRIBUTE_NORMAL] // as implemented in golang. Refer to the [Win32 API documentation] for details // on sequential file access. // // [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN func CreateTemp(dir, prefix string) (f *os.File, err error) { if dir == "" { dir = os.TempDir() } nconflict := 0 for i := 0; i < 10000; i++ { name := filepath.Join(dir, prefix+nextSuffix()) f, err = openFileSequential(name, windows.O_RDWR|windows.O_CREAT|windows.O_EXCL) if os.IsExist(err) { if nconflict++; nconflict > 10 { randmu.Lock() rand = reseed() randmu.Unlock() } continue } break } return } golang-github-moby-sys-0.0~git20231105.a4e0878/signal/000077500000000000000000000000001452213572400216615ustar00rootroot00000000000000golang-github-moby-sys-0.0~git20231105.a4e0878/signal/go.mod000066400000000000000000000001501452213572400227630ustar00rootroot00000000000000module github.com/moby/sys/signal go 1.16 require golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang-github-moby-sys-0.0~git20231105.a4e0878/signal/go.sum000066400000000000000000000003171452213572400230150ustar00rootroot00000000000000golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang-github-moby-sys-0.0~git20231105.a4e0878/signal/signal.go000066400000000000000000000031161452213572400234660ustar00rootroot00000000000000// Package signal provides helper functions for dealing with signals across // various operating systems. package signal import ( "fmt" "os" "os/signal" "strconv" "strings" "syscall" ) // CatchAll catches all signals and relays them to the specified channel. // SIGURG is not handled, as it's used by the Go runtime to support // preemptable system calls. func CatchAll(sigc chan os.Signal) { var handledSigs []os.Signal for n, s := range SignalMap { if n == "URG" { // Do not handle SIGURG, as in go1.14+, the go runtime issues // SIGURG as an interrupt to support preemptable system calls on Linux. continue } handledSigs = append(handledSigs, s) } signal.Notify(sigc, handledSigs...) } // StopCatch stops catching the signals and closes the specified channel. func StopCatch(sigc chan os.Signal) { signal.Stop(sigc) close(sigc) } // ParseSignal translates a string to a valid syscall signal. // It returns an error if the signal map doesn't include the given signal. func ParseSignal(rawSignal string) (syscall.Signal, error) { s, err := strconv.Atoi(rawSignal) if err == nil { if s == 0 { return -1, fmt.Errorf("invalid signal: %s", rawSignal) } return syscall.Signal(s), nil } signal, ok := SignalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")] if !ok { return -1, fmt.Errorf("invalid signal: %s", rawSignal) } return signal, nil } // ValidSignalForPlatform returns true if a signal is valid on the platform func ValidSignalForPlatform(sig syscall.Signal) bool { for _, v := range SignalMap { if v == sig { return true } } return false } golang-github-moby-sys-0.0~git20231105.a4e0878/signal/signal_darwin.go000066400000000000000000000017651452213572400250420ustar00rootroot00000000000000package signal import ( "syscall" ) // SignalMap is a map of Darwin signals. var SignalMap = map[string]syscall.Signal{ "ABRT": syscall.SIGABRT, "ALRM": syscall.SIGALRM, "BUS": syscall.SIGBUS, "CHLD": syscall.SIGCHLD, "CONT": syscall.SIGCONT, "EMT": syscall.SIGEMT, "FPE": syscall.SIGFPE, "HUP": syscall.SIGHUP, "ILL": syscall.SIGILL, "INFO": syscall.SIGINFO, "INT": syscall.SIGINT, "IO": syscall.SIGIO, "IOT": syscall.SIGIOT, "KILL": syscall.SIGKILL, "PIPE": syscall.SIGPIPE, "PROF": syscall.SIGPROF, "QUIT": syscall.SIGQUIT, "SEGV": syscall.SIGSEGV, "STOP": syscall.SIGSTOP, "SYS": syscall.SIGSYS, "TERM": syscall.SIGTERM, "TRAP": syscall.SIGTRAP, "TSTP": syscall.SIGTSTP, "TTIN": syscall.SIGTTIN, "TTOU": syscall.SIGTTOU, "URG": syscall.SIGURG, "USR1": syscall.SIGUSR1, "USR2": syscall.SIGUSR2, "VTALRM": syscall.SIGVTALRM, "WINCH": syscall.SIGWINCH, "XCPU": syscall.SIGXCPU, "XFSZ": syscall.SIGXFSZ, } golang-github-moby-sys-0.0~git20231105.a4e0878/signal/signal_freebsd.go000066400000000000000000000020541452213572400251600ustar00rootroot00000000000000package signal import ( "syscall" ) // SignalMap is a map of FreeBSD signals. var SignalMap = map[string]syscall.Signal{ "ABRT": syscall.SIGABRT, "ALRM": syscall.SIGALRM, "BUS": syscall.SIGBUS, "CHLD": syscall.SIGCHLD, "CONT": syscall.SIGCONT, "EMT": syscall.SIGEMT, "FPE": syscall.SIGFPE, "HUP": syscall.SIGHUP, "ILL": syscall.SIGILL, "INFO": syscall.SIGINFO, "INT": syscall.SIGINT, "IO": syscall.SIGIO, "IOT": syscall.SIGIOT, "KILL": syscall.SIGKILL, "LWP": syscall.SIGLWP, "PIPE": syscall.SIGPIPE, "PROF": syscall.SIGPROF, "QUIT": syscall.SIGQUIT, "SEGV": syscall.SIGSEGV, "STOP": syscall.SIGSTOP, "SYS": syscall.SIGSYS, "TERM": syscall.SIGTERM, "THR": syscall.SIGTHR, "TRAP": syscall.SIGTRAP, "TSTP": syscall.SIGTSTP, "TTIN": syscall.SIGTTIN, "TTOU": syscall.SIGTTOU, "URG": syscall.SIGURG, "USR1": syscall.SIGUSR1, "USR2": syscall.SIGUSR2, "VTALRM": syscall.SIGVTALRM, "WINCH": syscall.SIGWINCH, "XCPU": syscall.SIGXCPU, "XFSZ": syscall.SIGXFSZ, } golang-github-moby-sys-0.0~git20231105.a4e0878/signal/signal_linux.go000066400000000000000000000037651452213572400247170ustar00rootroot00000000000000//go:build !mips && !mipsle && !mips64 && !mips64le // +build !mips,!mipsle,!mips64,!mips64le package signal import ( "syscall" "golang.org/x/sys/unix" ) const ( sigrtmin = 34 sigrtmax = 64 ) // SignalMap is a map of Linux signals. var SignalMap = map[string]syscall.Signal{ "ABRT": unix.SIGABRT, "ALRM": unix.SIGALRM, "BUS": unix.SIGBUS, "CHLD": unix.SIGCHLD, "CLD": unix.SIGCLD, "CONT": unix.SIGCONT, "FPE": unix.SIGFPE, "HUP": unix.SIGHUP, "ILL": unix.SIGILL, "INT": unix.SIGINT, "IO": unix.SIGIO, "IOT": unix.SIGIOT, "KILL": unix.SIGKILL, "PIPE": unix.SIGPIPE, "POLL": unix.SIGPOLL, "PROF": unix.SIGPROF, "PWR": unix.SIGPWR, "QUIT": unix.SIGQUIT, "SEGV": unix.SIGSEGV, "STKFLT": unix.SIGSTKFLT, "STOP": unix.SIGSTOP, "SYS": unix.SIGSYS, "TERM": unix.SIGTERM, "TRAP": unix.SIGTRAP, "TSTP": unix.SIGTSTP, "TTIN": unix.SIGTTIN, "TTOU": unix.SIGTTOU, "URG": unix.SIGURG, "USR1": unix.SIGUSR1, "USR2": unix.SIGUSR2, "VTALRM": unix.SIGVTALRM, "WINCH": unix.SIGWINCH, "XCPU": unix.SIGXCPU, "XFSZ": unix.SIGXFSZ, "RTMIN": sigrtmin, "RTMIN+1": sigrtmin + 1, "RTMIN+2": sigrtmin + 2, "RTMIN+3": sigrtmin + 3, "RTMIN+4": sigrtmin + 4, "RTMIN+5": sigrtmin + 5, "RTMIN+6": sigrtmin + 6, "RTMIN+7": sigrtmin + 7, "RTMIN+8": sigrtmin + 8, "RTMIN+9": sigrtmin + 9, "RTMIN+10": sigrtmin + 10, "RTMIN+11": sigrtmin + 11, "RTMIN+12": sigrtmin + 12, "RTMIN+13": sigrtmin + 13, "RTMIN+14": sigrtmin + 14, "RTMIN+15": sigrtmin + 15, "RTMAX-14": sigrtmax - 14, "RTMAX-13": sigrtmax - 13, "RTMAX-12": sigrtmax - 12, "RTMAX-11": sigrtmax - 11, "RTMAX-10": sigrtmax - 10, "RTMAX-9": sigrtmax - 9, "RTMAX-8": sigrtmax - 8, "RTMAX-7": sigrtmax - 7, "RTMAX-6": sigrtmax - 6, "RTMAX-5": sigrtmax - 5, "RTMAX-4": sigrtmax - 4, "RTMAX-3": sigrtmax - 3, "RTMAX-2": sigrtmax - 2, "RTMAX-1": sigrtmax - 1, "RTMAX": sigrtmax, } golang-github-moby-sys-0.0~git20231105.a4e0878/signal/signal_linux_mipsx.go000066400000000000000000000040061452213572400261240ustar00rootroot00000000000000//go:build linux && (mips || mipsle || mips64 || mips64le) // +build linux // +build mips mipsle mips64 mips64le package signal import ( "syscall" "golang.org/x/sys/unix" ) const ( sigrtmin = 34 sigrtmax = 127 ) // SignalMap is a map of Linux signals. var SignalMap = map[string]syscall.Signal{ "ABRT": unix.SIGABRT, "ALRM": unix.SIGALRM, "BUS": unix.SIGBUS, "CHLD": unix.SIGCHLD, "CLD": unix.SIGCLD, "CONT": unix.SIGCONT, "FPE": unix.SIGFPE, "HUP": unix.SIGHUP, "ILL": unix.SIGILL, "INT": unix.SIGINT, "IO": unix.SIGIO, "IOT": unix.SIGIOT, "KILL": unix.SIGKILL, "PIPE": unix.SIGPIPE, "POLL": unix.SIGPOLL, "PROF": unix.SIGPROF, "PWR": unix.SIGPWR, "QUIT": unix.SIGQUIT, "SEGV": unix.SIGSEGV, "EMT": unix.SIGEMT, "STOP": unix.SIGSTOP, "SYS": unix.SIGSYS, "TERM": unix.SIGTERM, "TRAP": unix.SIGTRAP, "TSTP": unix.SIGTSTP, "TTIN": unix.SIGTTIN, "TTOU": unix.SIGTTOU, "URG": unix.SIGURG, "USR1": unix.SIGUSR1, "USR2": unix.SIGUSR2, "VTALRM": unix.SIGVTALRM, "WINCH": unix.SIGWINCH, "XCPU": unix.SIGXCPU, "XFSZ": unix.SIGXFSZ, "RTMIN": sigrtmin, "RTMIN+1": sigrtmin + 1, "RTMIN+2": sigrtmin + 2, "RTMIN+3": sigrtmin + 3, "RTMIN+4": sigrtmin + 4, "RTMIN+5": sigrtmin + 5, "RTMIN+6": sigrtmin + 6, "RTMIN+7": sigrtmin + 7, "RTMIN+8": sigrtmin + 8, "RTMIN+9": sigrtmin + 9, "RTMIN+10": sigrtmin + 10, "RTMIN+11": sigrtmin + 11, "RTMIN+12": sigrtmin + 12, "RTMIN+13": sigrtmin + 13, "RTMIN+14": sigrtmin + 14, "RTMIN+15": sigrtmin + 15, "RTMAX-14": sigrtmax - 14, "RTMAX-13": sigrtmax - 13, "RTMAX-12": sigrtmax - 12, "RTMAX-11": sigrtmax - 11, "RTMAX-10": sigrtmax - 10, "RTMAX-9": sigrtmax - 9, "RTMAX-8": sigrtmax - 8, "RTMAX-7": sigrtmax - 7, "RTMAX-6": sigrtmax - 6, "RTMAX-5": sigrtmax - 5, "RTMAX-4": sigrtmax - 4, "RTMAX-3": sigrtmax - 3, "RTMAX-2": sigrtmax - 2, "RTMAX-1": sigrtmax - 1, "RTMAX": sigrtmax, } golang-github-moby-sys-0.0~git20231105.a4e0878/signal/signal_linux_test.go000066400000000000000000000027411452213572400257470ustar00rootroot00000000000000//go:build darwin || linux // +build darwin linux package signal import ( "os" "syscall" "testing" "time" ) func TestCatchAll(t *testing.T) { sigs := make(chan os.Signal, 1) CatchAll(sigs) defer StopCatch(sigs) listOfSignals := map[string]string{ "CONT": syscall.SIGCONT.String(), "HUP": syscall.SIGHUP.String(), "CHLD": syscall.SIGCHLD.String(), "ILL": syscall.SIGILL.String(), "FPE": syscall.SIGFPE.String(), "CLD": syscall.SIGCLD.String(), } for sigStr := range listOfSignals { if signal, ok := SignalMap[sigStr]; ok { _ = syscall.Kill(syscall.Getpid(), signal) s := <-sigs if s.String() != signal.String() { t.Errorf("expected: %q, got: %q", signal, s) } } } } func TestCatchAllIgnoreSigUrg(t *testing.T) { sigs := make(chan os.Signal, 1) CatchAll(sigs) defer StopCatch(sigs) err := syscall.Kill(syscall.Getpid(), syscall.SIGURG) if err != nil { t.Fatal(err) } timer := time.NewTimer(1 * time.Second) defer timer.Stop() select { case <-timer.C: case s := <-sigs: t.Fatalf("expected no signals to be handled, but received %q", s.String()) } } func TestStopCatch(t *testing.T) { signal := SignalMap["HUP"] channel := make(chan os.Signal, 1) CatchAll(channel) _ = syscall.Kill(syscall.Getpid(), signal) signalString := <-channel if signalString.String() != signal.String() { t.Errorf("expected: %q, got: %q", signal, signalString) } StopCatch(channel) _, ok := <-channel if ok { t.Error("expected: !ok, got: ok") } } golang-github-moby-sys-0.0~git20231105.a4e0878/signal/signal_test.go000066400000000000000000000017501452213572400245270ustar00rootroot00000000000000package signal import ( "syscall" "testing" ) func TestParseSignal(t *testing.T) { _, err := ParseSignal("0") expectedErr := "invalid signal: 0" if err == nil || err.Error() != expectedErr { t.Errorf("expected %q, but got %v", expectedErr, err) } _, err = ParseSignal("SIG") expectedErr = "invalid signal: SIG" if err == nil || err.Error() != expectedErr { t.Errorf("expected %q, but got %v", expectedErr, err) } for sigStr := range SignalMap { responseSignal, err := ParseSignal(sigStr) if err != nil { t.Error(err) } signal := SignalMap[sigStr] if responseSignal != signal { t.Errorf("expected: %q, got: %q", signal, responseSignal) } } } func TestValidSignalForPlatform(t *testing.T) { isValidSignal := ValidSignalForPlatform(syscall.Signal(0)) if isValidSignal { t.Error("expected !isValidSignal") } for _, sigN := range SignalMap { isValidSignal = ValidSignalForPlatform(sigN) if !isValidSignal { t.Error("expected isValidSignal") } } } golang-github-moby-sys-0.0~git20231105.a4e0878/signal/signal_unix.go000066400000000000000000000011341452213572400245270ustar00rootroot00000000000000//go:build !windows // +build !windows package signal import ( "syscall" ) // Signals used in cli/command (no windows equivalent, use // invalid signals so they don't get handled) const ( // SIGCHLD is a signal sent to a process when a child process terminates, is interrupted, or resumes after being interrupted. SIGCHLD = syscall.SIGCHLD // SIGWINCH is a signal sent to a process when its controlling terminal changes its size SIGWINCH = syscall.SIGWINCH // SIGPIPE is a signal sent to a process when a pipe is written to before the other end is open for reading SIGPIPE = syscall.SIGPIPE ) golang-github-moby-sys-0.0~git20231105.a4e0878/signal/signal_unsupported.go000066400000000000000000000003661452213572400261420ustar00rootroot00000000000000//go:build !linux && !darwin && !freebsd && !windows // +build !linux,!darwin,!freebsd,!windows package signal import ( "syscall" ) // SignalMap is an empty map of signals for unsupported platform. var SignalMap = map[string]syscall.Signal{} golang-github-moby-sys-0.0~git20231105.a4e0878/signal/signal_windows.go000066400000000000000000000034431452213572400252430ustar00rootroot00000000000000package signal import ( "syscall" "golang.org/x/sys/windows" ) // Signals used in cli/command (no windows equivalent, use // invalid signals so they don't get handled) const ( SIGCHLD = syscall.Signal(0xff) SIGWINCH = syscall.Signal(0xff) SIGPIPE = syscall.Signal(0xff) ) // SignalMap is a map of "supported" signals. As per the comment in GOLang's // ztypes_windows.go: "More invented values for signals". Windows doesn't // really support signals in any way, shape or form that Unix does. var SignalMap = map[string]syscall.Signal{ "ABRT": syscall.Signal(windows.SIGABRT), "ALRM": syscall.Signal(windows.SIGALRM), "BUS": syscall.Signal(windows.SIGBUS), "FPE": syscall.Signal(windows.SIGFPE), "HUP": syscall.Signal(windows.SIGHUP), "ILL": syscall.Signal(windows.SIGILL), "INT": syscall.Signal(windows.SIGINT), "KILL": syscall.Signal(windows.SIGKILL), "PIPE": syscall.Signal(windows.SIGPIPE), "QUIT": syscall.Signal(windows.SIGQUIT), "SEGV": syscall.Signal(windows.SIGSEGV), "TERM": syscall.Signal(windows.SIGTERM), "TRAP": syscall.Signal(windows.SIGTRAP), // additional linux signals supported for LCOW "CHLD": syscall.Signal(0x11), "CLD": syscall.Signal(0x11), "CONT": syscall.Signal(0x12), "IO": syscall.Signal(0x1d), "IOT": syscall.Signal(0x6), "POLL": syscall.Signal(0x1d), "PROF": syscall.Signal(0x1b), "PWR": syscall.Signal(0x1e), "STKFLT": syscall.Signal(0x10), "STOP": syscall.Signal(0x13), "SYS": syscall.Signal(0x1f), "TSTP": syscall.Signal(0x14), "TTIN": syscall.Signal(0x15), "TTOU": syscall.Signal(0x16), "URG": syscall.Signal(0x17), "USR1": syscall.Signal(0xa), "USR2": syscall.Signal(0xc), "VTALRM": syscall.Signal(0x1a), "WINCH": syscall.Signal(0x1c), "XCPU": syscall.Signal(0x18), "XFSZ": syscall.Signal(0x19), } golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/000077500000000000000000000000001452213572400220725ustar00rootroot00000000000000golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/LICENSE.APACHE000066400000000000000000000250131452213572400240200ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2014-2018 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/LICENSE.BSD000066400000000000000000000027251452213572400235140ustar00rootroot00000000000000Copyright (c) 2014-2018 The Docker & Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/README.md000066400000000000000000000006021452213572400233470ustar00rootroot00000000000000Package symlink implements EvalSymlinksInScope which is an extension of filepath.EvalSymlinks, as well as a Windows long-path aware version of filepath.EvalSymlinks from the [Go standard library](https://golang.org/pkg/path/filepath). The code from filepath.EvalSymlinks has been adapted in fs.go. Please read the LICENSE.BSD file that governs fs.go and LICENSE.APACHE for fs_test.go. golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/doc.go000066400000000000000000000004031452213572400231630ustar00rootroot00000000000000// Package symlink implements EvalSymlinksInScope which is an extension of // filepath.EvalSymlinks, as well as a Windows long-path aware version of // filepath.EvalSymlinks from the Go standard library (https://golang.org/pkg/path/filepath). package symlink golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/fs.go000066400000000000000000000113661452213572400230400ustar00rootroot00000000000000// Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.BSD file. // This code is a modified version of path/filepath/symlink.go from the Go standard library. package symlink import ( "bytes" "errors" "os" "path/filepath" "strings" ) // FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an // absolute path. This function handles paths in a platform-agnostic manner. func FollowSymlinkInScope(path, root string) (string, error) { path, err := filepath.Abs(filepath.FromSlash(path)) if err != nil { return "", err } root, err = filepath.Abs(filepath.FromSlash(root)) if err != nil { return "", err } return evalSymlinksInScope(path, root) } // evalSymlinksInScope will evaluate symlinks in `path` within a scope `root` and return // a result guaranteed to be contained within the scope `root`, at the time of the call. // Symlinks in `root` are not evaluated and left as-is. // Errors encountered while attempting to evaluate symlinks in path will be returned. // Non-existing paths are valid and do not constitute an error. // `path` has to contain `root` as a prefix, or else an error will be returned. // Trying to break out from `root` does not constitute an error. // // Example: // // If /foo/bar -> /outside, // FollowSymlinkInScope("/foo/bar", "/foo") == "/foo/outside" instead of "/outside" // // IMPORTANT: it is the caller's responsibility to call evalSymlinksInScope *after* relevant symlinks // are created and not to create subsequently, additional symlinks that could potentially make a // previously-safe path, unsafe. Example: if /foo/bar does not exist, evalSymlinksInScope("/foo/bar", "/foo") // would return "/foo/bar". If one makes /foo/bar a symlink to /baz subsequently, then "/foo/bar" should // no longer be considered safely contained in "/foo". func evalSymlinksInScope(path, root string) (string, error) { root = filepath.Clean(root) if path == root { return path, nil } if !strings.HasPrefix(path, root) { return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root) } const maxIter = 255 originalPath := path // given root of "/a" and path of "/a/b/../../c" we want path to be "/b/../../c" path = path[len(root):] if root == string(filepath.Separator) { path = string(filepath.Separator) + path } if !strings.HasPrefix(path, string(filepath.Separator)) { return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root) } path = filepath.Clean(path) // consume path by taking each frontmost path element, // expanding it if it's a symlink, and appending it to b var b bytes.Buffer // b here will always be considered to be the "current absolute path inside // root" when we append paths to it, we also append a slash and use // filepath.Clean after the loop to trim the trailing slash for n := 0; path != ""; n++ { if n > maxIter { return "", errors.New("evalSymlinksInScope: too many links in " + originalPath) } // find next path component, p i := strings.IndexRune(path, filepath.Separator) var p string if i == -1 { p, path = path, "" } else { p, path = path[:i], path[i+1:] } if p == "" { continue } // this takes a b.String() like "b/../" and a p like "c" and turns it // into "/b/../c" which then gets filepath.Cleaned into "/c" and then // root gets prepended and we Clean again (to remove any trailing slash // if the first Clean gave us just "/") cleanP := filepath.Clean(string(filepath.Separator) + b.String() + p) if isDriveOrRoot(cleanP) { // never Lstat "/" itself, or drive letters on Windows b.Reset() continue } fullP := filepath.Clean(root + cleanP) fi, err := os.Lstat(fullP) if os.IsNotExist(err) { // if p does not exist, accept it b.WriteString(p) b.WriteRune(filepath.Separator) continue } if err != nil { return "", err } if fi.Mode()&os.ModeSymlink == 0 { b.WriteString(p) b.WriteRune(filepath.Separator) continue } // it's a symlink, put it at the front of path dest, err := os.Readlink(fullP) if err != nil { return "", err } if isAbs(dest) { b.Reset() } path = dest + string(filepath.Separator) + path } // see note above on "fullP := ..." for why this is double-cleaned and // what's happening here return filepath.Clean(root + filepath.Clean(string(filepath.Separator)+b.String())), nil } // EvalSymlinks returns the path name after the evaluation of any symbolic // links. // If path is relative the result will be relative to the current directory, // unless one of the components is an absolute symbolic link. // This version has been updated to support long paths prepended with `\\?\`. func EvalSymlinks(path string) (string, error) { return evalSymlinks(path) } golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/fs_unix.go000066400000000000000000000004301452213572400240710ustar00rootroot00000000000000//go:build !windows // +build !windows package symlink import ( "path/filepath" ) func evalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) } func isDriveOrRoot(p string) bool { return p == string(filepath.Separator) } var isAbs = filepath.IsAbs golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/fs_unix_test.go000066400000000000000000000215131452213572400251350ustar00rootroot00000000000000//go:build !windows // +build !windows // Licensed under the Apache License, Version 2.0; See LICENSE.APACHE package symlink import ( "fmt" "os" "path/filepath" "testing" ) // TODO Windows: This needs some serious work to port to Windows. For now, // turning off testing in this package. type dirOrLink struct { path string target string } func makeFs(tmpdir string, fs []dirOrLink) error { for _, s := range fs { s.path = filepath.Join(tmpdir, s.path) if s.target == "" { _ = os.MkdirAll(s.path, 0o755) continue } if err := os.MkdirAll(filepath.Dir(s.path), 0o755); err != nil { return err } if err := os.Symlink(s.target, s.path); err != nil && !os.IsExist(err) { return err } } return nil } func testSymlink(tmpdir, path, expected, scope string) error { rewrite, err := FollowSymlinkInScope(filepath.Join(tmpdir, path), filepath.Join(tmpdir, scope)) if err != nil { return err } expected, err = filepath.Abs(filepath.Join(tmpdir, expected)) if err != nil { return err } if expected != rewrite { return fmt.Errorf("expected %q got %q", expected, rewrite) } return nil } func TestFollowSymlinkAbsolute(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "testdata/fs/a/d/c/data", "testdata/b/c/data", "testdata"); err != nil { t.Fatal(err) } } func TestFollowSymlinkRelativePath(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/i", target: "a"}}); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "testdata/fs/i", "testdata/fs/a", "testdata"); err != nil { t.Fatal(err) } } func TestFollowSymlinkSkipSymlinksOutsideScope(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{ {path: "linkdir", target: "realdir"}, {path: "linkdir/foo/bar"}, }); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "linkdir/foo/bar", "linkdir/foo/bar", "linkdir/foo"); err != nil { t.Fatal(err) } } func TestFollowSymlinkInvalidScopePathPair(t *testing.T) { if _, err := FollowSymlinkInScope("toto", "testdata"); err == nil { t.Fatal("expected an error") } } func TestFollowSymlinkLastLink(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "testdata/fs/a/d", "testdata/b", "testdata"); err != nil { t.Fatal(err) } } func TestFollowSymlinkRelativeLinkChangeScope(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/e", target: "../b"}}); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "testdata/fs/a/e/c/data", "testdata/fs/b/c/data", "testdata"); err != nil { t.Fatal(err) } // avoid letting allowing symlink e lead us to ../b // normalize to the "testdata/fs/a" if err := testSymlink(tmpdir, "testdata/fs/a/e", "testdata/fs/a/b", "testdata/fs/a"); err != nil { t.Fatal(err) } } func TestFollowSymlinkDeepRelativeLinkChangeScope(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/f", target: "../../../../test"}}); err != nil { t.Fatal(err) } // avoid letting symlink f lead us out of the "testdata" scope // we don't normalize because symlink f is in scope and there is no // information leak if err := testSymlink(tmpdir, "testdata/fs/a/f", "testdata/test", "testdata"); err != nil { t.Fatal(err) } // avoid letting symlink f lead us out of the "testdata/fs" scope // we don't normalize because symlink f is in scope and there is no // information leak if err := testSymlink(tmpdir, "testdata/fs/a/f", "testdata/fs/test", "testdata/fs"); err != nil { t.Fatal(err) } } func TestFollowSymlinkRelativeLinkChain(t *testing.T) { tmpdir := t.TempDir() // avoid letting symlink g (pointed at by symlink h) take out of scope // TODO: we should probably normalize to scope here because ../[....]/root // is out of scope and we leak information if err := makeFs(tmpdir, []dirOrLink{ {path: "testdata/fs/b/h", target: "../g"}, {path: "testdata/fs/g", target: "../../../../../../../../../../../../root"}, }); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "testdata/fs/b/h", "testdata/root", "testdata"); err != nil { t.Fatal(err) } } func TestFollowSymlinkBreakoutPath(t *testing.T) { tmpdir := t.TempDir() // avoid letting symlink -> ../directory/file escape from scope // normalize to "testdata/fs/j" if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/j/k", target: "../i/a"}}); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "testdata/fs/j/k", "testdata/fs/j/i/a", "testdata/fs/j"); err != nil { t.Fatal(err) } } func TestFollowSymlinkToRoot(t *testing.T) { tmpdir := t.TempDir() // make sure we don't allow escaping to / // normalize to dir if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "/"}}); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "foo", "", ""); err != nil { t.Fatal(err) } } func TestFollowSymlinkSlashDotdot(t *testing.T) { tmpdir := t.TempDir() tmpdir = filepath.Join(tmpdir, "dir", "subdir") // make sure we don't allow escaping to / // normalize to dir if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "/../../"}}); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "foo", "", ""); err != nil { t.Fatal(err) } } func TestFollowSymlinkDotdot(t *testing.T) { tmpdir := t.TempDir() tmpdir = filepath.Join(tmpdir, "dir", "subdir") // make sure we stay in scope without leaking information // this also checks for escaping to / // normalize to dir if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "../../"}}); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "foo", "", ""); err != nil { t.Fatal(err) } } func TestFollowSymlinkRelativePath2(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{{path: "bar/foo", target: "baz/target"}}); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "bar/foo", "bar/baz/target", ""); err != nil { t.Fatal(err) } } func TestFollowSymlinkScopeLink(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{ {path: "root2"}, {path: "root", target: "root2"}, {path: "root2/foo", target: "../bar"}, }); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "root/foo", "root/bar", "root"); err != nil { t.Fatal(err) } } func TestFollowSymlinkRootScope(t *testing.T) { tmpdir := t.TempDir() expected, err := filepath.EvalSymlinks(tmpdir) if err != nil { t.Fatal(err) } rewrite, err := FollowSymlinkInScope(tmpdir, "/") if err != nil { t.Fatal(err) } if rewrite != expected { t.Fatalf("expected %q got %q", expected, rewrite) } } func TestFollowSymlinkEmpty(t *testing.T) { res, err := FollowSymlinkInScope("", "") if err != nil { t.Fatal(err) } wd, err := os.Getwd() if err != nil { t.Fatal(err) } if res != wd { t.Fatalf("expected %q got %q", wd, res) } } func TestFollowSymlinkCircular(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{{path: "root/foo", target: "foo"}}); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "root/foo", "", "root"); err == nil { t.Fatal("expected an error for foo -> foo") } if err := makeFs(tmpdir, []dirOrLink{ {path: "root/bar", target: "baz"}, {path: "root/baz", target: "../bak"}, {path: "root/bak", target: "/bar"}, }); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "root/foo", "", "root"); err == nil { t.Fatal("expected an error for bar -> baz -> bak -> bar") } } func TestFollowSymlinkComplexChainWithTargetPathsContainingLinks(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{ {path: "root2"}, {path: "root", target: "root2"}, {path: "root/a", target: "r/s"}, {path: "root/r", target: "../root/t"}, {path: "root/root/t/s/b", target: "/../u"}, {path: "root/u/c", target: "."}, {path: "root/u/x/y", target: "../v"}, {path: "root/u/v", target: "/../w"}, }); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "root/a/b/c/x/y/z", "root/w/z", "root"); err != nil { t.Fatal(err) } } func TestFollowSymlinkBreakoutNonExistent(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{ {path: "root/slash", target: "/"}, {path: "root/sym", target: "/idontexist/../slash"}, }); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "root/sym/file", "root/file", "root"); err != nil { t.Fatal(err) } } func TestFollowSymlinkNoLexicalCleaning(t *testing.T) { tmpdir := t.TempDir() if err := makeFs(tmpdir, []dirOrLink{ {path: "root/sym", target: "/foo/bar"}, {path: "root/hello", target: "/sym/../baz"}, }); err != nil { t.Fatal(err) } if err := testSymlink(tmpdir, "root/hello", "root/foo/baz", "root"); err != nil { t.Fatal(err) } } golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/fs_windows.go000066400000000000000000000106151452213572400246060ustar00rootroot00000000000000package symlink import ( "bytes" "errors" "os" "path/filepath" "strings" "golang.org/x/sys/windows" ) func toShort(path string) (string, error) { p, err := windows.UTF16FromString(path) if err != nil { return "", err } b := p // GetShortPathName says we can reuse buffer n, err := windows.GetShortPathName(&p[0], &b[0], uint32(len(b))) if err != nil { return "", err } if n > uint32(len(b)) { b = make([]uint16, n) if _, err = windows.GetShortPathName(&p[0], &b[0], uint32(len(b))); err != nil { return "", err } } return windows.UTF16ToString(b), nil } func toLong(path string) (string, error) { p, err := windows.UTF16FromString(path) if err != nil { return "", err } b := p // GetLongPathName says we can reuse buffer n, err := windows.GetLongPathName(&p[0], &b[0], uint32(len(b))) if err != nil { return "", err } if n > uint32(len(b)) { b = make([]uint16, n) n, err = windows.GetLongPathName(&p[0], &b[0], uint32(len(b))) if err != nil { return "", err } } b = b[:n] return windows.UTF16ToString(b), nil } func evalSymlinks(path string) (string, error) { path, err := walkSymlinks(path) if err != nil { return "", err } p, err := toShort(path) if err != nil { return "", err } p, err = toLong(p) if err != nil { return "", err } // windows.GetLongPathName does not change the case of the drive letter, // but the result of EvalSymlinks must be unique, so we have // EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`). // Make drive letter upper case. if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' { p = string(p[0]+'A'-'a') + p[1:] } else if len(p) >= 6 && p[5] == ':' && 'a' <= p[4] && p[4] <= 'z' { p = p[:3] + string(p[4]+'A'-'a') + p[5:] } return filepath.Clean(p), nil } const ( utf8RuneSelf = 0x80 longPathPrefix = `\\?\` ) func walkSymlinks(path string) (string, error) { const maxIter = 255 originalPath := path // consume path by taking each frontmost path element, // expanding it if it's a symlink, and appending it to b var b bytes.Buffer for n := 0; path != ""; n++ { if n > maxIter { return "", errors.New("too many links in " + originalPath) } // A path beginning with `\\?\` represents the root, so automatically // skip that part and begin processing the next segment. if strings.HasPrefix(path, longPathPrefix) { b.WriteString(longPathPrefix) path = path[4:] continue } // find next path component, p i := -1 for j, c := range path { if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) { i = j break } } var p string if i == -1 { p, path = path, "" } else { p, path = path[:i], path[i+1:] } if p == "" { if b.Len() == 0 { // must be absolute path b.WriteRune(filepath.Separator) } continue } // If this is the first segment after the long path prefix, accept the // current segment as a volume root or UNC share and move on to the next. if b.String() == longPathPrefix { b.WriteString(p) b.WriteRune(filepath.Separator) continue } fi, err := os.Lstat(b.String() + p) if err != nil { return "", err } if fi.Mode()&os.ModeSymlink == 0 { b.WriteString(p) if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') { b.WriteRune(filepath.Separator) } continue } // it's a symlink, put it at the front of path dest, err := os.Readlink(b.String() + p) if err != nil { return "", err } if isAbs(dest) { b.Reset() } path = dest + string(filepath.Separator) + path } return filepath.Clean(b.String()), nil } func isDriveOrRoot(p string) bool { if p == string(filepath.Separator) { return true } length := len(p) if length >= 2 { if p[length-1] == ':' && (('a' <= p[length-2] && p[length-2] <= 'z') || ('A' <= p[length-2] && p[length-2] <= 'Z')) { return true } } return false } // isAbs is a platform-specific wrapper for filepath.IsAbs. On Windows, // golang filepath.IsAbs does not consider a path \windows\system32 as absolute // as it doesn't start with a drive-letter/colon combination. However, in // docker we need to verify things such as WORKDIR /windows/system32 in // a Dockerfile (which gets translated to \windows\system32 when being processed // by the daemon. This SHOULD be treated as absolute from a docker processing // perspective. func isAbs(path string) bool { if filepath.IsAbs(path) || strings.HasPrefix(path, string(os.PathSeparator)) { return true } return false } golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/go.mod000066400000000000000000000001511452213572400231750ustar00rootroot00000000000000module github.com/moby/sys/symlink go 1.16 require golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang-github-moby-sys-0.0~git20231105.a4e0878/symlink/go.sum000066400000000000000000000003171452213572400232260ustar00rootroot00000000000000golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang-github-moby-sys-0.0~git20231105.a4e0878/user/000077500000000000000000000000001452213572400213625ustar00rootroot00000000000000golang-github-moby-sys-0.0~git20231105.a4e0878/user/go.mod000066400000000000000000000001121452213572400224620ustar00rootroot00000000000000module github.com/moby/sys/user go 1.17 require golang.org/x/sys v0.1.0 golang-github-moby-sys-0.0~git20231105.a4e0878/user/go.sum000066400000000000000000000002271452213572400225160ustar00rootroot00000000000000golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang-github-moby-sys-0.0~git20231105.a4e0878/user/lookup_unix.go000066400000000000000000000077111452213572400242730ustar00rootroot00000000000000//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris // +build darwin dragonfly freebsd linux netbsd openbsd solaris package user import ( "io" "os" "strconv" "golang.org/x/sys/unix" ) // Unix-specific path to the passwd and group formatted files. const ( unixPasswdPath = "/etc/passwd" unixGroupPath = "/etc/group" ) // LookupUser looks up a user by their username in /etc/passwd. If the user // cannot be found (or there is no /etc/passwd file on the filesystem), then // LookupUser returns an error. func LookupUser(username string) (User, error) { return lookupUserFunc(func(u User) bool { return u.Name == username }) } // LookupUid looks up a user by their user id in /etc/passwd. If the user cannot // be found (or there is no /etc/passwd file on the filesystem), then LookupId // returns an error. func LookupUid(uid int) (User, error) { return lookupUserFunc(func(u User) bool { return u.Uid == uid }) } func lookupUserFunc(filter func(u User) bool) (User, error) { // Get operating system-specific passwd reader-closer. passwd, err := GetPasswd() if err != nil { return User{}, err } defer passwd.Close() // Get the users. users, err := ParsePasswdFilter(passwd, filter) if err != nil { return User{}, err } // No user entries found. if len(users) == 0 { return User{}, ErrNoPasswdEntries } // Assume the first entry is the "correct" one. return users[0], nil } // LookupGroup looks up a group by its name in /etc/group. If the group cannot // be found (or there is no /etc/group file on the filesystem), then LookupGroup // returns an error. func LookupGroup(groupname string) (Group, error) { return lookupGroupFunc(func(g Group) bool { return g.Name == groupname }) } // LookupGid looks up a group by its group id in /etc/group. If the group cannot // be found (or there is no /etc/group file on the filesystem), then LookupGid // returns an error. func LookupGid(gid int) (Group, error) { return lookupGroupFunc(func(g Group) bool { return g.Gid == gid }) } func lookupGroupFunc(filter func(g Group) bool) (Group, error) { // Get operating system-specific group reader-closer. group, err := GetGroup() if err != nil { return Group{}, err } defer group.Close() // Get the users. groups, err := ParseGroupFilter(group, filter) if err != nil { return Group{}, err } // No user entries found. if len(groups) == 0 { return Group{}, ErrNoGroupEntries } // Assume the first entry is the "correct" one. return groups[0], nil } func GetPasswdPath() (string, error) { return unixPasswdPath, nil } func GetPasswd() (io.ReadCloser, error) { return os.Open(unixPasswdPath) } func GetGroupPath() (string, error) { return unixGroupPath, nil } func GetGroup() (io.ReadCloser, error) { return os.Open(unixGroupPath) } // CurrentUser looks up the current user by their user id in /etc/passwd. If the // user cannot be found (or there is no /etc/passwd file on the filesystem), // then CurrentUser returns an error. func CurrentUser() (User, error) { return LookupUid(unix.Getuid()) } // CurrentGroup looks up the current user's group by their primary group id's // entry in /etc/passwd. If the group cannot be found (or there is no // /etc/group file on the filesystem), then CurrentGroup returns an error. func CurrentGroup() (Group, error) { return LookupGid(unix.Getgid()) } func currentUserSubIDs(fileName string) ([]SubID, error) { u, err := CurrentUser() if err != nil { return nil, err } filter := func(entry SubID) bool { return entry.Name == u.Name || entry.Name == strconv.Itoa(u.Uid) } return ParseSubIDFileFilter(fileName, filter) } func CurrentUserSubUIDs() ([]SubID, error) { return currentUserSubIDs("/etc/subuid") } func CurrentUserSubGIDs() ([]SubID, error) { return currentUserSubIDs("/etc/subgid") } func CurrentProcessUIDMap() ([]IDMap, error) { return ParseIDMapFile("/proc/self/uid_map") } func CurrentProcessGIDMap() ([]IDMap, error) { return ParseIDMapFile("/proc/self/gid_map") } golang-github-moby-sys-0.0~git20231105.a4e0878/user/user.go000066400000000000000000000350761452213572400227020ustar00rootroot00000000000000package user import ( "bufio" "bytes" "errors" "fmt" "io" "os" "strconv" "strings" ) const ( minID = 0 maxID = 1<<31 - 1 // for 32-bit systems compatibility ) var ( // ErrNoPasswdEntries is returned if no matching entries were found in /etc/group. ErrNoPasswdEntries = errors.New("no matching entries in passwd file") // ErrNoGroupEntries is returned if no matching entries were found in /etc/passwd. ErrNoGroupEntries = errors.New("no matching entries in group file") // ErrRange is returned if a UID or GID is outside of the valid range. ErrRange = fmt.Errorf("uids and gids must be in range %d-%d", minID, maxID) ) type User struct { Name string Pass string Uid int Gid int Gecos string Home string Shell string } type Group struct { Name string Pass string Gid int List []string } // SubID represents an entry in /etc/sub{u,g}id type SubID struct { Name string SubID int64 Count int64 } // IDMap represents an entry in /proc/PID/{u,g}id_map type IDMap struct { ID int64 ParentID int64 Count int64 } func parseLine(line []byte, v ...interface{}) { parseParts(bytes.Split(line, []byte(":")), v...) } func parseParts(parts [][]byte, v ...interface{}) { if len(parts) == 0 { return } for i, p := range parts { // Ignore cases where we don't have enough fields to populate the arguments. // Some configuration files like to misbehave. if len(v) <= i { break } // Use the type of the argument to figure out how to parse it, scanf() style. // This is legit. switch e := v[i].(type) { case *string: *e = string(p) case *int: // "numbers", with conversion errors ignored because of some misbehaving configuration files. *e, _ = strconv.Atoi(string(p)) case *int64: *e, _ = strconv.ParseInt(string(p), 10, 64) case *[]string: // Comma-separated lists. if len(p) != 0 { *e = strings.Split(string(p), ",") } else { *e = []string{} } default: // Someone goof'd when writing code using this function. Scream so they can hear us. panic(fmt.Sprintf("parseLine only accepts {*string, *int, *int64, *[]string} as arguments! %#v is not a pointer!", e)) } } } func ParsePasswdFile(path string) ([]User, error) { passwd, err := os.Open(path) if err != nil { return nil, err } defer passwd.Close() return ParsePasswd(passwd) } func ParsePasswd(passwd io.Reader) ([]User, error) { return ParsePasswdFilter(passwd, nil) } func ParsePasswdFileFilter(path string, filter func(User) bool) ([]User, error) { passwd, err := os.Open(path) if err != nil { return nil, err } defer passwd.Close() return ParsePasswdFilter(passwd, filter) } func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) { if r == nil { return nil, errors.New("nil source for passwd-formatted data") } var ( s = bufio.NewScanner(r) out = []User{} ) for s.Scan() { line := bytes.TrimSpace(s.Bytes()) if len(line) == 0 { continue } // see: man 5 passwd // name:password:UID:GID:GECOS:directory:shell // Name:Pass:Uid:Gid:Gecos:Home:Shell // root:x:0:0:root:/root:/bin/bash // adm:x:3:4:adm:/var/adm:/bin/false p := User{} parseLine(line, &p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell) if filter == nil || filter(p) { out = append(out, p) } } if err := s.Err(); err != nil { return nil, err } return out, nil } func ParseGroupFile(path string) ([]Group, error) { group, err := os.Open(path) if err != nil { return nil, err } defer group.Close() return ParseGroup(group) } func ParseGroup(group io.Reader) ([]Group, error) { return ParseGroupFilter(group, nil) } func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) { group, err := os.Open(path) if err != nil { return nil, err } defer group.Close() return ParseGroupFilter(group, filter) } func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { if r == nil { return nil, errors.New("nil source for group-formatted data") } rd := bufio.NewReader(r) out := []Group{} // Read the file line-by-line. for { var ( isPrefix bool wholeLine []byte err error ) // Read the next line. We do so in chunks (as much as reader's // buffer is able to keep), check if we read enough columns // already on each step and store final result in wholeLine. for { var line []byte line, isPrefix, err = rd.ReadLine() if err != nil { // We should return no error if EOF is reached // without a match. if err == io.EOF { err = nil } return out, err } // Simple common case: line is short enough to fit in a // single reader's buffer. if !isPrefix && len(wholeLine) == 0 { wholeLine = line break } wholeLine = append(wholeLine, line...) // Check if we read the whole line already. if !isPrefix { break } } // There's no spec for /etc/passwd or /etc/group, but we try to follow // the same rules as the glibc parser, which allows comments and blank // space at the beginning of a line. wholeLine = bytes.TrimSpace(wholeLine) if len(wholeLine) == 0 || wholeLine[0] == '#' { continue } // see: man 5 group // group_name:password:GID:user_list // Name:Pass:Gid:List // root:x:0:root // adm:x:4:root,adm,daemon p := Group{} parseLine(wholeLine, &p.Name, &p.Pass, &p.Gid, &p.List) if filter == nil || filter(p) { out = append(out, p) } } } type ExecUser struct { Uid int Gid int Sgids []int Home string } // GetExecUserPath is a wrapper for GetExecUser. It reads data from each of the // given file paths and uses that data as the arguments to GetExecUser. If the // files cannot be opened for any reason, the error is ignored and a nil // io.Reader is passed instead. func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) { var passwd, group io.Reader if passwdFile, err := os.Open(passwdPath); err == nil { passwd = passwdFile defer passwdFile.Close() } if groupFile, err := os.Open(groupPath); err == nil { group = groupFile defer groupFile.Close() } return GetExecUser(userSpec, defaults, passwd, group) } // GetExecUser parses a user specification string (using the passwd and group // readers as sources for /etc/passwd and /etc/group data, respectively). In // the case of blank fields or missing data from the sources, the values in // defaults is used. // // GetExecUser will return an error if a user or group literal could not be // found in any entry in passwd and group respectively. // // Examples of valid user specifications are: // - "" // - "user" // - "uid" // - "user:group" // - "uid:gid // - "user:gid" // - "uid:group" // // It should be noted that if you specify a numeric user or group id, they will // not be evaluated as usernames (only the metadata will be filled). So attempting // to parse a user with user.Name = "1337" will produce the user with a UID of // 1337. func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (*ExecUser, error) { if defaults == nil { defaults = new(ExecUser) } // Copy over defaults. user := &ExecUser{ Uid: defaults.Uid, Gid: defaults.Gid, Sgids: defaults.Sgids, Home: defaults.Home, } // Sgids slice *cannot* be nil. if user.Sgids == nil { user.Sgids = []int{} } // Allow for userArg to have either "user" syntax, or optionally "user:group" syntax var userArg, groupArg string parseLine([]byte(userSpec), &userArg, &groupArg) // Convert userArg and groupArg to be numeric, so we don't have to execute // Atoi *twice* for each iteration over lines. uidArg, uidErr := strconv.Atoi(userArg) gidArg, gidErr := strconv.Atoi(groupArg) // Find the matching user. users, err := ParsePasswdFilter(passwd, func(u User) bool { if userArg == "" { // Default to current state of the user. return u.Uid == user.Uid } if uidErr == nil { // If the userArg is numeric, always treat it as a UID. return uidArg == u.Uid } return u.Name == userArg }) // If we can't find the user, we have to bail. if err != nil && passwd != nil { if userArg == "" { userArg = strconv.Itoa(user.Uid) } return nil, fmt.Errorf("unable to find user %s: %w", userArg, err) } var matchedUserName string if len(users) > 0 { // First match wins, even if there's more than one matching entry. matchedUserName = users[0].Name user.Uid = users[0].Uid user.Gid = users[0].Gid user.Home = users[0].Home } else if userArg != "" { // If we can't find a user with the given username, the only other valid // option is if it's a numeric username with no associated entry in passwd. if uidErr != nil { // Not numeric. return nil, fmt.Errorf("unable to find user %s: %w", userArg, ErrNoPasswdEntries) } user.Uid = uidArg // Must be inside valid uid range. if user.Uid < minID || user.Uid > maxID { return nil, ErrRange } // Okay, so it's numeric. We can just roll with this. } // On to the groups. If we matched a username, we need to do this because of // the supplementary group IDs. if groupArg != "" || matchedUserName != "" { groups, err := ParseGroupFilter(group, func(g Group) bool { // If the group argument isn't explicit, we'll just search for it. if groupArg == "" { // Check if user is a member of this group. for _, u := range g.List { if u == matchedUserName { return true } } return false } if gidErr == nil { // If the groupArg is numeric, always treat it as a GID. return gidArg == g.Gid } return g.Name == groupArg }) if err != nil && group != nil { return nil, fmt.Errorf("unable to find groups for spec %v: %w", matchedUserName, err) } // Only start modifying user.Gid if it is in explicit form. if groupArg != "" { if len(groups) > 0 { // First match wins, even if there's more than one matching entry. user.Gid = groups[0].Gid } else { // If we can't find a group with the given name, the only other valid // option is if it's a numeric group name with no associated entry in group. if gidErr != nil { // Not numeric. return nil, fmt.Errorf("unable to find group %s: %w", groupArg, ErrNoGroupEntries) } user.Gid = gidArg // Must be inside valid gid range. if user.Gid < minID || user.Gid > maxID { return nil, ErrRange } // Okay, so it's numeric. We can just roll with this. } } else if len(groups) > 0 { // Supplementary group ids only make sense if in the implicit form. user.Sgids = make([]int, len(groups)) for i, group := range groups { user.Sgids[i] = group.Gid } } } return user, nil } // GetAdditionalGroups looks up a list of groups by name or group id // against the given /etc/group formatted data. If a group name cannot // be found, an error will be returned. If a group id cannot be found, // or the given group data is nil, the id will be returned as-is // provided it is in the legal range. func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, error) { groups := []Group{} if group != nil { var err error groups, err = ParseGroupFilter(group, func(g Group) bool { for _, ag := range additionalGroups { if g.Name == ag || strconv.Itoa(g.Gid) == ag { return true } } return false }) if err != nil { return nil, fmt.Errorf("Unable to find additional groups %v: %w", additionalGroups, err) } } gidMap := make(map[int]struct{}) for _, ag := range additionalGroups { var found bool for _, g := range groups { // if we found a matched group either by name or gid, take the // first matched as correct if g.Name == ag || strconv.Itoa(g.Gid) == ag { if _, ok := gidMap[g.Gid]; !ok { gidMap[g.Gid] = struct{}{} found = true break } } } // we asked for a group but didn't find it. let's check to see // if we wanted a numeric group if !found { gid, err := strconv.ParseInt(ag, 10, 64) if err != nil { // Not a numeric ID either. return nil, fmt.Errorf("Unable to find group %s: %w", ag, ErrNoGroupEntries) } // Ensure gid is inside gid range. if gid < minID || gid > maxID { return nil, ErrRange } gidMap[int(gid)] = struct{}{} } } gids := []int{} for gid := range gidMap { gids = append(gids, gid) } return gids, nil } // GetAdditionalGroupsPath is a wrapper around GetAdditionalGroups // that opens the groupPath given and gives it as an argument to // GetAdditionalGroups. func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int, error) { var group io.Reader if groupFile, err := os.Open(groupPath); err == nil { group = groupFile defer groupFile.Close() } return GetAdditionalGroups(additionalGroups, group) } func ParseSubIDFile(path string) ([]SubID, error) { subid, err := os.Open(path) if err != nil { return nil, err } defer subid.Close() return ParseSubID(subid) } func ParseSubID(subid io.Reader) ([]SubID, error) { return ParseSubIDFilter(subid, nil) } func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error) { subid, err := os.Open(path) if err != nil { return nil, err } defer subid.Close() return ParseSubIDFilter(subid, filter) } func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) { if r == nil { return nil, errors.New("nil source for subid-formatted data") } var ( s = bufio.NewScanner(r) out = []SubID{} ) for s.Scan() { line := bytes.TrimSpace(s.Bytes()) if len(line) == 0 { continue } // see: man 5 subuid p := SubID{} parseLine(line, &p.Name, &p.SubID, &p.Count) if filter == nil || filter(p) { out = append(out, p) } } if err := s.Err(); err != nil { return nil, err } return out, nil } func ParseIDMapFile(path string) ([]IDMap, error) { r, err := os.Open(path) if err != nil { return nil, err } defer r.Close() return ParseIDMap(r) } func ParseIDMap(r io.Reader) ([]IDMap, error) { return ParseIDMapFilter(r, nil) } func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error) { r, err := os.Open(path) if err != nil { return nil, err } defer r.Close() return ParseIDMapFilter(r, filter) } func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) { if r == nil { return nil, errors.New("nil source for idmap-formatted data") } var ( s = bufio.NewScanner(r) out = []IDMap{} ) for s.Scan() { line := bytes.TrimSpace(s.Bytes()) if len(line) == 0 { continue } // see: man 7 user_namespaces p := IDMap{} parseParts(bytes.Fields(line), &p.ID, &p.ParentID, &p.Count) if filter == nil || filter(p) { out = append(out, p) } } if err := s.Err(); err != nil { return nil, err } return out, nil } golang-github-moby-sys-0.0~git20231105.a4e0878/user/user_fuzzer.go000066400000000000000000000014151452213572400242750ustar00rootroot00000000000000//go:build gofuzz // +build gofuzz package user import ( "io" "strings" ) func IsDivisbleBy(n int, divisibleby int) bool { return (n % divisibleby) == 0 } func FuzzUser(data []byte) int { if len(data) == 0 { return -1 } if !IsDivisbleBy(len(data), 5) { return -1 } var divided [][]byte chunkSize := len(data) / 5 for i := 0; i < len(data); i += chunkSize { end := i + chunkSize divided = append(divided, data[i:end]) } _, _ = ParsePasswdFilter(strings.NewReader(string(divided[0])), nil) var passwd, group io.Reader group = strings.NewReader(string(divided[1])) _, _ = GetAdditionalGroups([]string{string(divided[2])}, group) passwd = strings.NewReader(string(divided[3])) _, _ = GetExecUser(string(divided[4]), nil, passwd, group) return 1 } golang-github-moby-sys-0.0~git20231105.a4e0878/user/user_test.go000066400000000000000000000254221452213572400237330ustar00rootroot00000000000000package user import ( "fmt" "io" "reflect" "sort" "strconv" "strings" "testing" ) func TestUserParseLine(t *testing.T) { var ( a, b string c []string d int ) parseLine([]byte(""), &a, &b) if a != "" || b != "" { t.Fatalf("a and b should be empty ('%v', '%v')", a, b) } parseLine([]byte("a"), &a, &b) if a != "a" || b != "" { t.Fatalf("a should be 'a' and b should be empty ('%v', '%v')", a, b) } parseLine([]byte("bad boys:corny cows"), &a, &b) if a != "bad boys" || b != "corny cows" { t.Fatalf("a should be 'bad boys' and b should be 'corny cows' ('%v', '%v')", a, b) } parseLine([]byte(""), &c) if len(c) != 0 { t.Fatalf("c should be empty (%#v)", c) } parseLine([]byte("d,e,f:g:h:i,j,k"), &c, &a, &b, &c) if a != "g" || b != "h" || len(c) != 3 || c[0] != "i" || c[1] != "j" || c[2] != "k" { t.Fatalf("a should be 'g', b should be 'h', and c should be ['i','j','k'] ('%v', '%v', '%#v')", a, b, c) } parseLine([]byte("::::::::::"), &a, &b, &c) if a != "" || b != "" || len(c) != 0 { t.Fatalf("a, b, and c should all be empty ('%v', '%v', '%#v')", a, b, c) } parseLine([]byte("not a number"), &d) if d != 0 { t.Fatalf("d should be 0 (%v)", d) } parseLine([]byte("b:12:c"), &a, &d, &b) if a != "b" || b != "c" || d != 12 { t.Fatalf("a should be 'b' and b should be 'c', and d should be 12 ('%v', '%v', %v)", a, b, d) } } func TestUserParsePasswd(t *testing.T) { users, err := ParsePasswdFilter(strings.NewReader(` root:x:0:0:root:/root:/bin/bash adm:x:3:4:adm:/var/adm:/bin/false this is just some garbage data `), nil) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(users) != 3 { t.Fatalf("Expected 3 users, got %v", len(users)) } if users[0].Uid != 0 || users[0].Name != "root" { t.Fatalf("Expected users[0] to be 0 - root, got %v - %v", users[0].Uid, users[0].Name) } if users[1].Uid != 3 || users[1].Name != "adm" { t.Fatalf("Expected users[1] to be 3 - adm, got %v - %v", users[1].Uid, users[1].Name) } } func TestUserParseGroup(t *testing.T) { groups, err := ParseGroupFilter(strings.NewReader(` root:x:0:root adm:x:4:root,adm,daemon this is just some garbage data `+largeGroup()), nil) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(groups) != 4 { t.Fatalf("Expected 4 groups, got %v", len(groups)) } if groups[0].Gid != 0 || groups[0].Name != "root" || len(groups[0].List) != 1 { t.Fatalf("Expected groups[0] to be 0 - root - 1 member, got %v - %v - %v", groups[0].Gid, groups[0].Name, len(groups[0].List)) } if groups[1].Gid != 4 || groups[1].Name != "adm" || len(groups[1].List) != 3 { t.Fatalf("Expected groups[1] to be 4 - adm - 3 members, got %v - %v - %v", groups[1].Gid, groups[1].Name, len(groups[1].List)) } } func TestValidGetExecUser(t *testing.T) { const passwdContent = ` root:x:0:0:root user:/root:/bin/bash adm:x:42:43:adm:/var/adm:/bin/false 111:x:222:333::/var/garbage odd:x:111:112::/home/odd::::: user7456:x:7456:100:Vasya:/home/user7456 this is just some garbage data ` groupContent := ` root:x:0:root adm:x:43: grp:x:1234:root,adm,user7456 444:x:555:111 odd:x:444: this is just some garbage data ` + largeGroup() defaultExecUser := ExecUser{ Uid: 8888, Gid: 8888, Sgids: []int{8888}, Home: "/8888", } tests := []struct { ref string expected ExecUser }{ { ref: "root", expected: ExecUser{ Uid: 0, Gid: 0, Sgids: []int{0, 1234}, Home: "/root", }, }, { ref: "adm", expected: ExecUser{ Uid: 42, Gid: 43, Sgids: []int{1234}, Home: "/var/adm", }, }, { ref: "root:adm", expected: ExecUser{ Uid: 0, Gid: 43, Sgids: defaultExecUser.Sgids, Home: "/root", }, }, { ref: "adm:1234", expected: ExecUser{ Uid: 42, Gid: 1234, Sgids: defaultExecUser.Sgids, Home: "/var/adm", }, }, { ref: "42:1234", expected: ExecUser{ Uid: 42, Gid: 1234, Sgids: defaultExecUser.Sgids, Home: "/var/adm", }, }, { ref: "1337:1234", expected: ExecUser{ Uid: 1337, Gid: 1234, Sgids: defaultExecUser.Sgids, Home: defaultExecUser.Home, }, }, { ref: "1337", expected: ExecUser{ Uid: 1337, Gid: defaultExecUser.Gid, Sgids: defaultExecUser.Sgids, Home: defaultExecUser.Home, }, }, { ref: "", expected: ExecUser{ Uid: defaultExecUser.Uid, Gid: defaultExecUser.Gid, Sgids: defaultExecUser.Sgids, Home: defaultExecUser.Home, }, }, // Regression tests for #695. { ref: "111", expected: ExecUser{ Uid: 111, Gid: 112, Sgids: defaultExecUser.Sgids, Home: "/home/odd", }, }, { ref: "111:444", expected: ExecUser{ Uid: 111, Gid: 444, Sgids: defaultExecUser.Sgids, Home: "/home/odd", }, }, // Test for #3036. { ref: "7456", expected: ExecUser{ Uid: 7456, Gid: 100, Sgids: []int{1234, 1000}, // 1000 is largegroup GID Home: "/home/user7456", }, }, } for _, test := range tests { passwd := strings.NewReader(passwdContent) group := strings.NewReader(groupContent) execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group) if err != nil { t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error()) t.Fail() continue } if !reflect.DeepEqual(test.expected, *execUser) { t.Logf("ref: %v", test.ref) t.Logf("got: %#v", execUser) t.Logf("expected: %#v", test.expected) t.Fail() continue } } } func TestInvalidGetExecUser(t *testing.T) { const passwdContent = ` root:x:0:0:root user:/root:/bin/bash adm:x:42:43:adm:/var/adm:/bin/false -42:x:12:13:broken:/very/broken this is just some garbage data ` const groupContent = ` root:x:0:root adm:x:43: grp:x:1234:root,adm this is just some garbage data ` tests := []string{ // No such user/group. "notuser", "notuser:notgroup", "root:notgroup", "notuser:adm", "8888:notgroup", "notuser:8888", // Invalid user/group values. "-1:0", "0:-3", "-5:-2", "-42", "-43", } for _, test := range tests { passwd := strings.NewReader(passwdContent) group := strings.NewReader(groupContent) execUser, err := GetExecUser(test, nil, passwd, group) if err == nil { t.Logf("got unexpected success when parsing '%s': %#v", test, execUser) t.Fail() continue } } } func TestGetExecUserNilSources(t *testing.T) { const passwdContent = ` root:x:0:0:root user:/root:/bin/bash adm:x:42:43:adm:/var/adm:/bin/false this is just some garbage data ` const groupContent = ` root:x:0:root adm:x:43: grp:x:1234:root,adm this is just some garbage data ` defaultExecUser := ExecUser{ Uid: 8888, Gid: 8888, Sgids: []int{8888}, Home: "/8888", } tests := []struct { ref string passwd, group bool expected ExecUser }{ { ref: "", passwd: false, group: false, expected: ExecUser{ Uid: 8888, Gid: 8888, Sgids: []int{8888}, Home: "/8888", }, }, { ref: "root", passwd: true, group: false, expected: ExecUser{ Uid: 0, Gid: 0, Sgids: []int{8888}, Home: "/root", }, }, { ref: "0", passwd: false, group: false, expected: ExecUser{ Uid: 0, Gid: 8888, Sgids: []int{8888}, Home: "/8888", }, }, { ref: "0:0", passwd: false, group: false, expected: ExecUser{ Uid: 0, Gid: 0, Sgids: []int{8888}, Home: "/8888", }, }, } for _, test := range tests { var passwd, group io.Reader if test.passwd { passwd = strings.NewReader(passwdContent) } if test.group { group = strings.NewReader(groupContent) } execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group) if err != nil { t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error()) t.Fail() continue } if !reflect.DeepEqual(test.expected, *execUser) { t.Logf("got: %#v", execUser) t.Logf("expected: %#v", test.expected) t.Fail() continue } } } func TestGetAdditionalGroups(t *testing.T) { type foo struct { groups []string expected []int hasError bool } groupContent := ` root:x:0:root adm:x:43: grp:x:1234:root,adm adm:x:4343:root,adm-duplicate this is just some garbage data ` + largeGroup() tests := []foo{ { // empty group groups: []string{}, expected: []int{}, }, { // single group groups: []string{"adm"}, expected: []int{43}, }, { // multiple groups groups: []string{"adm", "grp"}, expected: []int{43, 1234}, }, { // invalid group groups: []string{"adm", "grp", "not-exist"}, expected: nil, hasError: true, }, { // group with numeric id groups: []string{"43"}, expected: []int{43}, }, { // group with unknown numeric id groups: []string{"adm", "10001"}, expected: []int{43, 10001}, }, { // groups specified twice with numeric and name groups: []string{"adm", "43"}, expected: []int{43}, }, { // groups with too small id groups: []string{"-1"}, expected: nil, hasError: true, }, { // groups with too large id groups: []string{strconv.FormatInt(1<<31, 10)}, expected: nil, hasError: true, }, { // group with very long list of users groups: []string{"largegroup"}, expected: []int{1000}, }, } for _, test := range tests { group := strings.NewReader(groupContent) gids, err := GetAdditionalGroups(test.groups, group) if test.hasError && err == nil { t.Errorf("Parse(%#v) expects error but has none", test) continue } if !test.hasError && err != nil { t.Errorf("Parse(%#v) has error %v", test, err) continue } sort.Ints(gids) if !reflect.DeepEqual(gids, test.expected) { t.Errorf("Gids(%v), expect %v from groups %v", gids, test.expected, test.groups) } } } func TestGetAdditionalGroupsNumeric(t *testing.T) { tests := []struct { groups []string expected []int hasError bool }{ { // numeric groups only groups: []string{"1234", "5678"}, expected: []int{1234, 5678}, }, { // numeric and alphabetic groups: []string{"1234", "fake"}, expected: nil, hasError: true, }, } for _, test := range tests { gids, err := GetAdditionalGroups(test.groups, nil) if test.hasError && err == nil { t.Errorf("Parse(%#v) expects error but has none", test) continue } if !test.hasError && err != nil { t.Errorf("Parse(%#v) has error %v", test, err) continue } sort.Ints(gids) if !reflect.DeepEqual(gids, test.expected) { t.Errorf("Gids(%v), expect %v from groups %v", gids, test.expected, test.groups) } } } // Generate a proper "largegroup" entry for group tests. func largeGroup() (res string) { var b strings.Builder b.WriteString("largegroup:x:1000:user1") for i := 2; i <= 7500; i++ { fmt.Fprintf(&b, ",user%d", i) } return b.String() }