github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/client/logging/stat_linux.go (about)

     1  package logging
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"time"
     8  
     9  	"golang.org/x/sys/unix"
    10  
    11  	"github.com/telepresenceio/telepresence/v2/pkg/dos"
    12  )
    13  
    14  type statable interface {
    15  	Fd() uintptr
    16  	Name() string
    17  }
    18  
    19  type fileInfo struct {
    20  	size  int64
    21  	uid   int
    22  	gid   int
    23  	btime time.Time
    24  	mtime time.Time
    25  	ctime time.Time
    26  }
    27  
    28  func osFStat(dfile dos.File) (SysInfo, error) {
    29  	file, ok := dfile.(statable)
    30  	if !ok {
    31  		return nil, fmt.Errorf("files of type %T don't support Fstat", dfile)
    32  	}
    33  	const want = 0 |
    34  		unix.STATX_SIZE |
    35  		unix.STATX_UID |
    36  		unix.STATX_GID |
    37  		unix.STATX_BTIME |
    38  		unix.STATX_MTIME |
    39  		unix.STATX_CTIME
    40  
    41  	var stat unix.Statx_t
    42  	if err := unix.Statx(int(file.Fd()), "", unix.AT_EMPTY_PATH, want, &stat); err != nil {
    43  		if errors.Is(err, unix.ENOSYS) {
    44  			// The statx(2) system call was introduced in Linux 4.11 (2017).  That's new
    45  			// enough that we should have a fallback.
    46  			return oldFStat(file)
    47  		}
    48  		return nil, fmt.Errorf("failed to statx %s: %w", file.Name(), err)
    49  	}
    50  
    51  	if stat.Mask&want != want {
    52  		// Not all filesystems (notably: tmpfs) support btime.
    53  		return oldFStat(file)
    54  	}
    55  
    56  	return fileInfo{
    57  		size:  int64(stat.Size),
    58  		uid:   int(stat.Uid),
    59  		gid:   int(stat.Gid),
    60  		btime: time.Unix(stat.Btime.Sec, int64(stat.Btime.Nsec)),
    61  		mtime: time.Unix(stat.Mtime.Sec, int64(stat.Mtime.Nsec)),
    62  		ctime: time.Unix(stat.Ctime.Sec, int64(stat.Ctime.Nsec)),
    63  	}, nil
    64  }
    65  
    66  func oldFStat(file statable) (SysInfo, error) {
    67  	var stat unix.Stat_t
    68  	if err := unix.Fstat(int(file.Fd()), &stat); err != nil {
    69  		return nil, fmt.Errorf("failed to stat %s: %w", file.Name(), err)
    70  	}
    71  	return fileInfo{
    72  		size: stat.Size,
    73  		uid:  int(stat.Uid),
    74  		gid:  int(stat.Gid),
    75  		// The reason we wanted statx(2) in the first place is
    76  		// because fstat(2) doesn't give us the birthtime.  Fake it
    77  		// with the changetime.  I'm not sure why changetime is the
    78  		// best choice, but it's what Telepresence did before we
    79  		// added statx support.
    80  		btime: time.Unix(stat.Ctim.Sec, stat.Ctim.Nsec),
    81  		mtime: time.Unix(stat.Mtim.Sec, stat.Mtim.Nsec),
    82  		ctime: time.Unix(stat.Ctim.Sec, stat.Ctim.Nsec),
    83  	}, nil
    84  }
    85  
    86  func (u fileInfo) Size() int64 {
    87  	return u.size
    88  }
    89  
    90  func (u fileInfo) SetOwnerAndGroup(name string) error {
    91  	return os.Chown(name, u.uid, u.gid)
    92  }
    93  
    94  func (u fileInfo) HaveSameOwnerAndGroup(other SysInfo) bool {
    95  	ou := other.(fileInfo)
    96  	return u.uid == ou.uid && u.gid == ou.gid
    97  }
    98  
    99  func (u fileInfo) String() string {
   100  	return fmt.Sprintf("BTIME %v, MTIME %v, CTIME %v, UID %d, GID %d",
   101  		u.btime, u.mtime, u.ctime, u.uid, u.gid)
   102  }
   103  
   104  func (u fileInfo) BirthTime() time.Time  { return u.btime }
   105  func (u fileInfo) ModifyTime() time.Time { return u.mtime }
   106  func (u fileInfo) ChangeTime() time.Time { return u.ctime }