@@ -21,6 +21,8 @@ const _defaultMode = 438; // => 0666 => rw-rw-rw-
2121/// The default `mode` to use when creating a directory.
2222const _defaultDirectoryMode = 511 ; // => 0777 => rwxrwxrwx
2323
24+ const _nanosecondsPerSecond = 1000000000 ;
25+
2426Exception _getError (int err, String message, String path) {
2527 //TODO(brianquinlan): In the long-term, do we need to avoid exceptions that
2628 // are part of `dart:io`? Can we move those exceptions into a different
@@ -50,6 +52,147 @@ int _tempFailureRetry(int Function() f) {
5052 return result;
5153}
5254
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+
53196/// The POSIX `read` function.
54197///
55198/// See https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html
@@ -112,9 +255,29 @@ final class PosixFileSystem extends FileSystem {
112255 });
113256
114257 @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+ });
118281
119282 @override
120283 void removeDirectory (String path) => ffi.using ((arena) {
0 commit comments