jaulib v1.3.0
Jau Support Library (C++, Java, ..)
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
boolean equals(final Object other)
Definition: DirItem.java:87
String basename()
Return the basename, shall not be empty nor contain a dirname.
Definition: DirItem.java:63
String dirname()
Returns the dirname, shall not be empty and denotes .
Definition: DirItem.java:60
String path()
Returns a full unix path representation combining dirname() and basename().
Definition: DirItem.java:68
Generic file type and POSIX protection mode bits as used in file_stats, touch(), mkdir() etc.
Definition: FMode.java:37
boolean isSet(final Bit bit)
Definition: FMode.java:123
boolean equals(final Object other)
Definition: FMode.java:149
FMode set(final Bit bit)
Sets the given bit and returns this instance for chaining.
Definition: FMode.java:130
boolean equals(final Object other)
Definition: FileStats.java:94
boolean isSet(final Type bit)
Definition: FileStats.java:74
Platform agnostic representation of POSIX ::lstat() and ::stat() for a given pathname.
Definition: FileStats.java:41
boolean equals(final Object other)
Definition: FileStats.java:145
int fd()
Returns the file descriptor if has_fd(), otherwise -1 for no file descriptor.
Definition: FileStats.java:328
String path()
Returns the unix path representation.
Definition: FileStats.java:261
Field fields()
Returns the retrieved field_t fields.
Definition: FileStats.java:312
boolean is_file()
Returns true if entity is a file, might be in combination with is_link().
Definition: FileStats.java:386
Instant mtime()
Returns the last modification time of this element since Unix Epoch.
Definition: FileStats.java:350
FileStats(final DirItem item)
Instantiates a file_stats for the given dir_item.
Definition: FileStats.java:186
Instant ctime()
Returns the last status change time of this element since Unix Epoch.
Definition: FileStats.java:348
boolean ok()
Returns true if no error occurred.
Definition: FileStats.java:356
boolean exists()
Returns true if entity does not exist, exclusive bit.
Definition: FileStats.java:395
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...
Definition: FileStats.java:293
boolean has_access()
Returns true if entity gives no access to user, exclusive bit.
Definition: FileStats.java:392
FMode prot_mode()
Returns the POSIX protection bit portion of fmode_t, i.e.
Definition: FileStats.java:318
FileStats(final int fd)
Instantiates a file_stats for the given fd file descriptor.
Definition: FileStats.java:195
int gid()
Returns the group id, owning the element.
Definition: FileStats.java:334
boolean is_link()
Returns true if entity is a symbolic link, might be in combination with is_file(),...
Definition: FileStats.java:389
String link_target_path()
Returns the stored link-target path this symbolic-link points to if instance is a symbolic-link,...
Definition: FileStats.java:270
DirItem item()
Returns the dir_item.
Definition: FileStats.java:250
FileStats link_target()
Returns the link-target this symbolic-link points to if instance is a symbolic-link,...
Definition: FileStats.java:281
FileStats()
Instantiate an empty file_stats with fmode_t::not_existing set.
Definition: FileStats.java:123
boolean is_fifo()
Returns true if entity is a fifo/pipe, might be in combination with is_link().
Definition: FileStats.java:380
FMode mode()
Returns the FMode, file type and mode.
Definition: FileStats.java:315
boolean is_socket()
Returns true if entity is a socket, might be in combination with is_link().
Definition: FileStats.java:371
boolean has_fd()
Returns true if entity has a file descriptor.
Definition: FileStats.java:368
Instant btime()
Returns the birth time of this element since Unix Epoch, i.e.
Definition: FileStats.java:344
boolean is_block()
Returns true if entity is a block device, might be in combination with is_link().
Definition: FileStats.java:374
boolean is_dir()
Returns true if entity is a directory, might be in combination with is_link().
Definition: FileStats.java:383
Instant atime()
Returns the last access time of this element since Unix Epoch.
Definition: FileStats.java:346
boolean has(final Field.Type bit)
Returns true if the given field_t fields were retrieved, otherwise false.
Definition: FileStats.java:309
FMode type_mode()
Returns the type bit portion of fmode_t, i.e.
Definition: FileStats.java:321
int uid()
Returns the user id, owning the element.
Definition: FileStats.java:331
boolean is_char()
Returns true if entity is a character device, might be in combination with is_link().
Definition: FileStats.java:377
int errno_res()
Returns the errno value occurred to produce this instance, or zero for no error.
Definition: FileStats.java:353
long size()
Returns the size in bytes of this element if is_file(), otherwise zero.
Definition: FileStats.java:341
FileStats(final String path)
Instantiates a file_stats for the given path.
Definition: FileStats.java:177
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
type
File type mode bits.
Definition: FileStats.java:47
mode
POSIX file protection mode bits.
Definition: FileStats.java:49