jaulib v1.3.6
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
FileStats.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2022 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24package org.jau.fs;
25
26import java.time.Instant;
27import java.time.ZoneOffset;
28
29/**
30 * Platform agnostic representation of POSIX ::lstat() and ::stat()
31 * for a given pathname.
32 *
33 * Implementation follows the symbolic link, i.e. first opens
34 * the given pathname with ::lstat() and if identifying as a symbolic link
35 * opens it via ::stat() to retrieve the actual properties like size, time and ownership.
36 *
37 * Implementation supports named file descriptor, see is_fd().
38 *
39 * On `GNU/Linux` implementation uses ::statx().
40 */
41public class FileStats {
42 public static class Field {
43 public enum Type {
44 /** No mode bit set */
45 none ( 0 ),
46 /** File type mode bits */
47 type ( 0b0000000000000001 ),
48 /** POSIX file protection mode bits */
49 mode ( 0b0000000000000010 ),
50 nlink ( 0b0000000000000100 ),
51 uid ( 0b0000000000001000 ),
52 gid ( 0b0000000000010000 ),
53 atime ( 0b0000000000100000 ),
54 mtime ( 0b0000000001000000 ),
55 ctime ( 0b0000000010000000 ),
56 ino ( 0b0000000100000000 ),
57 size ( 0b0000001000000000 ),
58 blocks ( 0b0000010000000000 ),
59 btime ( 0b0000100000000000 ),
60 fd ( 0b0001000000000000 );
61
62 Type(final int v) { value = v; }
63 public final int value;
64 }
65 public int mask;
66
67 public Field(final int v) {
68 mask = v;
69 }
70 public Field() {
71 mask = 0;
72 }
73
74 public boolean isSet(final Type bit) { return bit.value == ( mask & bit.value ); }
75 public void set(final Type bit) { mask = (short) ( mask | bit.value ); }
76
77 @Override
78 public String toString() {
79 int count = 0;
80 final StringBuilder out = new StringBuilder();
81 for (final Type dt : Type.values()) {
82 if( isSet(dt) ) {
83 if( 0 < count ) { out.append(", "); }
84 out.append(dt.name()); count++;
85 }
86 }
87 if( 1 < count ) {
88 out.insert(0, "[");
89 out.append("]");
90 }
91 return out.toString();
92 }
93 @Override
94 public boolean equals(final Object other) {
95 if (this == other) {
96 return true;
97 }
98 return (other instanceof Field) &&
99 this.mask == ((Field)other).mask;
100 }
101 }
102
103 private final DirItem item_;
104 private final String link_target_path_; // stored link-target path this symbolic-link points to if is_link(), otherwise nullptr.
105
106 private final Field has_fields_;
107 private final FMode mode_;
108 private final int fd_;
109 private final int uid_;
110 private final int gid_;
111 private final int errno_res_;
112
113 private final long size_;
114 // final ZonedDateTime utc = btime_.atZone(ZoneOffset.UTC); String s = utc.toString();
115 private final Instant btime_; // Birth or creation time
116 private final Instant atime_; // Last access
117 private final Instant ctime_; // Last meta-status change
118 private final Instant mtime_; // Last modification
119
120 private FileStats link_target_; // link-target this symbolic-link points to if is_link(), otherwise nullptr.
121
122 /** Instantiate an empty file_stats with fmode_t::not_existing set. */
123 public FileStats() {
124 item_ = new DirItem();
125 link_target_path_ = null;
126
127 has_fields_ = new Field();
128 mode_ = new FMode();
129 mode_.set(FMode.Bit.not_existing);
130 fd_ = -1;
131 uid_ = 0;
132 gid_ = 0;
133 errno_res_ = 0;
134
135 size_ = 0;
136 btime_ = Instant.ofEpochSecond(0, 0);
137 atime_ = btime_;
138 ctime_ = btime_;
139 mtime_ = btime_;
140
141 link_target_ = null;
142 }
143
144 @Override
145 public boolean equals(final Object other) {
146 if (this == other) {
147 return true;
148 }
149 if( other instanceof FileStats ) {
150 final FileStats o = (FileStats)other;
151 return item_.equals( o.item_ ) &&
152 has_fields_.equals(o.has_fields_) &&
153 mode_.equals(o.mode_) &&
154 uid_ == o.uid_ && gid_ == o.gid_ &&
155 errno_res_ == o.errno_res_ &&
156 size_ == o.size_ &&
157 btime_.equals( o.btime_ ) &&
158 atime_.equals( o.atime_ ) &&
159 ctime_.equals( o.ctime_ ) &&
160 mtime_.equals( o.mtime_ ) &&
161 ( !is_link() ||
162 ( link_target_path_.equals(o.link_target_path_) &&
163 link_target_.equals(o.link_target_)
164 )
165 );
166 } else {
167 return false;
168 }
169 }
170
171 /**
172 * Instantiates a file_stats for the given `path`.
173 *
174 * The dir_item will be constructed without parent_dir
175 * @param path the path to produce stats for
176 */
177 public FileStats(final String path) {
178 this( (byte)0, ctorImpl1(path) );
179 }
180
181 /**
182 * Instantiates a file_stats for the given dir_item.
183 *
184 * @param item the dir_item to produce stats for
185 */
186 public FileStats(final DirItem item) {
187 this( (byte)0, ctorImpl2(item.dirname(), item.basename()) );
188 }
189
190 /**
191 * Instantiates a file_stats for the given `fd` file descriptor.
192 *
193 * @param fd file descriptor of an opened file
194 */
195 public FileStats(final int fd) {
196 this( (byte)0, ctorImpl3(fd) );
197 }
198
199 private FileStats(final byte dummy, final long h) { // use dummy to avoid override with public fd ctor
200 {
201 final String[/*3*/] s3 = getString3DirItemLinkTargetPath(h);
202 item_ = new DirItem(s3[0], s3[1]);
203 link_target_path_ = s3[2];
204 }
205 {
206 final int[/*6*/] i6 = getInt6FieldsFModeFdUidGidErrno(h);
207 has_fields_ = new Field(i6[0]);
208 mode_ = new FMode(i6[1]);
209 fd_ = i6[2];
210 uid_ = i6[3];
211 gid_ = i6[4];
212 errno_res_ = i6[5];
213 }
214 {
215 final long[/*1+ 2*4*/] l9 = getLong9SizeTimes(h);
216 size_ = l9[0];
217 btime_ = Instant.ofEpochSecond(l9[1+0*2+0], l9[1+0*2+1]);
218 atime_ = Instant.ofEpochSecond(l9[1+1*2+0], l9[1+1*2+1]);
219 ctime_ = Instant.ofEpochSecond(l9[1+2*2+0], l9[1+2*2+1]);
220 mtime_ = Instant.ofEpochSecond(l9[1+3*2+0], l9[1+3*2+1]);
221 }
222 {
223 final long lth = ctorLinkTargetImpl(h);
224 if( lth == 0 ) {
225 link_target_ = null;
226 } else {
227 link_target_ = new FileStats( (byte)0, lth );
228 }
229 }
230 dtorImpl(h);
231 }
232 private static native long ctorImpl1(final String path);
233 private static native long ctorImpl2(final String dirname, final String basename);
234 private static native long ctorImpl3(final int fd);
235 private static native void dtorImpl(final long h);
236 private static native int[/*6*/] getInt6FieldsFModeFdUidGidErrno(final long h);
237 private static native String[/*3*/] getString3DirItemLinkTargetPath(final long h);
238 private static native long[/*1+ 2*4*/] getLong9SizeTimes(final long h);
239 private static native long ctorLinkTargetImpl(final long h);
240
241 /**
242 * Returns the dir_item.
243 *
244 * In case this instance is created by following a symbolic link instance,
245 * it represents the resolved path relative to the used symbolic link's dirname.
246 *
247 * @see is_link()
248 * @see path()
249 */
250 public DirItem item() { return item_; }
251
252 /**
253 * Returns the unix path representation.
254 *
255 * In case this instance is created by following a symbolic link instance,
256 * it represents the resolved path relative to the used symbolic link's dirname.
257 *
258 * @see is_link()
259 * @see item()
260 */
261 public String path() { return item_.path(); }
262
263 /**
264 * Returns the stored link-target path this symbolic-link points to if instance is a symbolic-link, otherwise nullptr.
265 *
266 * @see is_link()
267 * @see link_target()
268 * @see final_target()
269 */
270 public String link_target_path() { return link_target_path_; }
271
272 /**
273 * Returns the link-target this symbolic-link points to if instance is a symbolic-link, otherwise nullptr.
274 *
275 * nullptr is also returned for an erroneous symbolic-links, i.e. non-existing link-targets or recursive loop-errors.
276 *
277 * @see is_link()
278 * @see link_target_path()
279 * @see final_target()
280 */
281 public FileStats link_target() { return link_target_; }
282
283 /**
284 * Returns the final target element, either a pointer to this instance if not a symbolic-link
285 * or the final link-target a symbolic-link (chain) points to.
286 *
287 * @param link_count optional size_t pointer to store the number of symbolic links leading to the final target, excluding the final instance. 0 indicates no symbolic-link;
288 *
289 * @see is_link()
290 * @see link_target_path()
291 * @see link_target()
292 */
293 public FileStats final_target(final long link_count[]) {
294 long count = 0;
295 FileStats fs0 = this;
296 FileStats fs1 = fs0.link_target();
297 while( null != fs1 ) {
298 ++count;
299 fs0 = fs1;
300 fs1 = fs0.link_target();
301 }
302 if( null != link_count && link_count.length > 0 ) {
303 link_count[0] = count;
304 }
305 return fs0;
306 }
307
308 /** Returns true if the given field_t fields were retrieved, otherwise false. */
309 public boolean has(final Field.Type bit) { return has_fields_.isSet(bit); }
310
311 /** Returns the retrieved field_t fields. */
312 public Field fields() { return has_fields_; }
313
314 /** Returns the FMode, file type and mode. */
315 public FMode mode() { return mode_; }
316
317 /** Returns the POSIX protection bit portion of fmode_t, i.e. mode() & {@link FMode.Bit#protection_mask}. */
318 public FMode prot_mode() { return mode_.mask(FMode.Bit.protection_mask.value); }
319
320 /** Returns the type bit portion of fmode_t, i.e. mode() & fmode_t::type_mask. */
321 public FMode type_mode() { return mode_.mask(FMode.Bit.type_mask.value); }
322
323 /**
324 * Returns the file descriptor if has_fd(), otherwise -1 for no file descriptor.
325 *
326 * @see has_fd()
327 */
328 public int fd() { return fd_; }
329
330 /** Returns the user id, owning the element. */
331 public int uid() { return uid_; }
332
333 /** Returns the group id, owning the element. */
334 public int gid() { return gid_; }
335
336 /**
337 * Returns the size in bytes of this element if is_file(), otherwise zero.
338 *
339 * If the element also is_link(), the linked target size is returned.
340 */
341 public long size() { return size_; }
342
343 /** Returns the birth time of this element since Unix Epoch, i.e. its creation time. */
344 public Instant btime() { return btime_; }
345 /** Returns the last access time of this element since Unix Epoch. */
346 public Instant atime() { return atime_; }
347 /** Returns the last status change time of this element since Unix Epoch. */
348 public Instant ctime() { return ctime_; }
349 /** Returns the last modification time of this element since Unix Epoch. */
350 public Instant mtime() { return mtime_; }
351
352 /** Returns the `errno` value occurred to produce this instance, or zero for no error. */
353 public int errno_res() { return errno_res_; }
354
355 /** Returns true if no error occurred */
356 public boolean ok() { return 0 == errno_res_; }
357
358 /**
359 * Returns true if entity has a file descriptor.
360 *
361 * Detected named file descriptors are (`%d` stands for integer)
362 * - `/dev/fd/%d` (GNU/Linux, FreeBSD, ..)
363 * - `/proc/self/fd/%d` (GNU/Linux)
364 *
365 * @see fd()
366 * @see FileUtil#to_named_fd(int)
367 */
368 public boolean has_fd() { return 0 <= fd_; }
369
370 /** Returns true if entity is a socket, might be in combination with is_link(). */
371 public boolean is_socket() { return mode_.isSet( FMode.Bit.sock ); }
372
373 /** Returns true if entity is a block device, might be in combination with is_link(). */
374 public boolean is_block() { return mode_.isSet( FMode.Bit.blk ); }
375
376 /** Returns true if entity is a character device, might be in combination with is_link(). */
377 public boolean is_char() { return mode_.isSet( FMode.Bit.chr ); }
378
379 /** Returns true if entity is a fifo/pipe, might be in combination with is_link(). */
380 public boolean is_fifo() { return mode_.isSet( FMode.Bit.fifo ); }
381
382 /** Returns true if entity is a directory, might be in combination with is_link(). */
383 public boolean is_dir() { return mode_.isSet( FMode.Bit.dir ); }
384
385 /** Returns true if entity is a file, might be in combination with is_link(). */
386 public boolean is_file() { return mode_.isSet( FMode.Bit.file ); }
387
388 /** Returns true if entity is a symbolic link, might be in combination with is_file(), is_dir(), is_fifo(), is_char(), is_block(), is_socket(). */
389 public boolean is_link() { return mode_.isSet( FMode.Bit.link ); }
390
391 /** Returns true if entity gives no access to user, exclusive bit. */
392 public boolean has_access() { return !mode_.isSet( FMode.Bit.no_access); }
393
394 /** Returns true if entity does not exist, exclusive bit. */
395 public boolean exists() { return !mode_.isSet( FMode.Bit.not_existing ); }
396
397 @Override
398 public String toString() {
399 final String stored_path, link_detail;
400 {
401 if( null != link_target_path_ ) {
402 stored_path = " [-> "+link_target_path_+"]";
403 } else {
404 stored_path = new String();
405 }
406 final long link_count[] = { 0 };
407 final FileStats final_target_ = final_target(link_count);
408 if( 0 < link_count[0] ) {
409 link_detail = " -(" + link_count[0] + ")-> '" + final_target_.path() + "'";
410 } else {
411 link_detail = new String();
412 }
413 }
414 final StringBuilder res = new StringBuilder( "file_stats[");
415 res.append(mode_)
416 .append(", '"+item_.path()+"'"+stored_path+link_detail );
417 if( 0 == errno_res_ ) {
418 if( has( Field.Type.uid ) ) {
419 res.append( ", uid " ).append( uid_ );
420 }
421 if( has( Field.Type.gid ) ) {
422 res.append( ", gid " ).append( gid_ );
423 }
424 if( has( Field.Type.size ) ) {
425 res.append( ", size " ).append( String.format("%,d", size_ ) );
426 }
427 if( has( Field.Type.btime ) ) {
428 res.append( ", btime " ).append( btime_.atZone(ZoneOffset.UTC) );
429 }
430 if( has( Field.Type.atime ) ) {
431 res.append( ", atime " ).append( atime_.atZone(ZoneOffset.UTC) );
432 }
433 if( has( Field.Type.ctime ) ) {
434 res.append( ", ctime " ).append( ctime_.atZone(ZoneOffset.UTC) );
435 }
436 if( has( Field.Type.mtime ) ) {
437 res.append( ", mtime " ).append( mtime_.atZone(ZoneOffset.UTC) );
438 }
439 // res.append( ", fields ").append( jau::fs::to_string( has_fields_ ) );
440 } else {
441 res.append( ", errno " ).append( errno_res_ );
442 }
443 res.append("]");
444 return res.toString();
445 }
446}
Representing a directory item split into dirname() and basename().
Definition DirItem.java:29
Generic file type and POSIX protection mode bits as used in file_stats, touch(), mkdir() etc.
Definition FMode.java:37
boolean equals(final Object other)
boolean isSet(final Type bit)
Platform agnostic representation of POSIX ::lstat() and ::stat() for a given pathname.
boolean equals(final Object other)
int fd()
Returns the file descriptor if has_fd(), otherwise -1 for no file descriptor.
String path()
Returns the unix path representation.
Field fields()
Returns the retrieved field_t fields.
boolean is_file()
Returns true if entity is a file, might be in combination with is_link().
Instant mtime()
Returns the last modification time of this element since Unix Epoch.
FileStats(final DirItem item)
Instantiates a file_stats for the given dir_item.
Instant ctime()
Returns the last status change time of this element since Unix Epoch.
boolean ok()
Returns true if no error occurred.
boolean exists()
Returns true if entity does not exist, exclusive bit.
FileStats final_target(final long link_count[])
Returns the final target element, either a pointer to this instance if not a symbolic-link or the fin...
boolean has_access()
Returns true if entity gives no access to user, exclusive bit.
FMode prot_mode()
Returns the POSIX protection bit portion of fmode_t, i.e.
FileStats(final int fd)
Instantiates a file_stats for the given fd file descriptor.
int gid()
Returns the group id, owning the element.
boolean is_link()
Returns true if entity is a symbolic link, might be in combination with is_file(),...
String link_target_path()
Returns the stored link-target path this symbolic-link points to if instance is a symbolic-link,...
DirItem item()
Returns the dir_item.
FileStats link_target()
Returns the link-target this symbolic-link points to if instance is a symbolic-link,...
FileStats()
Instantiate an empty file_stats with fmode_t::not_existing set.
boolean is_fifo()
Returns true if entity is a fifo/pipe, might be in combination with is_link().
FMode mode()
Returns the FMode, file type and mode.
boolean is_socket()
Returns true if entity is a socket, might be in combination with is_link().
boolean has_fd()
Returns true if entity has a file descriptor.
Instant btime()
Returns the birth time of this element since Unix Epoch, i.e.
boolean is_block()
Returns true if entity is a block device, might be in combination with is_link().
boolean is_dir()
Returns true if entity is a directory, might be in combination with is_link().
Instant atime()
Returns the last access time of this element since Unix Epoch.
boolean has(final Field.Type bit)
Returns true if the given field_t fields were retrieved, otherwise false.
FMode type_mode()
Returns the type bit portion of fmode_t, i.e.
int uid()
Returns the user id, owning the element.
boolean is_char()
Returns true if entity is a character device, might be in combination with is_link().
int errno_res()
Returns the errno value occurred to produce this instance, or zero for no error.
long size()
Returns the size in bytes of this element if is_file(), otherwise zero.
FileStats(final String path)
Instantiates a file_stats for the given path.
file
Type: Entity is a file ), might be in combination with link.
Definition FMode.java:101
protection_mask
12 bit protection bit mask 07777 for rwx_all | set_uid | set_gid | sticky .
Definition FMode.java:88
dir
Type: Entity is a directory ), might be in combination with link.
Definition FMode.java:99
not_existing
Type: Entity does not exist ), exclusive bit.
Definition FMode.java:107
fifo
Type: Entity is a fifo/pipe ), might be in combination with link.
Definition FMode.java:97
blk
Type: Entity is a block device ), might be in combination with link.
Definition FMode.java:93
type_mask
Type mask for sock | blk | chr | fifo | dir | file | link | no_access | not_existing.
Definition FMode.java:109
link
Type: Entity is a symbolic link ), might be in combination with file or dir ), fifo ),...
Definition FMode.java:103
chr
Type: Entity is a character device ), might be in combination with link.
Definition FMode.java:95
no_access
Type: Entity gives no access to user ), exclusive bit.
Definition FMode.java:105
sock
Type: Entity is a socket, might be in combination with link.
Definition FMode.java:91
mode
POSIX file protection mode bits.