fork of https://github.com/sourcegraph/zoekt
0

Configure Feed

Select the types of activity you want to include in your feed.

mountinfo: switch to using new external sourcegraph/mountinfo package (#456)

+16 -501
+5 -2
cmd/zoekt-sourcegraph-indexserver/main.go
··· 36 36 "go.uber.org/automaxprocs/maxprocs" 37 37 "golang.org/x/net/trace" 38 38 39 - "github.com/sourcegraph/zoekt/internal/mountinfo" 39 + "github.com/sourcegraph/mountinfo" 40 40 41 41 "github.com/sourcegraph/zoekt" 42 42 "github.com/sourcegraph/zoekt/build" ··· 1069 1069 go oc.Run() 1070 1070 1071 1071 logger := sglog.Scoped("metricsRegistration", "") 1072 - mountinfo.MustRegisterNewMountPointInfoMetric(logger, mountinfo.MountPointInfoOpts{Namespace: "zoekt_indexserver"}, map[string]string{"indexDir": conf.index}) 1072 + opts := mountinfo.CollectorOpts{Namespace: "zoekt_indexserver"} 1073 + 1074 + c := mountinfo.NewCollector(logger, opts, map[string]string{"indexDir": conf.index}) 1075 + prometheus.DefaultRegisterer.MustRegister(c) 1073 1076 1074 1077 s.Run() 1075 1078 return nil
+7 -2
cmd/zoekt-webserver/main.go
··· 35 35 "syscall" 36 36 "time" 37 37 38 + "github.com/sourcegraph/mountinfo" 39 + 38 40 "github.com/sourcegraph/zoekt" 39 41 "github.com/sourcegraph/zoekt/build" 40 42 "github.com/sourcegraph/zoekt/debugserver" 41 - "github.com/sourcegraph/zoekt/internal/mountinfo" 42 43 "github.com/sourcegraph/zoekt/internal/profiler" 43 44 "github.com/sourcegraph/zoekt/internal/tracer" 44 45 "github.com/sourcegraph/zoekt/query" ··· 184 185 metricsLogger := sglog.Scoped("metricsRegistration", "") 185 186 186 187 mustRegisterMemoryMapMetrics(metricsLogger) 187 - mountinfo.MustRegisterNewMountPointInfoMetric(metricsLogger, mountinfo.MountPointInfoOpts{Namespace: "zoekt_webserver"}, map[string]string{"indexDir": *index}) 188 + 189 + opts := mountinfo.CollectorOpts{Namespace: "zoekt_webserver"} 190 + c := mountinfo.NewCollector(metricsLogger, opts, map[string]string{"indexDir": *index}) 191 + 192 + prometheus.DefaultRegisterer.MustRegister(c) 188 193 189 194 // Do not block on loading shards so we can become partially available 190 195 // sooner. Otherwise on large instances zoekt can be unavailable on the
+2 -1
go.mod
··· 27 27 github.com/rs/xid v1.4.0 28 28 github.com/sourcegraph/go-ctags v0.0.0-20220611154803-db463692f037 29 29 github.com/sourcegraph/log v0.0.0-20221006140640-7c567caa79cb 30 + github.com/sourcegraph/mountinfo v0.0.0-20221027185101-272dd8baaf4a 30 31 github.com/uber/jaeger-client-go v2.30.0+incompatible 31 32 github.com/uber/jaeger-lib v2.4.1+incompatible 32 33 github.com/xanzy/go-gitlab v0.73.1 ··· 44 45 golang.org/x/net v0.1.0 45 46 golang.org/x/oauth2 v0.1.0 46 47 golang.org/x/sync v0.1.0 47 - golang.org/x/sys v0.1.0 48 48 gopkg.in/natefinch/lumberjack.v2 v2.0.0 49 49 ) 50 50 ··· 107 107 go.uber.org/zap v1.23.0 // indirect 108 108 golang.org/x/crypto v0.1.0 // indirect 109 109 golang.org/x/mod v0.6.0 // indirect 110 + golang.org/x/sys v0.1.0 // indirect 110 111 golang.org/x/text v0.4.0 // indirect 111 112 golang.org/x/time v0.1.0 // indirect 112 113 golang.org/x/tools v0.2.0 // indirect
+2
go.sum
··· 575 575 github.com/sourcegraph/go-ctags v0.0.0-20220611154803-db463692f037/go.mod h1:ZYjpRXoJrRlxjU9ZfpaUKJkk62AjhJPffN3rlw2aqxM= 576 576 github.com/sourcegraph/log v0.0.0-20221006140640-7c567caa79cb h1:cgfkhaHK7OMlkwROcC4oSFRPZT/KRGFwx7M2i8T0A1c= 577 577 github.com/sourcegraph/log v0.0.0-20221006140640-7c567caa79cb/go.mod h1:UxiwB6C3xk3xOySJpW1R0MDUyfGuJRFS5Z8C+SA5p2I= 578 + github.com/sourcegraph/mountinfo v0.0.0-20221027185101-272dd8baaf4a h1:fD9C4qHZWr7girCD8EwBHdmdViIWtdVTBO6k9SInVQo= 579 + github.com/sourcegraph/mountinfo v0.0.0-20221027185101-272dd8baaf4a/go.mod h1:SrU3BANBcV8XmM6ul5PBvBXDwTliGyjwiklR8OW61hI= 578 580 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 579 581 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 580 582 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-223
internal/mountinfo/mountinfo.go
··· 1 - package mountinfo 2 - 3 - import ( 4 - "errors" 5 - "fmt" 6 - "os" 7 - "path/filepath" 8 - "runtime" 9 - "strings" 10 - 11 - "github.com/prometheus/client_golang/prometheus" 12 - "github.com/prometheus/client_golang/prometheus/promauto" 13 - sglog "github.com/sourcegraph/log" 14 - "golang.org/x/sys/unix" 15 - ) 16 - 17 - // defaultSysMountPoint is the common mount point for the sysfs pseudo-filesystem. 18 - const defaultSysMountPoint = "/sys" 19 - 20 - // MountPointInfoOpts modifies the behavior of the metric created 21 - // by MustRegisterNewMountPointInfoMetric. 22 - type MountPointInfoOpts struct { 23 - // If non-empty, Namespace prefixes the "mount_point_info" metric by the provided string and 24 - // an underscore ("_"). 25 - Namespace string 26 - } 27 - 28 - // MustRegisterNewMountPointInfoMetric registers a Prometheus metric named "mount_point_info" that 29 - // contains the names of the block storage devices that back each of the requested mounts. 30 - // 31 - // Mounts is a set of name -> file path mappings (example: {"indexDir": "/home/.zoekt"}). 32 - // 33 - // The metric "mount_point_info" has a constant value of 1 and two labels: 34 - // - mount_name: caller-provided name for the given mount (example: "indexDir") 35 - // - device: name of the block device that backs the given mount file path (example: "sdb") 36 - // 37 - // This metric only works on Linux-based operating systems that have access to the sysfs pseudo-filesystem. 38 - // On all other operating systems, this metric will not emit any values. 39 - func MustRegisterNewMountPointInfoMetric(logger sglog.Logger, opts MountPointInfoOpts, mounts map[string]string) { 40 - logger = logger.Scoped("mountPointInfo", "registration logic for mount_point_info Prometheus metric") 41 - 42 - metric := promauto.NewGaugeVec(prometheus.GaugeOpts{ 43 - Namespace: opts.Namespace, 44 - Name: "mount_point_info", 45 - Help: "An info metric with a constant '1' value that contains mount_name, device mappings", 46 - }, []string{"mount_name", "device"}) 47 - 48 - // This device discovery logic relies on the sysfs pseudo-filesystem, which only exists 49 - // on linux. 50 - // 51 - // See https://en.wikipedia.org/wiki/Sysfs for more information. 52 - if runtime.GOOS != "linux" { 53 - return 54 - } 55 - 56 - for name, filePath := range mounts { 57 - // for each <mountName>:<mountFilePath> pairing, 58 - // discover the name of the block device that stores <mountFilePath>. 59 - discoveryLogger := logger.Scoped("deviceNameDiscovery", "").With( 60 - sglog.String("mountName", name), 61 - sglog.String("mountFilePath", filePath), 62 - ) 63 - 64 - device, err := discoverDeviceName(discoveryLogger, discoverDeviceNameOpts{}, filePath) 65 - if err != nil { 66 - discoveryLogger.Warn("skipping metric registration", 67 - sglog.String("reason", "failed to discover device name"), 68 - sglog.Error(err), 69 - ) 70 - 71 - continue 72 - } 73 - 74 - discoveryLogger.Debug("discovered device name", 75 - sglog.String("deviceName", device), 76 - ) 77 - 78 - metric.WithLabelValues(name, device).Set(1) 79 - } 80 - } 81 - 82 - type discoverDeviceNameOpts struct { 83 - // sysfsMountPoint is the location of the sysfs mount point. 84 - // If empty, defaultSysMountPoint will be used instead. 85 - sysfsMountPoint string 86 - 87 - // getDeviceNumber, if non-nil, is the function that will be used to find 88 - // the number of the block device that stores the specified file. 89 - // If getDeviceNumber is nil, mountinfo.getDeviceNumber will be used instead. 90 - getDeviceNumber func(filePath string) (major uint32, minor uint32, err error) 91 - } 92 - 93 - // discoverDeviceName returns the name of the block device that filePath is 94 - // stored on. 95 - func discoverDeviceName(logger sglog.Logger, opts discoverDeviceNameOpts, filePath string) (string, error) { 96 - // Note: It's quite involved to implement the device discovery logic for 97 - // every possible kind of storage device (e.x. logical volumes, NFS, etc.) See 98 - // https://unix.stackexchange.com/a/11312 for more information. 99 - // 100 - // As a result, this logic will only work correctly for filePaths that are either: 101 - // - stored directly on a block device 102 - // - stored on a block device's partition 103 - // 104 - // For all other device types, this logic will either: 105 - // - return an incorrect device name 106 - // - return an error 107 - // 108 - // This logic was implemented from information gathered from the following sources (amongst others): 109 - // - "The Linux Programming Interface" by Michael Kerrisk: Chapter 14 110 - // - "Linux Kernel Development" by Robert Love: Chapters 13, 17 111 - // - https://man7.org/linux/man-pages/man5/sysfs.5.html 112 - // - https://en.wikipedia.org/wiki/Sysfs 113 - // - https://unix.stackexchange.com/a/11312 114 - // - https://www.kernel.org/doc/ols/2005/ols2005v1-pages-321-334.pdf 115 - 116 - getDeviceNumber := getDeviceNumber 117 - if opts.getDeviceNumber != nil { 118 - getDeviceNumber = opts.getDeviceNumber 119 - } 120 - 121 - sysfsMountPoint := defaultSysMountPoint 122 - if opts.sysfsMountPoint != "" { 123 - sysfsMountPoint = opts.sysfsMountPoint 124 - } 125 - 126 - sysfsMountPoint = filepath.Clean(sysfsMountPoint) 127 - 128 - // the provided sysfs mountpoint could itself be a symlink, so we 129 - // resolve it immediately so that future file path 130 - // evaluations / massaging doesn't break 131 - sysfsMountPoint, err := filepath.EvalSymlinks(sysfsMountPoint) 132 - if err != nil { 133 - return "", fmt.Errorf("verifying sysfs mountpoint %q: failed to resolve symlink %w", sysfsMountPoint, err) 134 - } 135 - 136 - major, minor, err := getDeviceNumber(filePath) 137 - if err != nil { 138 - return "", fmt.Errorf("discovering device number: %w", err) 139 - } 140 - 141 - // Represent the number in <major>:<minor> format. 142 - deviceNumber := fmt.Sprintf("%d:%d", major, minor) 143 - 144 - logger.Debug( 145 - "discovered device number", 146 - sglog.String("deviceNumber", deviceNumber), 147 - ) 148 - 149 - // /sys/dev/block/<device_number> symlinks to /sys/devices/.../block/.../<deviceName> 150 - symlink := filepath.Join(sysfsMountPoint, "dev", "block", deviceNumber) 151 - 152 - devicePath, err := filepath.EvalSymlinks(symlink) 153 - if err != nil { 154 - return "", fmt.Errorf("discovering device path: failed to evaluate sysfs symlink %q: %w", symlink, err) 155 - } 156 - 157 - devicePath, err = filepath.Abs(devicePath) 158 - if err != nil { 159 - return "", fmt.Errorf("discovering device path: failed to massage device path %q to absolute path: %w", devicePath, err) 160 - } 161 - 162 - logger.Debug("discovered device path", 163 - sglog.String("devicePath", devicePath), 164 - ) 165 - 166 - // Check to see if devicePath points to a disk partition. If so, we need to find the parent 167 - // device. 168 - 169 - // massage the sysfs folder name to ensure that it always ends in a '/' 170 - // so that strings.HasPrefix does what we expect when checking to see if 171 - // we're still under the /sys sub-folder 172 - sysFolderPrefix := strings.TrimSuffix(sysfsMountPoint, string(os.PathSeparator)) 173 - sysFolderPrefix = sysFolderPrefix + string(os.PathSeparator) 174 - 175 - for { 176 - if !strings.HasPrefix(devicePath, sysFolderPrefix) { 177 - // ensure that we're still under the /sys/ sub-folder 178 - return "", fmt.Errorf("validating device path: device path %q isn't a subpath of %q", devicePath, sysFolderPrefix) 179 - } 180 - 181 - _, err := os.Stat(filepath.Join(devicePath, "partition")) 182 - if errors.Is(err, os.ErrNotExist) { 183 - break 184 - } 185 - 186 - parent := filepath.Dir(devicePath) 187 - 188 - logger.Debug("changing device path", 189 - sglog.String("reason", "oldDevicePath represents a disk partition"), 190 - 191 - sglog.String("oldDevicePath", devicePath), 192 - sglog.String("newDevicePath", parent), 193 - ) 194 - 195 - devicePath = parent 196 - } 197 - 198 - // If this device is a block device, its device path should have a symlink 199 - // to the block subsystem. 200 - 201 - subsystemPath, err := filepath.EvalSymlinks(filepath.Join(devicePath, "subsystem")) 202 - if err != nil { 203 - return "", fmt.Errorf("validating device path: failed to discover subsystem that device (path %q) is part of: %w", devicePath, err) 204 - } 205 - 206 - if filepath.Base(subsystemPath) != "block" { 207 - return "", fmt.Errorf("validating device path: device (path %q) is not part of the block subsystem", devicePath) 208 - } 209 - 210 - device := filepath.Base(devicePath) 211 - return filepath.Base(device), nil 212 - } 213 - 214 - func getDeviceNumber(filePath string) (major uint32, minor uint32, err error) { 215 - var stat unix.Stat_t 216 - err = unix.Stat(filePath, &stat) 217 - if err != nil { 218 - return 0, 0, fmt.Errorf("failed to stat %q: %w", filePath, err) 219 - } 220 - 221 - major, minor = unix.Major(uint64(stat.Dev)), unix.Minor(uint64(stat.Dev)) 222 - return major, minor, nil 223 - }
-29
internal/mountinfo/mountinfo_linux_test.go
··· 1 - //go:build linux 2 - 3 - package mountinfo 4 - 5 - import ( 6 - "log" 7 - "os" 8 - "testing" 9 - 10 - "github.com/sourcegraph/log/logtest" 11 - ) 12 - 13 - func Test_DeviceName_SmokeTest(t *testing.T) { 14 - // A simple smoke test to verify that we can find the storage device 15 - // for the current working directory. 16 - logger := logtest.Scoped(t) 17 - 18 - filePath, err := os.Getwd() 19 - if err != nil { 20 - log.Fatalf("getting current working directory: %s", err) 21 - } 22 - 23 - device, err := discoverDeviceName(logger, discoverDeviceNameOpts{}, filePath) 24 - if err != nil { 25 - t.Fatalf("discovering device name for file path %q: %s", filePath, err) 26 - } 27 - 28 - t.Logf("discovered device name %q for file path %q", device, filePath) 29 - }
-196
internal/mountinfo/mountinfo_test.go
··· 1 - package mountinfo 2 - 3 - import ( 4 - "archive/tar" 5 - "compress/gzip" 6 - "io" 7 - "os" 8 - "path/filepath" 9 - "testing" 10 - 11 - "github.com/google/go-cmp/cmp" 12 - "github.com/sourcegraph/log/logtest" 13 - ) 14 - 15 - func Test_DeviceName_Snapshots(t *testing.T) { 16 - // This test uses sysfs snapshots from real linux machines to ensure 17 - // that the device discovery logic returns the expected device name. 18 - 19 - for _, test := range []struct { 20 - name string 21 - 22 - sysfsTarballFile string 23 - 24 - deviceMajor uint32 25 - deviceMinor uint32 26 - 27 - expectedDeviceName string 28 - }{ 29 - { 30 - name: "should find the name of the block device that backs a partition (vda1 -> vda)", 31 - 32 - // ( lsblk output from the snapshotted machine) 33 - // ~ # lsblk 34 - // NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS 35 - // nbd0 43:0 0 0B 0 disk 36 - // nbd1 43:32 0 0B 0 disk 37 - // nbd2 43:64 0 0B 0 disk 38 - // nbd3 43:96 0 0B 0 disk 39 - // nbd4 43:128 0 0B 0 disk 40 - // nbd5 43:160 0 0B 0 disk 41 - // nbd6 43:192 0 0B 0 disk 42 - // nbd7 43:224 0 0B 0 disk 43 - // vda 254:0 0 59.6G 0 disk 44 - // └─vda1 254:1 0 59.6G 0 part /etc/hosts # test targets this partition 45 - // /etc/hostname 46 - // /etc/resolv.conf 47 - // /data/index 48 - // nbd8 43:256 0 0B 0 disk 49 - // nbd9 43:288 0 0B 0 disk 50 - // nbd10 43:320 0 0B 0 disk 51 - // nbd11 43:352 0 0B 0 disk 52 - // nbd12 43:384 0 0B 0 disk 53 - // nbd13 43:416 0 0B 0 disk 54 - // nbd14 43:448 0 0B 0 disk 55 - // nbd15 43:480 0 0B 0 disk 56 - 57 - sysfsTarballFile: "sysfs.vda1.tar.gz", 58 - 59 - deviceMajor: 254, // points to vda1 partition 60 - deviceMinor: 1, 61 - 62 - expectedDeviceName: "vda", 63 - }, 64 - { 65 - name: "should find the device name for a lvm volume backed by a single disk", 66 - 67 - // ( lsblk output from the snapshotted machine) 68 - // ~ # lsblk 69 - // NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS 70 - // sda 8:0 0 7.3T 0 disk 71 - // └─sda1 8:1 0 1024G 0 part /var/lib/plex 72 - // nvme0n1 259:0 0 1.8T 0 disk 73 - // ├─nvme0n1p1 259:1 0 529M 0 part 74 - // ├─nvme0n1p2 259:2 0 99M 0 part 75 - // ├─nvme0n1p3 259:3 0 16M 0 part 76 - // ├─nvme0n1p4 259:4 0 293G 0 part 77 - // ├─nvme0n1p5 259:5 0 512M 0 part /boot 78 - // └─nvme0n1p6 259:6 0 1.5T 0 part 79 - // └─pool-nixos 254:0 0 600G 0 lvm /nix/store 80 - // / # test targets this device 81 - 82 - sysfsTarballFile: "sysfs.lvm.dm-0.tar.gz", 83 - 84 - deviceMajor: 254, // points to dm-0 device 85 - deviceMinor: 0, 86 - 87 - // TODO@ggilmore: technically, dm-0 is a lvm volume backed by a partition stored on the nvme device. 88 - // For consistency with the other test case, we should be returning nvme0n1 (the parent disk device) as the 89 - // device name. I'll revisit this later, as I need to figure out how to programmatically determine 90 - // the nvme01n1 <-> dm-0 translation. 91 - expectedDeviceName: "dm-0", 92 - }, 93 - } { 94 - test := test 95 - 96 - t.Run(t.Name(), func(t *testing.T) { 97 - t.Parallel() 98 - 99 - // provide a custom sysfs location so that we can point the test 100 - // at our sysfs snapshot 101 - mockSysFSDir := filepath.Join(t.TempDir(), "sys") 102 - 103 - // unpack sysfs tarball 104 - tarball := filepath.Join("testdata", test.sysfsTarballFile) 105 - decompressSysFSTarball(t, tarball, mockSysFSDir) 106 - 107 - logger := logtest.Scoped(t) 108 - 109 - mockGetDeviceNumber := func(_ string) (major uint32, minor uint32, err error) { 110 - return test.deviceMajor, test.deviceMinor, nil 111 - } 112 - fakeFilePath := "doesn't matter" // the file path itself doesn't matter since we hard-code the device number 113 - 114 - // execute the test with our injected mocks 115 - actualDeviceName, err := discoverDeviceName( 116 - logger, 117 - discoverDeviceNameOpts{ 118 - sysfsMountPoint: mockSysFSDir, 119 - getDeviceNumber: mockGetDeviceNumber, 120 - }, 121 - fakeFilePath, 122 - ) 123 - 124 - if err != nil { 125 - t.Fatalf("discovering device name for file path %q: %s", fakeFilePath, err) 126 - } 127 - 128 - // verify that the discovered device name is the one that we expect 129 - 130 - if diff := cmp.Diff(test.expectedDeviceName, actualDeviceName); diff != "" { 131 - t.Fatalf("recieved unexpected device name (-want +got):\n%s", diff) 132 - } 133 - }) 134 - } 135 - } 136 - 137 - func decompressSysFSTarball(t *testing.T, tarball, outputFolder string) { 138 - t.Helper() 139 - 140 - file, err := os.Open(tarball) 141 - if err != nil { 142 - t.Fatalf("opening tarball %q: %s", tarball, err) 143 - } 144 - 145 - defer file.Close() 146 - 147 - gz, err := gzip.NewReader(file) 148 - if err != nil { 149 - t.Fatalf("initialzing gzip reader: %s", err) 150 - } 151 - 152 - reader := tar.NewReader(gz) 153 - 154 - for { 155 - header, err := reader.Next() 156 - if err == io.EOF { 157 - break 158 - } 159 - 160 - if err != nil { 161 - t.Fatalf("intializing tar reader: %s", err) 162 - } 163 - 164 - outputFile := filepath.Join(outputFolder, header.Name) 165 - 166 - switch header.Typeflag { 167 - case tar.TypeDir: 168 - err := os.MkdirAll(outputFile, os.FileMode(header.Mode)) 169 - if err != nil { 170 - t.Fatalf("creating directory %q: %s", outputFile, err) 171 - } 172 - 173 - case tar.TypeSymlink: 174 - err := os.Symlink(header.Linkname, outputFile) 175 - if err != nil { 176 - t.Fatalf("creating symlink (%q -> %q): %s", outputFile, header.Linkname, err) 177 - } 178 - 179 - case tar.TypeReg: 180 - f, err := os.OpenFile(outputFile, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) 181 - if err != nil { 182 - t.Fatalf("creating file %q: %s", outputFile, err) 183 - } 184 - 185 - _, err = io.Copy(f, reader) 186 - if err != nil { 187 - t.Fatalf("writing file %q: %s", outputFile, err) 188 - } 189 - 190 - f.Close() 191 - 192 - default: 193 - t.Fatalf("encounted unknown file header type (%d) for file %q", header.Typeflag, header.Name) 194 - } 195 - } 196 - }
-48
internal/mountinfo/testdata/snapshot.sh
··· 1 - #!/usr/bin/env bash 2 - 3 - # Create a tarball of this system's sysfs filesystem + place it in the home directory. 4 - # 5 - # (This special logic is necessary since /sys is a pseudo-filesystem that exposes kernel variables. 6 - # The files in /sys and their sizes will frequently change in between read()'s, which can break naive tar invocations.) 7 - # 8 - # Usage: ./snapshot.sh sysfs.tar.gz 9 - 10 - dst="$PWD/$1" 11 - tmp=$(mktemp -d -t sysfs_snapshot_XXXXXXX) 12 - 13 - cleanup() { 14 - rm -rf "$tmp" 15 - } 16 - trap cleanup EXIT 17 - 18 - set -euxo pipefail 19 - 20 - find /sys/devices/*/block /sys/dev/block /sys/class/block -print0 | sort -z | while IFS= read -d $'\0' -r file; do 21 - # create the new file name by stripping the leading 22 - # /sys and mashing it against the temp folder 23 - temp_file="${tmp}/${file#*/sys/}" 24 - 25 - # create equivalent symlink 26 - if [ -L "$file" ]; then 27 - cp -d "$file" "$temp_file" 28 - continue 29 - fi 30 - 31 - # create necessary directories 32 - if [ -d "$file" ]; then 33 - mkdir -p "$temp_file" 34 - continue 35 - fi 36 - 37 - # skip over any files that we lack permissions to read, 38 - # we encounter I/O errors when trying to read, or 39 - # have some other weirdness 40 - if ! wc -l "$file" >/dev/null 2>&1; then 41 - continue 42 - fi 43 - 44 - cp "$file" "$temp_file" 45 - done 46 - 47 - cd "$tmp" 48 - tar vczf "$dst" .
internal/mountinfo/testdata/sysfs.lvm.dm-0.tar.gz

This is a binary file and will not be displayed.

internal/mountinfo/testdata/sysfs.vda1.tar.gz

This is a binary file and will not be displayed.