2 Copyright © 1995-2013, The AROS Development Team. All rights reserved.
8 #include <proto/exec.h>
19 #include <aros/debug.h>
21 /* 2922 is the number of days between 1.1.1970 and 1.1.1978 (2 leap
22 years and 6 normal). The former number is the start value
23 for time(), the latter the start time for the AmigaOS
26 #define OFFSET_FROM_1970 2922*24*60*60
28 static mode_t
__prot_a2u(ULONG protect
);
29 static uid_t
__id_a2u(UWORD id
);
30 static void hashlittle2(const void *key
, size_t length
,
31 uint32_t *pc
, uint32_t *pb
);
32 static void __fill_statbuffer(
35 struct FileInfoBlock
*fib
,
36 int fallback_to_defaults
,
39 int __stat(BPTR lock
, struct stat
*sb
, BOOL filehandle
)
41 struct FileInfoBlock
*fib
;
44 int fallback_to_defaults
= 0;
47 fib
= AllocDosObject(DOS_FIB
, NULL
);
51 errno
= __stdc_ioerr2errno(IoErr());
57 ? ExamineFH(lock
, fib
)
61 if(IoErr() == ERROR_NOT_IMPLEMENTED
||
62 IoErr() == ERROR_ACTION_NOT_KNOWN
)
64 fallback_to_defaults
= 1;
68 errno
= __stdc_ioerr2errno(IoErr());
69 FreeDosObject(DOS_FIB
, fib
);
74 /* Get the full path of the stated filesystem object and use it to
80 if(!(buffer
= AllocVec(buffersize
, MEMF_ANY
)))
83 FreeDosObject(DOS_FIB
, fib
);
88 ? NameFromFH(lock
, buffer
, buffersize
)
89 : NameFromLock(lock
, buffer
, buffersize
);
92 else if( IoErr() == ERROR_OBJECT_IN_USE
93 || IoErr() == ERROR_NOT_IMPLEMENTED
94 || IoErr() == ERROR_ACTION_NOT_KNOWN
95 || (IoErr() == ERROR_OBJECT_NOT_FOUND
&& fib
->fib_EntryType
== ST_PIPEFILE
))
97 /* We can't retrieve name because lock is an exclusive lock
98 or Examine is not implemented in this handler
99 or the lock refers to an XPIPE: file having always empty name */
103 else if(IoErr() != ERROR_LINE_TOO_LONG
)
105 errno
= __stdc_ioerr2errno(IoErr());
106 FreeDosObject(DOS_FIB
, fib
);
115 // We need a FileLock. Otherwise a call of Info() within __fill_statbuffer() will crash
116 // FIXME: how can we get a lock on an exclusive file?
119 BPTR filelock
= DupLockFromFH(lock
);
120 __fill_statbuffer(sb
, (char*) buffer
, fib
, fallback_to_defaults
, filelock
);
125 __fill_statbuffer(sb
, (char*) buffer
, fib
, fallback_to_defaults
, lock
);
129 FreeDosObject(DOS_FIB
, fib
);
135 int __stat_from_path(const char *path
, struct stat
*sb
)
139 int cwdsize
= FILENAME_MAX
;
141 char *abspath
= NULL
;
142 char *filepart
= NULL
;
144 struct FileInfoBlock
*fib
= NULL
;
147 int fallback_to_defaults
= 0;
151 /* copy path and strip trailing slash */
153 if (!(mypath
= AllocVec(len
+ 1, MEMF_ANY
)))
158 strcpy(mypath
, path
);
159 if (len
&& mypath
[len
-1] == '/')
160 mypath
[len
-1] = '\0';
162 /* do we have an absolute path */
163 if (!strchr(mypath
, ':'))
165 /* no, then create one */
166 cwdlock
= CurrentDir(BNULL
);
170 if (!(cwd
= AllocVec(cwdsize
, MEMF_ANY
)))
176 if (NameFromLock(cwdlock
, cwd
, cwdsize
))
178 else if (IoErr() != ERROR_LINE_TOO_LONG
)
180 errno
= __stdc_ioerr2errno(IoErr());
189 /* get memory for current dir + '/' + input path + zero byte */
190 len
= strlen(cwd
) + 1 + len
+ 1;
191 abspath
= AllocVec(len
, MEMF_ANY
);
198 strcpy(abspath
, cwd
);
199 AddPart(abspath
, mypath
, len
);
205 /* split into path part and file part */
206 split
= FilePart(abspath
);
207 filepart
= AllocVec(strlen(split
) + 1, MEMF_ANY
);
213 strcpy(filepart
, split
);
216 if ( !(fib
= AllocDosObject(DOS_FIB
, NULL
))
217 || !(lock
= Lock(abspath
, SHARED_LOCK
)))
219 errno
= __stdc_ioerr2errno(IoErr());
223 /* examine parent directory of object to stat */
224 if (!Examine(lock
, fib
))
226 if (IoErr() == ERROR_NOT_IMPLEMENTED
||
227 IoErr() == ERROR_ACTION_NOT_KNOWN
)
228 fallback_to_defaults
= 1;
231 errno
= __stdc_ioerr2errno(IoErr());
236 if (*filepart
== '\0' || fallback_to_defaults
)
237 __fill_statbuffer(sb
, abspath
, fib
, fallback_to_defaults
, lock
);
239 /* examine entries of parent directory until we find the object to stat */
242 loop
= ExNext(lock
, fib
);
246 if (stricmp(fib
->fib_FileName
, filepart
) == 0)
248 __fill_statbuffer(sb
, abspath
, fib
, 0, lock
);
256 if (IoErr() != ERROR_NO_MORE_ENTRIES
)
257 errno
= __stdc_ioerr2errno(IoErr());
259 /* nothing found to stat */
270 /* if we had absolute path as input, mypath is free'd here */
276 FreeDosObject(DOS_FIB
, fib
);
282 static mode_t
__prot_a2u(ULONG protect
)
286 if ((protect
& FIBF_SCRIPT
))
288 /* The following three flags are low-active! */
289 if (!(protect
& FIBF_EXECUTE
))
291 if (!(protect
& FIBF_WRITE
))
293 if (!(protect
& FIBF_READ
))
295 if ((protect
& FIBF_GRP_EXECUTE
))
297 if ((protect
& FIBF_GRP_WRITE
))
299 if ((protect
& FIBF_GRP_READ
))
301 if ((protect
& FIBF_OTR_EXECUTE
))
303 if ((protect
& FIBF_OTR_WRITE
))
305 if ((protect
& FIBF_OTR_READ
))
312 static uid_t
__id_a2u(UWORD id
)
330 /* The hash function code below is adapted from Bob Jenkins public domain hash
331 function, see http://burtleburtle.net/bob/hash/doobs.html for details */
334 # define HASH_LITTLE_ENDIAN 0
336 # define HASH_LITTLE_ENDIAN 1
339 #define hashsize(n) ((uint32_t)1<<(n))
340 #define hashmask(n) (hashsize(n)-1)
341 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
345 a -= c; a ^= rot(c, 4); c += b; \
346 b -= a; b ^= rot(a, 6); a += c; \
347 c -= b; c ^= rot(b, 8); b += a; \
348 a -= c; a ^= rot(c,16); c += b; \
349 b -= a; b ^= rot(a,19); a += c; \
350 c -= b; c ^= rot(b, 4); b += a; \
353 #define final(a,b,c) \
355 c ^= b; c -= rot(b,14); \
356 a ^= c; a -= rot(c,11); \
357 b ^= a; b -= rot(a,25); \
358 c ^= b; c -= rot(b,16); \
359 a ^= c; a -= rot(c,4); \
360 b ^= a; b -= rot(a,14); \
361 c ^= b; c -= rot(b,24); \
365 * hashlittle2() -- hash a variable-length key into two 32-bit values
366 * k : the key (the unaligned variable-length array of bytes)
367 * length : the length of the key, counting by bytes
368 * pc : IN: primary initval, OUT: primary hash
369 * pb : IN: secondary initval, OUT: secondary hash
371 * Returns two 32-bit hash values. This is good enough for hash table
372 * lookup with 2^^64 buckets, or if you want a second hash if you're not
373 * happy with the first, or if you want a probably-unique 64-bit ID for
374 * the key. *pc is better mixed than *pb, so use *pc first. If you want
375 * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
377 static void hashlittle2(
378 const void *key
, /* the key to hash */
379 size_t length
, /* length of the key */
380 uint32_t *pc
, /* IN: primary initval, OUT: primary hash */
381 uint32_t *pb
) /* IN: secondary initval, OUT: secondary hash */
383 uint32_t a
,b
,c
; /* internal state */
384 union { const void *ptr
; size_t i
; } u
; /* needed for Mac Powerbook G4 */
386 /* Set up the internal state */
387 a
= b
= c
= 0xdeadbeef + ((uint32_t)length
) + *pc
;
391 if (HASH_LITTLE_ENDIAN
&& ((u
.i
& 0x3) == 0)) {
392 const uint32_t *k
= (const uint32_t *)key
; /* read 32-bit chunks */
397 /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
408 /*----------------------------- handle the last (probably partial) block */
410 * "k[2]&0xffffff" actually reads beyond the end of the string, but
411 * then masks off the part it's not allowed to read. Because the
412 * string is aligned, the masked-off tail is in the same word as the
413 * rest of the string. Every machine with memory protection I've seen
414 * does it on word boundaries, so is OK with this. But VALGRIND will
415 * still catch it and complain. The masking trick does make the hash
416 * noticably faster for short strings (like English words).
422 case 12: c
+=k
[2]; b
+=k
[1]; a
+=k
[0]; break;
423 case 11: c
+=k
[2]&0xffffff; b
+=k
[1]; a
+=k
[0]; break;
424 case 10: c
+=k
[2]&0xffff; b
+=k
[1]; a
+=k
[0]; break;
425 case 9 : c
+=k
[2]&0xff; b
+=k
[1]; a
+=k
[0]; break;
426 case 8 : b
+=k
[1]; a
+=k
[0]; break;
427 case 7 : b
+=k
[1]&0xffffff; a
+=k
[0]; break;
428 case 6 : b
+=k
[1]&0xffff; a
+=k
[0]; break;
429 case 5 : b
+=k
[1]&0xff; a
+=k
[0]; break;
430 case 4 : a
+=k
[0]; break;
431 case 3 : a
+=k
[0]&0xffffff; break;
432 case 2 : a
+=k
[0]&0xffff; break;
433 case 1 : a
+=k
[0]&0xff; break;
434 case 0 : *pc
=c
; *pb
=b
; return; /* zero length strings require no mixing */
437 #else /* make valgrind happy */
439 k8
= (const uint8_t *)k
;
442 case 12: c
+=k
[2]; b
+=k
[1]; a
+=k
[0]; break;
443 case 11: c
+=((uint32_t)k8
[10])<<16; /* fall through */
444 case 10: c
+=((uint32_t)k8
[9])<<8; /* fall through */
445 case 9 : c
+=k8
[8]; /* fall through */
446 case 8 : b
+=k
[1]; a
+=k
[0]; break;
447 case 7 : b
+=((uint32_t)k8
[6])<<16; /* fall through */
448 case 6 : b
+=((uint32_t)k8
[5])<<8; /* fall through */
449 case 5 : b
+=k8
[4]; /* fall through */
450 case 4 : a
+=k
[0]; break;
451 case 3 : a
+=((uint32_t)k8
[2])<<16; /* fall through */
452 case 2 : a
+=((uint32_t)k8
[1])<<8; /* fall through */
453 case 1 : a
+=k8
[0]; break;
454 case 0 : *pc
=c
; *pb
=b
; return; /* zero length strings require no mixing */
457 #endif /* !valgrind */
459 } else if (HASH_LITTLE_ENDIAN
&& ((u
.i
& 0x1) == 0)) {
460 const uint16_t *k
= (const uint16_t *)key
; /* read 16-bit chunks */
463 /*--------------- all but last block: aligned reads and different mixing */
466 a
+= k
[0] + (((uint32_t)k
[1])<<16);
467 b
+= k
[2] + (((uint32_t)k
[3])<<16);
468 c
+= k
[4] + (((uint32_t)k
[5])<<16);
474 /*----------------------------- handle the last (probably partial) block */
475 k8
= (const uint8_t *)k
;
478 case 12: c
+=k
[4]+(((uint32_t)k
[5])<<16);
479 b
+=k
[2]+(((uint32_t)k
[3])<<16);
480 a
+=k
[0]+(((uint32_t)k
[1])<<16);
482 case 11: c
+=((uint32_t)k8
[10])<<16; /* fall through */
484 b
+=k
[2]+(((uint32_t)k
[3])<<16);
485 a
+=k
[0]+(((uint32_t)k
[1])<<16);
487 case 9 : c
+=k8
[8]; /* fall through */
488 case 8 : b
+=k
[2]+(((uint32_t)k
[3])<<16);
489 a
+=k
[0]+(((uint32_t)k
[1])<<16);
491 case 7 : b
+=((uint32_t)k8
[6])<<16; /* fall through */
493 a
+=k
[0]+(((uint32_t)k
[1])<<16);
495 case 5 : b
+=k8
[4]; /* fall through */
496 case 4 : a
+=k
[0]+(((uint32_t)k
[1])<<16);
498 case 3 : a
+=((uint32_t)k8
[2])<<16; /* fall through */
503 case 0 : *pc
=c
; *pb
=b
; return; /* zero length strings require no mixing */
506 } else { /* need to read the key one byte at a time */
507 const uint8_t *k
= (const uint8_t *)key
;
509 /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
513 a
+= ((uint32_t)k
[1])<<8;
514 a
+= ((uint32_t)k
[2])<<16;
515 a
+= ((uint32_t)k
[3])<<24;
517 b
+= ((uint32_t)k
[5])<<8;
518 b
+= ((uint32_t)k
[6])<<16;
519 b
+= ((uint32_t)k
[7])<<24;
521 c
+= ((uint32_t)k
[9])<<8;
522 c
+= ((uint32_t)k
[10])<<16;
523 c
+= ((uint32_t)k
[11])<<24;
529 /*-------------------------------- last block: affect all 32 bits of (c) */
530 switch(length
) /* all the case statements fall through */
532 case 12: c
+=((uint32_t)k
[11])<<24;
533 case 11: c
+=((uint32_t)k
[10])<<16;
534 case 10: c
+=((uint32_t)k
[9])<<8;
536 case 8 : b
+=((uint32_t)k
[7])<<24;
537 case 7 : b
+=((uint32_t)k
[6])<<16;
538 case 6 : b
+=((uint32_t)k
[5])<<8;
540 case 4 : a
+=((uint32_t)k
[3])<<24;
541 case 3 : a
+=((uint32_t)k
[2])<<16;
542 case 2 : a
+=((uint32_t)k
[1])<<8;
545 case 0 : *pc
=c
; *pb
=b
; return; /* zero length strings require no mixing */
553 static void __fill_statbuffer(
556 struct FileInfoBlock
*fib
,
557 int fallback_to_defaults
,
561 uint32_t pc
= 1, pb
= 1; /* initial hash values */
563 hashlittle2(buffer
, strlen((char*) buffer
), &pc
, &pb
);
564 hash
= pc
+ (((uint64_t)pb
)<<32);
566 if(fallback_to_defaults
)
568 /* Empty file, not protected, as old as it can be... */
570 fib
->fib_NumBlocks
= 0;
571 fib
->fib_Date
.ds_Days
= 0;
572 fib
->fib_Date
.ds_Minute
= 0;
573 fib
->fib_Date
.ds_Tick
= 0;
574 fib
->fib_OwnerUID
= 0;
575 fib
->fib_OwnerGID
= 0;
576 fib
->fib_Protection
= 0;
577 /* Most probable value */
578 fib
->fib_DirEntryType
= ST_PIPEFILE
;
581 sb
->st_dev
= (dev_t
) (lock
? ((struct FileLock
*)BADDR(lock
))->fl_Volume
: BNULL
);
582 sb
->st_ino
= hash
; /* hash value will be truncated if st_ino size is
583 smaller than uint64_t, but it's ok */
584 sb
->st_size
= (off_t
)fib
->fib_Size
;
585 /* FIXME: Update to properly handle the struct timespec fields
586 st_atim, st_mtim and st_ctim */
589 sb
->st_mtime
= (fib
->fib_Date
.ds_Days
* 24*60 + fib
->fib_Date
.ds_Minute
+ __stdc_gmtoffset()) * 60 +
590 fib
->fib_Date
.ds_Tick
/ TICKS_PER_SECOND
+ OFFSET_FROM_1970
;
591 sb
->st_uid
= __id_a2u(fib
->fib_OwnerUID
);
592 sb
->st_gid
= __id_a2u(fib
->fib_OwnerGID
);
593 sb
->st_mode
= __prot_a2u(fib
->fib_Protection
);
596 struct InfoData info
;
598 if (lock
&& Info(lock
, &info
))
600 sb
->st_blksize
= info
.id_BytesPerBlock
;
604 /* The st_blksize is just a guideline anyway, so we set it
605 to 1024 in case Info() didn't succeed */
606 sb
->st_blksize
= 1024;
609 if(fib
->fib_Size
> 0 && sb
->st_blksize
> 0)
611 (1 + ((long) fib
->fib_Size
- 1) / sb
->st_blksize
) *
612 (sb
->st_blksize
/ 512);
616 switch (fib
->fib_DirEntryType
)
619 /* don't use S_IFIFO, we don't have a mkfifo() call ! */
620 sb
->st_mode
|= S_IFCHR
;
627 sb
->st_mode
|= S_IFDIR
;
632 sb
->st_mode
|= S_IFLNK
;
639 sb
->st_mode
|= S_IFREG
;