summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/ext/disklayout/inode.go
blob: 9ab9a49883bee52b2fa9ecb4ff0ce01053f978da (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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
// Copyright 2019 The gVisor Authors.
//
// 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 disklayout

import (
	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
	"gvisor.dev/gvisor/pkg/sentry/kernel/time"
)

// The Inode interface must be implemented by structs representing ext inodes.
// The inode stores all the metadata pertaining to the file (except for the
// file name which is held by the directory entry). It does NOT expose all
// fields and should be extended if need be.
//
// Some file systems (e.g. FAT) use the directory entry to store all this
// information. Ext file systems do not so that they can support hard links.
// However, ext4 cheats a little bit and duplicates the file type in the
// directory entry for performance gains.
//
// See https://www.kernel.org/doc/html/latest/filesystems/ext4/dynamic.html#index-nodes.
type Inode interface {
	// Mode returns the linux file mode which is majorly used to extract
	// information like:
	// - File permissions (read/write/execute by user/group/others).
	// - Sticky, set UID and GID bits.
	// - File type.
	//
	// Masks to extract this information are provided in pkg/abi/linux/file.go.
	Mode() linux.FileMode

	// UID returns the owner UID.
	UID() auth.KUID

	// GID returns the owner GID.
	GID() auth.KGID

	// Size returns the size of the file in bytes.
	Size() uint64

	// InodeSize returns the size of this inode struct in bytes.
	// In ext2 and ext3, the inode struct and inode disk record size was fixed at
	// 128 bytes. Ext4 makes it possible for the inode struct to be bigger.
	// However, accessing any field beyond the 128 bytes marker must be verified
	// using this method.
	InodeSize() uint16

	// AccessTime returns the last access time. Shows when the file was last read.
	//
	// If InExtendedAttr is set, then this should NOT be used because the
	// underlying field is used to store the extended attribute value checksum.
	AccessTime() time.Time

	// ChangeTime returns the last change time. Shows when the file meta data
	// (like permissions) was last changed.
	//
	// If InExtendedAttr is set, then this should NOT be used because the
	// underlying field is used to store the lower 32 bits of the attribute
	// value’s reference count.
	ChangeTime() time.Time

	// ModificationTime returns the last modification time. Shows when the file
	// content was last modified.
	//
	// If InExtendedAttr is set, then this should NOT be used because
	// the underlying field contains the number of the inode that owns the
	// extended attribute.
	ModificationTime() time.Time

	// DeletionTime returns the deletion time. Inodes are marked as deleted by
	// writing to the underlying field. FS tools can restore files until they are
	// actually overwritten.
	DeletionTime() time.Time

	// LinksCount returns the number of hard links to this inode.
	//
	// Normally there is an upper limit on the number of hard links:
	//   - ext2/ext3 = 32,000
	//   - ext4      = 65,000
	//
	// This implies that an ext4 directory cannot have more than 64,998
	// subdirectories because each subdirectory will have a hard link to the
	// directory via the `..` entry. The directory has hard link via the `.` entry
	// of its own. And finally the inode is initiated with 1 hard link (itself).
	//
	// The underlying value is reset to 1 if all the following hold:
	//     - Inode is a directory.
	//     - SbDirNlink is enabled.
	//     - Number of hard links is incremented past 64,999.
	// Hard link value of 1 for a directory would indicate that the number of hard
	// links is unknown because a directory can have minimum 2 hard links (itself
	// and `.` entry).
	LinksCount() uint16

	// Flags returns InodeFlags which represents the inode flags.
	Flags() InodeFlags

	// Data returns the underlying inode.i_block array as a slice so it's
	// modifiable. This field is special and is used to store various kinds of
	// things depending on the filesystem version and inode type. The underlying
	// field name in Linux is a little misleading.
	//   - In ext2/ext3, it contains the block map.
	//   - In ext4, it contains the extent tree root node.
	//   - For inline files, it contains the file contents.
	//   - For symlinks, it contains the link path (if it fits here).
	//
	// See https://www.kernel.org/doc/html/latest/filesystems/ext4/dynamic.html#the-contents-of-inode-i-block.
	Data() []byte
}

// Inode flags. This is not comprehensive and flags which were not used in
// the Linux kernel have been excluded.
const (
	// InSync indicates that all writes to the file must be synchronous.
	InSync = 0x8

	// InImmutable indicates that this file is immutable.
	InImmutable = 0x10

	// InAppend indicates that this file can only be appended to.
	InAppend = 0x20

	// InNoDump indicates that teh dump(1) utility should not dump this file.
	InNoDump = 0x40

	// InNoAccessTime indicates that the access time of this inode must not be
	// updated.
	InNoAccessTime = 0x80

	// InIndex indicates that this directory has hashed indexes.
	InIndex = 0x1000

	// InJournalData indicates that file data must always be written through a
	// journal device.
	InJournalData = 0x4000

	// InDirSync indicates that all the directory entiry data must be written
	// synchronously.
	InDirSync = 0x10000

	// InTopDir indicates that this inode is at the top of the directory hierarchy.
	InTopDir = 0x20000

	// InHugeFile indicates that this is a huge file.
	InHugeFile = 0x40000

	// InExtents indicates that this inode uses extents.
	InExtents = 0x80000

	// InExtendedAttr indicates that this inode stores a large extended attribute
	// value in its data blocks.
	InExtendedAttr = 0x200000

	// InInline indicates that this inode has inline data.
	InInline = 0x10000000

	// InReserved indicates that this inode is reserved for the ext4 library.
	InReserved = 0x80000000
)

// InodeFlags represents all possible combinations of inode flags. It aims to
// cover the bit masks and provide a more user-friendly interface.
type InodeFlags struct {
	Sync         bool
	Immutable    bool
	Append       bool
	NoDump       bool
	NoAccessTime bool
	Index        bool
	JournalData  bool
	DirSync      bool
	TopDir       bool
	HugeFile     bool
	Extents      bool
	ExtendedAttr bool
	Inline       bool
	Reserved     bool
}

// ToInt converts inode flags back to its 32-bit rep.
func (f InodeFlags) ToInt() uint32 {
	var res uint32

	if f.Sync {
		res |= InSync
	}
	if f.Immutable {
		res |= InImmutable
	}
	if f.Append {
		res |= InAppend
	}
	if f.NoDump {
		res |= InNoDump
	}
	if f.NoAccessTime {
		res |= InNoAccessTime
	}
	if f.Index {
		res |= InIndex
	}
	if f.JournalData {
		res |= InJournalData
	}
	if f.DirSync {
		res |= InDirSync
	}
	if f.TopDir {
		res |= InTopDir
	}
	if f.HugeFile {
		res |= InHugeFile
	}
	if f.Extents {
		res |= InExtents
	}
	if f.ExtendedAttr {
		res |= InExtendedAttr
	}
	if f.Inline {
		res |= InInline
	}
	if f.Reserved {
		res |= InReserved
	}

	return res
}

// InodeFlagsFromInt converts the integer representation of inode flags to
// a InodeFlags struct.
func InodeFlagsFromInt(f uint32) InodeFlags {
	return InodeFlags{
		Sync:         f&InSync > 0,
		Immutable:    f&InImmutable > 0,
		Append:       f&InAppend > 0,
		NoDump:       f&InNoDump > 0,
		NoAccessTime: f&InNoAccessTime > 0,
		Index:        f&InIndex > 0,
		JournalData:  f&InJournalData > 0,
		DirSync:      f&InDirSync > 0,
		TopDir:       f&InTopDir > 0,
		HugeFile:     f&InHugeFile > 0,
		Extents:      f&InExtents > 0,
		ExtendedAttr: f&InExtendedAttr > 0,
		Inline:       f&InInline > 0,
		Reserved:     f&InReserved > 0,
	}
}

// These masks define how users can view/modify inode flags. The rest of the
// flags are for internal kernel usage only.
const (
	InUserReadFlagMask  = 0x4BDFFF
	InUserWriteFlagMask = 0x4B80FF
)