@@ -21,6 +21,8 @@ const _defaultMode = 438; // => 0666 => rw-rw-rw-
21
21
/// The default `mode` to use when creating a directory.
22
22
const _defaultDirectoryMode = 511 ; // => 0777 => rwxrwxrwx
23
23
24
+ const _nanosecondsPerSecond = 1000000000 ;
25
+
24
26
Exception _getError (int err, String message, String path) {
25
27
//TODO(brianquinlan): In the long-term, do we need to avoid exceptions that
26
28
// are part of `dart:io`? Can we move those exceptions into a different
@@ -50,6 +52,147 @@ int _tempFailureRetry(int Function() f) {
50
52
return result;
51
53
}
52
54
55
+ /// Information about a directory, link, etc. stored in the [PosixFileSystem] .
56
+ final class PosixMetadata implements Metadata {
57
+ /// The `st_mode` field of the POSIX stat struct.
58
+ ///
59
+ /// See [stat.h] (https://pubs.opengroup.org/onlinepubs/009696799/basedefs/sys/stat.h.html)
60
+ /// for information on how to interpret this field.
61
+ final int mode;
62
+ final int _flags;
63
+
64
+ @override
65
+ final int size;
66
+
67
+ /// The time that the file system object was last accessed in nanoseconds
68
+ /// since the epoch.
69
+ ///
70
+ /// Access time is updated when the object is read or modified.
71
+ ///
72
+ /// The resolution of the access time varies by platform and file system.
73
+ final int accessedTimeNanos;
74
+
75
+ /// The time that the file system object was created in nanoseconds since the
76
+ /// epoch.
77
+ ///
78
+ /// This will always be `null` on Android and Linux.
79
+ ///
80
+ /// The resolution of the creation time varies by platform and file system.
81
+ final int ? creationTimeNanos;
82
+
83
+ /// The time that the file system object was last modified in nanoseconds
84
+ /// since the epoch.
85
+ ///
86
+ /// The resolution of the modification time varies by platform and file
87
+ /// system.
88
+ final int modificationTimeNanos;
89
+
90
+ int get _fmt => mode & libc.S_IFMT ;
91
+
92
+ @override
93
+ FileSystemType get type {
94
+ if (_fmt == libc.S_IFBLK ) {
95
+ return FileSystemType .block;
96
+ }
97
+ if (_fmt == libc.S_IFCHR ) {
98
+ return FileSystemType .character;
99
+ }
100
+ if (_fmt == libc.S_IFDIR ) {
101
+ return FileSystemType .directory;
102
+ }
103
+ if (_fmt == libc.S_IFREG ) {
104
+ return FileSystemType .file;
105
+ }
106
+ if (_fmt == libc.S_IFLNK ) {
107
+ return FileSystemType .link;
108
+ }
109
+ if (_fmt == libc.S_IFIFO ) {
110
+ return FileSystemType .pipe;
111
+ }
112
+ if (_fmt == libc.S_IFSOCK ) {
113
+ return FileSystemType .socket;
114
+ }
115
+ return FileSystemType .unknown;
116
+ }
117
+
118
+ @override
119
+ bool get isDirectory => type == FileSystemType .directory;
120
+
121
+ @override
122
+ bool get isFile => type == FileSystemType .file;
123
+
124
+ @override
125
+ bool get isLink => type == FileSystemType .link;
126
+
127
+ @override
128
+ DateTime get access =>
129
+ DateTime .fromMicrosecondsSinceEpoch (accessedTimeNanos ~ / 1000 );
130
+
131
+ @override
132
+ DateTime ? get creation =>
133
+ creationTimeNanos == null
134
+ ? null
135
+ : DateTime .fromMicrosecondsSinceEpoch (creationTimeNanos! ~ / 1000 );
136
+
137
+ @override
138
+ DateTime get modification =>
139
+ DateTime .fromMicrosecondsSinceEpoch (modificationTimeNanos ~ / 1000 );
140
+
141
+ @override
142
+ bool ? get isHidden {
143
+ if (io.Platform .isIOS || io.Platform .isMacOS) {
144
+ return _flags & libc.UF_HIDDEN != 0 ;
145
+ }
146
+ return null ;
147
+ }
148
+
149
+ PosixMetadata ._(
150
+ this .mode,
151
+ this ._flags,
152
+ this .size,
153
+ this .accessedTimeNanos,
154
+ this .creationTimeNanos,
155
+ this .modificationTimeNanos,
156
+ );
157
+
158
+ /// Construct [PosixMetadata] from data returned by the `stat` system call.
159
+ factory PosixMetadata .fromFileAttributes ({
160
+ required int mode,
161
+ int flags = 0 ,
162
+ int size = 0 ,
163
+ int accessedTimeNanos = 0 ,
164
+ int ? creationTimeNanos,
165
+ int modificationTimeNanos = 0 ,
166
+ }) => PosixMetadata ._(
167
+ mode,
168
+ flags,
169
+ size,
170
+ accessedTimeNanos,
171
+ creationTimeNanos,
172
+ modificationTimeNanos,
173
+ );
174
+
175
+ @override
176
+ bool operator == (Object other) =>
177
+ other is PosixMetadata &&
178
+ mode == other.mode &&
179
+ _flags == other._flags &&
180
+ size == other.size &&
181
+ accessedTimeNanos == other.accessedTimeNanos &&
182
+ creationTimeNanos == other.creationTimeNanos &&
183
+ modificationTimeNanos == other.modificationTimeNanos;
184
+
185
+ @override
186
+ int get hashCode => Object .hash (
187
+ mode,
188
+ _flags,
189
+ size,
190
+ accessedTimeNanos,
191
+ creationTimeNanos,
192
+ modificationTimeNanos,
193
+ );
194
+ }
195
+
53
196
/// The POSIX `read` function.
54
197
///
55
198
/// See https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html
@@ -112,9 +255,29 @@ final class PosixFileSystem extends FileSystem {
112
255
});
113
256
114
257
@override
115
- Metadata metadata (String path) {
116
- throw UnimplementedError ();
117
- }
258
+ PosixMetadata metadata (String path) => ffi.using ((arena) {
259
+ final stat = arena< libc.Stat > ();
260
+
261
+ if (libc.lstat (path.toNativeUtf8 (allocator: arena).cast (), stat) == - 1 ) {
262
+ final errno = libc.errno;
263
+ throw _getError (errno, 'stat failed' , path);
264
+ }
265
+
266
+ return PosixMetadata .fromFileAttributes (
267
+ mode: stat.ref.st_mode,
268
+ flags: stat.ref.st_flags,
269
+ size: stat.ref.st_size,
270
+ accessedTimeNanos:
271
+ stat.ref.st_atim.tv_sec * _nanosecondsPerSecond +
272
+ stat.ref.st_atim.tv_sec,
273
+ creationTimeNanos:
274
+ stat.ref.st_btime.tv_sec * _nanosecondsPerSecond +
275
+ stat.ref.st_btime.tv_sec,
276
+ modificationTimeNanos:
277
+ stat.ref.st_mtim.tv_sec * _nanosecondsPerSecond +
278
+ stat.ref.st_mtim.tv_sec,
279
+ );
280
+ });
118
281
119
282
@override
120
283
void removeDirectory (String path) => ffi.using ((arena) {
0 commit comments