summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/gofer/attr.go
blob: 98700d014d66bcffa2913cbeaed5fcefa3bb2330 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2018 Google LLC
//
// 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.

package gofer

import (
	"syscall"

	"gvisor.googlesource.com/gvisor/pkg/p9"
	"gvisor.googlesource.com/gvisor/pkg/sentry/context"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
	ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
	"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
)

// getattr returns the 9p attributes of the p9.File. On success, Mode, Size, and RDev
// are guaranteed to be masked as valid.
func getattr(ctx context.Context, file contextFile) (p9.QID, p9.AttrMask, p9.Attr, error) {
	// Retrieve attributes over the wire.
	qid, valid, attr, err := file.getAttr(ctx, p9.AttrMaskAll())
	if err != nil {
		return qid, valid, attr, err
	}

	// Require mode, size, and raw device id.
	if !valid.Mode || !valid.Size || !valid.RDev {
		return qid, valid, attr, syscall.EIO
	}

	return qid, valid, attr, nil
}

func unstable(ctx context.Context, valid p9.AttrMask, pattr p9.Attr, mounter fs.FileOwner, client *p9.Client) fs.UnstableAttr {
	return fs.UnstableAttr{
		Size:             int64(pattr.Size),
		Usage:            int64(pattr.Size),
		Perms:            perms(valid, pattr, client),
		Owner:            owner(mounter, valid, pattr),
		AccessTime:       atime(ctx, valid, pattr),
		ModificationTime: mtime(ctx, valid, pattr),
		StatusChangeTime: ctime(ctx, valid, pattr),
		Links:            links(valid, pattr),
	}
}

func perms(valid p9.AttrMask, pattr p9.Attr, client *p9.Client) fs.FilePermissions {
	if pattr.Mode.IsDir() && !p9.VersionSupportsMultiUser(client.Version()) {
		// If user and group permissions bits are not supplied, use
		// "other" bits to supplement them.
		//
		// Older Gofer's fake directories only have "other" permission,
		// but will often be accessed via user or group permissions.
		if pattr.Mode&0770 == 0 {
			other := pattr.Mode & 07
			pattr.Mode = pattr.Mode | other<<3 | other<<6
		}
	}
	return fs.FilePermsFromP9(pattr.Mode)
}

func owner(mounter fs.FileOwner, valid p9.AttrMask, pattr p9.Attr) fs.FileOwner {
	// Unless the file returned its UID and GID, it belongs to the mounting
	// task's EUID/EGID.
	owner := mounter
	if valid.UID {
		owner.UID = auth.KUID(pattr.UID)
	}
	if valid.GID {
		owner.GID = auth.KGID(pattr.GID)
	}
	return owner
}

// bsize returns a block size from 9p attributes.
func bsize(pattr p9.Attr) int64 {
	if pattr.BlockSize > 0 {
		return int64(pattr.BlockSize)
	}
	// Some files may have no clue of their block size. Better not to report
	// something misleading or buggy and have a safe default.
	return usermem.PageSize
}

// ntype returns an fs.InodeType from 9p attributes.
func ntype(pattr p9.Attr) fs.InodeType {
	switch {
	case pattr.Mode.IsNamedPipe():
		return fs.Pipe
	case pattr.Mode.IsDir():
		return fs.Directory
	case pattr.Mode.IsSymlink():
		return fs.Symlink
	case pattr.Mode.IsCharacterDevice():
		return fs.CharacterDevice
	case pattr.Mode.IsBlockDevice():
		return fs.BlockDevice
	case pattr.Mode.IsSocket():
		return fs.Socket
	case pattr.Mode.IsRegular():
		fallthrough
	default:
		return fs.RegularFile
	}
}

// ctime returns a change time from 9p attributes.
func ctime(ctx context.Context, valid p9.AttrMask, pattr p9.Attr) ktime.Time {
	if valid.CTime {
		return ktime.FromUnix(int64(pattr.CTimeSeconds), int64(pattr.CTimeNanoSeconds))
	}
	// Approximate ctime with mtime if ctime isn't available.
	return mtime(ctx, valid, pattr)
}

// atime returns an access time from 9p attributes.
func atime(ctx context.Context, valid p9.AttrMask, pattr p9.Attr) ktime.Time {
	if valid.ATime {
		return ktime.FromUnix(int64(pattr.ATimeSeconds), int64(pattr.ATimeNanoSeconds))
	}
	return ktime.NowFromContext(ctx)
}

// mtime returns a modification time from 9p attributes.
func mtime(ctx context.Context, valid p9.AttrMask, pattr p9.Attr) ktime.Time {
	if valid.MTime {
		return ktime.FromUnix(int64(pattr.MTimeSeconds), int64(pattr.MTimeNanoSeconds))
	}
	return ktime.NowFromContext(ctx)
}

// links returns a hard link count from 9p attributes.
func links(valid p9.AttrMask, pattr p9.Attr) uint64 {
	// For gofer file systems that support link count (such as a local file gofer),
	// we return the link count reported by the underlying file system.
	if valid.NLink {
		return pattr.NLink
	}

	// This node is likely backed by a file system that doesn't support links.
	// We could readdir() and count children directories to provide an accurate
	// link count. However this may be expensive since the gofer may be backed by remote
	// storage. Instead, simply return 2 links for directories and 1 for everything else
	// since no one relies on an accurate link count for gofer-based file systems.
	switch ntype(pattr) {
	case fs.Directory:
		return 2
	default:
		return 1
	}
}