github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/linux/sys_getdents.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package linux
    16  
    17  import (
    18  	"bytes"
    19  	"io"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    22  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    23  	"github.com/SagerNet/gvisor/pkg/hostarch"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/arch"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    27  	"github.com/SagerNet/gvisor/pkg/syserror"
    28  	"github.com/SagerNet/gvisor/pkg/usermem"
    29  )
    30  
    31  // LINT.IfChange
    32  
    33  // Getdents implements linux syscall getdents(2) for 64bit systems.
    34  func Getdents(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    35  	fd := args[0].Int()
    36  	addr := args[1].Pointer()
    37  	size := int(args[2].Uint())
    38  
    39  	minSize := int(smallestDirent(t.Arch()))
    40  	if size < minSize {
    41  		// size is smaller than smallest possible dirent.
    42  		return 0, nil, linuxerr.EINVAL
    43  	}
    44  
    45  	n, err := getdents(t, fd, addr, size, (*dirent).Serialize)
    46  	return n, nil, err
    47  }
    48  
    49  // Getdents64 implements linux syscall getdents64(2).
    50  func Getdents64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    51  	fd := args[0].Int()
    52  	addr := args[1].Pointer()
    53  	size := int(args[2].Uint())
    54  
    55  	minSize := int(smallestDirent64(t.Arch()))
    56  	if size < minSize {
    57  		// size is smaller than smallest possible dirent.
    58  		return 0, nil, linuxerr.EINVAL
    59  	}
    60  
    61  	n, err := getdents(t, fd, addr, size, (*dirent).Serialize64)
    62  	return n, nil, err
    63  }
    64  
    65  // getdents implements the core of getdents(2)/getdents64(2).
    66  // f is the syscall implementation dirent serialization function.
    67  func getdents(t *kernel.Task, fd int32, addr hostarch.Addr, size int, f func(*dirent, io.Writer) (int, error)) (uintptr, error) {
    68  	dir := t.GetFile(fd)
    69  	if dir == nil {
    70  		return 0, linuxerr.EBADF
    71  	}
    72  	defer dir.DecRef(t)
    73  
    74  	w := &usermem.IOReadWriter{
    75  		Ctx:  t,
    76  		IO:   t.MemoryManager(),
    77  		Addr: addr,
    78  		Opts: usermem.IOOpts{
    79  			AddressSpaceActive: true,
    80  		},
    81  	}
    82  
    83  	ds := newDirentSerializer(f, w, t.Arch(), size)
    84  	rerr := dir.Readdir(t, ds)
    85  
    86  	switch err := handleIOError(t, ds.Written() > 0, rerr, syserror.ERESTARTSYS, "getdents", dir); err {
    87  	case nil:
    88  		dir.Dirent.InotifyEvent(linux.IN_ACCESS, 0)
    89  		return uintptr(ds.Written()), nil
    90  	case io.EOF:
    91  		return 0, nil
    92  	default:
    93  		return 0, err
    94  	}
    95  }
    96  
    97  // oldDirentHdr is a fixed sized header matching the fixed size fields found in
    98  // the old linux dirent struct.
    99  //
   100  // +marshal
   101  type oldDirentHdr struct {
   102  	Ino    uint64
   103  	Off    uint64
   104  	Reclen uint16 `marshal:"unaligned"` // Struct ends mid-word.
   105  }
   106  
   107  // direntHdr is a fixed sized header matching the fixed size fields found in the
   108  // new linux dirent struct.
   109  //
   110  // +marshal
   111  type direntHdr struct {
   112  	OldHdr oldDirentHdr
   113  	Typ    uint8 `marshal:"unaligned"` // Struct ends mid-word.
   114  }
   115  
   116  // dirent contains the data pointed to by a new linux dirent struct.
   117  type dirent struct {
   118  	Hdr  direntHdr
   119  	Name []byte
   120  }
   121  
   122  // newDirent returns a dirent from an fs.InodeOperationsInfo.
   123  func newDirent(width uint, name string, attr fs.DentAttr, offset uint64) *dirent {
   124  	d := &dirent{
   125  		Hdr: direntHdr{
   126  			OldHdr: oldDirentHdr{
   127  				Ino: attr.InodeID,
   128  				Off: offset,
   129  			},
   130  			Typ: fs.ToDirentType(attr.Type),
   131  		},
   132  		Name: []byte(name),
   133  	}
   134  	d.Hdr.OldHdr.Reclen = d.padRec(int(width))
   135  	return d
   136  }
   137  
   138  // smallestDirent returns the size of the smallest possible dirent using
   139  // the old linux dirent format.
   140  func smallestDirent(a arch.Context) uint {
   141  	d := dirent{}
   142  	return uint(d.Hdr.OldHdr.SizeBytes()) + a.Width() + 1
   143  }
   144  
   145  // smallestDirent64 returns the size of the smallest possible dirent using
   146  // the new linux dirent format.
   147  func smallestDirent64(a arch.Context) uint {
   148  	d := dirent{}
   149  	return uint(d.Hdr.SizeBytes()) + a.Width()
   150  }
   151  
   152  // padRec pads the name field until the rec length is a multiple of the width,
   153  // which must be a power of 2. It returns the padded rec length.
   154  func (d *dirent) padRec(width int) uint16 {
   155  	a := d.Hdr.SizeBytes() + len(d.Name)
   156  	r := (a + width) &^ (width - 1)
   157  	padding := r - a
   158  	d.Name = append(d.Name, make([]byte, padding)...)
   159  	return uint16(r)
   160  }
   161  
   162  // Serialize64 serializes a Dirent struct to a byte slice, keeping the new
   163  // linux dirent format. Returns the number of bytes serialized or an error.
   164  func (d *dirent) Serialize64(w io.Writer) (int, error) {
   165  	n1, err := d.Hdr.WriteTo(w)
   166  	if err != nil {
   167  		return 0, err
   168  	}
   169  	n2, err := w.Write(d.Name)
   170  	if err != nil {
   171  		return 0, err
   172  	}
   173  	return int(n1) + n2, nil
   174  }
   175  
   176  // Serialize serializes a Dirent struct to a byte slice, using the old linux
   177  // dirent format.
   178  // Returns the number of bytes serialized or an error.
   179  func (d *dirent) Serialize(w io.Writer) (int, error) {
   180  	n1, err := d.Hdr.OldHdr.WriteTo(w)
   181  	if err != nil {
   182  		return 0, err
   183  	}
   184  	n2, err := w.Write(d.Name)
   185  	if err != nil {
   186  		return 0, err
   187  	}
   188  	n3, err := w.Write([]byte{d.Hdr.Typ})
   189  	if err != nil {
   190  		return 0, err
   191  	}
   192  	return int(n1) + n2 + n3, nil
   193  }
   194  
   195  // direntSerializer implements fs.InodeOperationsInfoSerializer, serializing dirents to an
   196  // io.Writer.
   197  type direntSerializer struct {
   198  	serialize func(*dirent, io.Writer) (int, error)
   199  	w         io.Writer
   200  	// width is the arch native value width.
   201  	width uint
   202  	// offset is the current dirent offset.
   203  	offset uint64
   204  	// written is the total bytes serialized.
   205  	written int
   206  	// size is the size of the buffer to serialize into.
   207  	size int
   208  }
   209  
   210  func newDirentSerializer(f func(d *dirent, w io.Writer) (int, error), w io.Writer, ac arch.Context, size int) *direntSerializer {
   211  	return &direntSerializer{
   212  		serialize: f,
   213  		w:         w,
   214  		width:     ac.Width(),
   215  		size:      size,
   216  	}
   217  }
   218  
   219  // CopyOut implements fs.InodeOperationsInfoSerializer.CopyOut.
   220  // It serializes and writes the fs.DentAttr to the direntSerializer io.Writer.
   221  func (ds *direntSerializer) CopyOut(name string, attr fs.DentAttr) error {
   222  	ds.offset++
   223  
   224  	d := newDirent(ds.width, name, attr, ds.offset)
   225  
   226  	// Serialize dirent into a temp buffer.
   227  	var b bytes.Buffer
   228  	n, err := ds.serialize(d, &b)
   229  	if err != nil {
   230  		ds.offset--
   231  		return err
   232  	}
   233  
   234  	// Check that we have enough room remaining to write the dirent.
   235  	if n > (ds.size - ds.written) {
   236  		ds.offset--
   237  		return io.EOF
   238  	}
   239  
   240  	// Write out the temp buffer.
   241  	if _, err := b.WriteTo(ds.w); err != nil {
   242  		ds.offset--
   243  		return err
   244  	}
   245  
   246  	ds.written += n
   247  	return nil
   248  }
   249  
   250  // Written returns the total number of bytes written.
   251  func (ds *direntSerializer) Written() int {
   252  	return ds.written
   253  }
   254  
   255  // LINT.ThenChange(vfs2/getdents.go)