update mappings to reflect recent changes
[AROS.git] / compiler / clib / __stat.c
blob8b12a3f8f7a322bc5e1a4e11f9b8ed6f532a644e
1 /*
2 Copyright © 1995-2012, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <dos/dos.h>
7 #include <proto/dos.h>
8 #include <proto/exec.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <time.h>
16 #include "__stat.h"
17 #include "__arosc_privdata.h"
19 #include <sys/stat.h>
20 #include <aros/debug.h>
22 /* 2922 is the number of days between 1.1.1970 and 1.1.1978 (2 leap
23 years and 6 normal). The former number is the start value
24 for time(), the latter the start time for the AmigaOS
25 time functions.
27 #define OFFSET_FROM_1970 2922*24*60*60
29 static mode_t __prot_a2u(ULONG protect);
30 static uid_t __id_a2u(UWORD id);
31 static void hashlittle2(const void *key, size_t length,
32 uint32_t *pc, uint32_t *pb);
33 static void __fill_statbuffer(
34 struct stat *sb,
35 char *buffer,
36 struct FileInfoBlock *fib,
37 int fallback_to_defaults,
38 BPTR lock);
40 int __stat(BPTR lock, struct stat *sb, BOOL filehandle)
42 struct FileInfoBlock *fib;
43 UBYTE *buffer;
44 int buffersize = 256;
45 int fallback_to_defaults = 0;
46 BOOL Examined;
48 fib = AllocDosObject(DOS_FIB, NULL);
50 if (!fib)
52 errno = __arosc_ioerr2errno(IoErr());
54 return -1;
57 Examined = filehandle
58 ? ExamineFH(lock, fib)
59 : Examine(lock, fib);
60 if (!Examined)
62 if(IoErr() == ERROR_NOT_IMPLEMENTED ||
63 IoErr() == ERROR_ACTION_NOT_KNOWN)
65 fallback_to_defaults = 1;
67 else
69 errno = __arosc_ioerr2errno(IoErr());
70 FreeDosObject(DOS_FIB, fib);
71 return -1;
75 /* Get the full path of the stated filesystem object and use it to
76 compute hash value */
79 BOOL GotName;
81 if(!(buffer = AllocVec(buffersize, MEMF_ANY)))
83 errno = ENOMEM;
84 FreeDosObject(DOS_FIB, fib);
85 return -1;
88 GotName = filehandle
89 ? NameFromFH(lock, buffer, buffersize)
90 : NameFromLock(lock, buffer, buffersize);
91 if(GotName)
92 break;
93 else if( IoErr() == ERROR_OBJECT_IN_USE
94 || IoErr() == ERROR_NOT_IMPLEMENTED
95 || IoErr() == ERROR_ACTION_NOT_KNOWN
96 || (IoErr() == ERROR_OBJECT_NOT_FOUND && fib->fib_EntryType == ST_PIPEFILE))
98 /* We can't retrieve name because lock is an exclusive lock
99 or Examine is not implemented in this handler
100 or the lock refers to an XPIPE: file having always empty name */
101 buffer[0] = '\0';
102 break;
104 else if(IoErr() != ERROR_LINE_TOO_LONG)
106 errno = __arosc_ioerr2errno(IoErr());
107 FreeDosObject(DOS_FIB, fib);
108 FreeVec(buffer);
109 return -1;
111 FreeVec(buffer);
112 buffersize *= 2;
114 while(TRUE);
116 // We need a FileLock. Otherwise a call of Info() within __fill_statbuffer() will crash
117 // FIXME: how can we get a lock on an exclusive file?
118 if (filehandle)
120 BPTR filelock = DupLockFromFH(lock);
121 __fill_statbuffer(sb, (char*) buffer, fib, fallback_to_defaults, filelock);
122 UnLock(filelock);
124 else
126 __fill_statbuffer(sb, (char*) buffer, fib, fallback_to_defaults, lock);
129 FreeVec(buffer);
130 FreeDosObject(DOS_FIB, fib);
132 return 0;
136 int __stat_from_path(const char *path, struct stat *sb)
138 int len;
139 char *mypath;
140 int cwdsize = FILENAME_MAX;
141 char *cwd = NULL;
142 char *abspath = NULL;
143 char *filepart = NULL;
144 char *split;
145 struct FileInfoBlock *fib = NULL;
146 BPTR lock = BNULL;
147 BPTR cwdlock;
148 int fallback_to_defaults = 0;
149 BOOL loop;
150 int res = -1;
152 /* copy path and strip trailing slash */
153 len = strlen(path);
154 if (!(mypath = AllocVec(len + 1, MEMF_ANY)))
156 errno = ENOMEM;
157 goto out;
159 strcpy(mypath, path);
160 if (len && mypath[len-1] == '/')
161 mypath[len-1] = '\0';
163 /* do we have an absolute path */
164 if (!strchr(mypath, ':'))
166 /* no, then create one */
167 cwdlock = CurrentDir(BNULL);
168 CurrentDir(cwdlock);
171 if (!(cwd = AllocVec(cwdsize, MEMF_ANY)))
173 errno = ENOMEM;
174 goto out;
177 if (NameFromLock(cwdlock, cwd, cwdsize))
178 break;
179 else if (IoErr() != ERROR_LINE_TOO_LONG)
181 errno = __arosc_ioerr2errno(IoErr());
182 goto out;
185 FreeVec(cwd);
186 cwdsize *= 2;
188 while (TRUE);
190 /* get memory for current dir + '/' + input path + zero byte */
191 len = strlen(cwd) + 1 + len + 1;
192 abspath = AllocVec(len, MEMF_ANY);
193 if (!abspath)
195 errno = ENOMEM;
196 goto out;
199 strcpy(abspath, cwd);
200 AddPart(abspath, mypath, len);
201 FreeVec(mypath);
203 else
204 abspath = mypath;
206 /* split into path part and file part */
207 split = FilePart(abspath);
208 filepart = AllocVec(strlen(split) + 1, MEMF_ANY);
209 if (!filepart)
211 errno = ENOMEM;
212 goto out;
214 strcpy(filepart, split);
215 *split = '\0';
217 if ( !(fib = AllocDosObject(DOS_FIB, NULL))
218 || !(lock = Lock(abspath, SHARED_LOCK)))
220 errno = __arosc_ioerr2errno(IoErr());
221 goto out;
224 /* examine parent directory of object to stat */
225 if (!Examine(lock, fib))
227 if (IoErr() == ERROR_NOT_IMPLEMENTED ||
228 IoErr() == ERROR_ACTION_NOT_KNOWN)
229 fallback_to_defaults = 1;
230 else
232 errno = __arosc_ioerr2errno(IoErr());
233 goto out;
237 if (*filepart == '\0' || fallback_to_defaults)
238 __fill_statbuffer(sb, abspath, fib, fallback_to_defaults, lock);
239 else
240 /* examine entries of parent directory until we find the object to stat */
243 loop = ExNext(lock, fib);
245 if (loop)
247 if (stricmp(fib->fib_FileName, filepart) == 0)
249 __fill_statbuffer(sb, abspath, fib, 0, lock);
250 res = 0;
251 break;
254 continue;
257 if (IoErr() != ERROR_NO_MORE_ENTRIES)
258 errno = __arosc_ioerr2errno(IoErr());
259 else
260 /* nothing found to stat */
261 errno = ENOENT;
263 while (loop);
265 out:
266 if (lock)
267 UnLock(lock);
269 if (cwd)
270 FreeVec(cwd);
272 /* if we had absolute path as input, mypath is free'd here */
273 if (abspath)
274 FreeVec(abspath);
276 if (filepart)
277 FreeVec(filepart);
279 if (fib)
280 FreeDosObject(DOS_FIB, fib);
282 return res;
286 static mode_t __prot_a2u(ULONG protect)
288 mode_t uprot = 0000;
290 if ((protect & FIBF_SCRIPT))
291 uprot |= 0111;
292 /* The following three flags are low-active! */
293 if (!(protect & FIBF_EXECUTE))
294 uprot |= 0100;
295 if (!(protect & FIBF_WRITE))
296 uprot |= 0200;
297 if (!(protect & FIBF_READ))
298 uprot |= 0400;
299 if ((protect & FIBF_GRP_EXECUTE))
300 uprot |= 0010;
301 if ((protect & FIBF_GRP_WRITE))
302 uprot |= 0020;
303 if ((protect & FIBF_GRP_READ))
304 uprot |= 0040;
305 if ((protect & FIBF_OTR_EXECUTE))
306 uprot |= 0001;
307 if ((protect & FIBF_OTR_WRITE))
308 uprot |= 0002;
309 if ((protect & FIBF_OTR_READ))
310 uprot |= 0004;
312 return uprot;
316 static uid_t __id_a2u(UWORD id)
318 switch(id)
320 case (UWORD)-1:
321 return 0;
323 case (UWORD)-2:
324 return (UWORD)-1;
326 case 0:
327 return (UWORD)-2;
329 default:
330 return id;
334 /* The hash function code below is adapted from Bob Jenkins public domain hash
335 function, see http://burtleburtle.net/bob/hash/doobs.html for details */
337 #if AROS_BIG_ENDIAN
338 # define HASH_LITTLE_ENDIAN 0
339 #else
340 # define HASH_LITTLE_ENDIAN 1
341 #endif
343 #define hashsize(n) ((uint32_t)1<<(n))
344 #define hashmask(n) (hashsize(n)-1)
345 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
347 #define mix(a,b,c) \
349 a -= c; a ^= rot(c, 4); c += b; \
350 b -= a; b ^= rot(a, 6); a += c; \
351 c -= b; c ^= rot(b, 8); b += a; \
352 a -= c; a ^= rot(c,16); c += b; \
353 b -= a; b ^= rot(a,19); a += c; \
354 c -= b; c ^= rot(b, 4); b += a; \
357 #define final(a,b,c) \
359 c ^= b; c -= rot(b,14); \
360 a ^= c; a -= rot(c,11); \
361 b ^= a; b -= rot(a,25); \
362 c ^= b; c -= rot(b,16); \
363 a ^= c; a -= rot(c,4); \
364 b ^= a; b -= rot(a,14); \
365 c ^= b; c -= rot(b,24); \
369 * hashlittle2() -- hash a variable-length key into two 32-bit values
370 * k : the key (the unaligned variable-length array of bytes)
371 * length : the length of the key, counting by bytes
372 * pc : IN: primary initval, OUT: primary hash
373 * pb : IN: secondary initval, OUT: secondary hash
375 * Returns two 32-bit hash values. This is good enough for hash table
376 * lookup with 2^^64 buckets, or if you want a second hash if you're not
377 * happy with the first, or if you want a probably-unique 64-bit ID for
378 * the key. *pc is better mixed than *pb, so use *pc first. If you want
379 * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
381 static void hashlittle2(
382 const void *key, /* the key to hash */
383 size_t length, /* length of the key */
384 uint32_t *pc, /* IN: primary initval, OUT: primary hash */
385 uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */
387 uint32_t a,b,c; /* internal state */
388 union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
390 /* Set up the internal state */
391 a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc;
392 c += *pb;
394 u.ptr = key;
395 if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
396 const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
397 #ifdef VALGRIND
398 const uint8_t *k8;
399 #endif
401 /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
402 while (length > 12)
404 a += k[0];
405 b += k[1];
406 c += k[2];
407 mix(a,b,c);
408 length -= 12;
409 k += 3;
412 /*----------------------------- handle the last (probably partial) block */
414 * "k[2]&0xffffff" actually reads beyond the end of the string, but
415 * then masks off the part it's not allowed to read. Because the
416 * string is aligned, the masked-off tail is in the same word as the
417 * rest of the string. Every machine with memory protection I've seen
418 * does it on word boundaries, so is OK with this. But VALGRIND will
419 * still catch it and complain. The masking trick does make the hash
420 * noticably faster for short strings (like English words).
422 #ifndef VALGRIND
424 switch(length)
426 case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
427 case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
428 case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
429 case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
430 case 8 : b+=k[1]; a+=k[0]; break;
431 case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
432 case 6 : b+=k[1]&0xffff; a+=k[0]; break;
433 case 5 : b+=k[1]&0xff; a+=k[0]; break;
434 case 4 : a+=k[0]; break;
435 case 3 : a+=k[0]&0xffffff; break;
436 case 2 : a+=k[0]&0xffff; break;
437 case 1 : a+=k[0]&0xff; break;
438 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
441 #else /* make valgrind happy */
443 k8 = (const uint8_t *)k;
444 switch(length)
446 case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
447 case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
448 case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
449 case 9 : c+=k8[8]; /* fall through */
450 case 8 : b+=k[1]; a+=k[0]; break;
451 case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
452 case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
453 case 5 : b+=k8[4]; /* fall through */
454 case 4 : a+=k[0]; break;
455 case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
456 case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
457 case 1 : a+=k8[0]; break;
458 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
461 #endif /* !valgrind */
463 } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
464 const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
465 const uint8_t *k8;
467 /*--------------- all but last block: aligned reads and different mixing */
468 while (length > 12)
470 a += k[0] + (((uint32_t)k[1])<<16);
471 b += k[2] + (((uint32_t)k[3])<<16);
472 c += k[4] + (((uint32_t)k[5])<<16);
473 mix(a,b,c);
474 length -= 12;
475 k += 6;
478 /*----------------------------- handle the last (probably partial) block */
479 k8 = (const uint8_t *)k;
480 switch(length)
482 case 12: c+=k[4]+(((uint32_t)k[5])<<16);
483 b+=k[2]+(((uint32_t)k[3])<<16);
484 a+=k[0]+(((uint32_t)k[1])<<16);
485 break;
486 case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
487 case 10: c+=k[4];
488 b+=k[2]+(((uint32_t)k[3])<<16);
489 a+=k[0]+(((uint32_t)k[1])<<16);
490 break;
491 case 9 : c+=k8[8]; /* fall through */
492 case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
493 a+=k[0]+(((uint32_t)k[1])<<16);
494 break;
495 case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
496 case 6 : b+=k[2];
497 a+=k[0]+(((uint32_t)k[1])<<16);
498 break;
499 case 5 : b+=k8[4]; /* fall through */
500 case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
501 break;
502 case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
503 case 2 : a+=k[0];
504 break;
505 case 1 : a+=k8[0];
506 break;
507 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
510 } else { /* need to read the key one byte at a time */
511 const uint8_t *k = (const uint8_t *)key;
513 /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
514 while (length > 12)
516 a += k[0];
517 a += ((uint32_t)k[1])<<8;
518 a += ((uint32_t)k[2])<<16;
519 a += ((uint32_t)k[3])<<24;
520 b += k[4];
521 b += ((uint32_t)k[5])<<8;
522 b += ((uint32_t)k[6])<<16;
523 b += ((uint32_t)k[7])<<24;
524 c += k[8];
525 c += ((uint32_t)k[9])<<8;
526 c += ((uint32_t)k[10])<<16;
527 c += ((uint32_t)k[11])<<24;
528 mix(a,b,c);
529 length -= 12;
530 k += 12;
533 /*-------------------------------- last block: affect all 32 bits of (c) */
534 switch(length) /* all the case statements fall through */
536 case 12: c+=((uint32_t)k[11])<<24;
537 case 11: c+=((uint32_t)k[10])<<16;
538 case 10: c+=((uint32_t)k[9])<<8;
539 case 9 : c+=k[8];
540 case 8 : b+=((uint32_t)k[7])<<24;
541 case 7 : b+=((uint32_t)k[6])<<16;
542 case 6 : b+=((uint32_t)k[5])<<8;
543 case 5 : b+=k[4];
544 case 4 : a+=((uint32_t)k[3])<<24;
545 case 3 : a+=((uint32_t)k[2])<<16;
546 case 2 : a+=((uint32_t)k[1])<<8;
547 case 1 : a+=k[0];
548 break;
549 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
553 final(a,b,c);
554 *pc=c; *pb=b;
557 static void __fill_statbuffer(
558 struct stat *sb,
559 char *buffer,
560 struct FileInfoBlock *fib,
561 int fallback_to_defaults,
562 BPTR lock)
564 uint64_t hash;
565 uint32_t pc = 1, pb = 1; /* initial hash values */
567 hashlittle2(buffer, strlen((char*) buffer), &pc, &pb);
568 hash = pc + (((uint64_t)pb)<<32);
570 if(fallback_to_defaults)
572 /* Empty file, not protected, as old as it can be... */
573 fib->fib_Size = 0;
574 fib->fib_NumBlocks = 0;
575 fib->fib_Date.ds_Days = 0;
576 fib->fib_Date.ds_Minute = 0;
577 fib->fib_Date.ds_Tick = 0;
578 fib->fib_OwnerUID = 0;
579 fib->fib_OwnerGID = 0;
580 fib->fib_Protection = 0;
581 /* Most probable value */
582 fib->fib_DirEntryType = ST_PIPEFILE;
585 sb->st_dev = (dev_t) (lock ? ((struct FileLock *)BADDR(lock))->fl_Volume : BNULL);
586 sb->st_ino = hash; /* hash value will be truncated if st_ino size is
587 smaller than uint64_t, but it's ok */
588 sb->st_size = (off_t)fib->fib_Size;
589 sb->st_atime =
590 sb->st_ctime =
591 sb->st_mtime = (fib->fib_Date.ds_Days * 24*60 + fib->fib_Date.ds_Minute + __arosc_gmtoffset()) * 60 +
592 fib->fib_Date.ds_Tick / TICKS_PER_SECOND + OFFSET_FROM_1970;
593 sb->st_uid = __id_a2u(fib->fib_OwnerUID);
594 sb->st_gid = __id_a2u(fib->fib_OwnerGID);
595 sb->st_mode = __prot_a2u(fib->fib_Protection);
598 struct InfoData info;
600 if (lock && Info(lock, &info))
602 sb->st_blksize = info.id_BytesPerBlock;
604 else
606 /* The st_blksize is just a guideline anyway, so we set it
607 to 1024 in case Info() didn't succeed */
608 sb->st_blksize = 1024;
611 if(fib->fib_Size > 0 && sb->st_blksize > 0)
612 sb->st_blocks =
613 (1 + ((long) fib->fib_Size - 1) / sb->st_blksize) *
614 (sb->st_blksize / 512);
615 else
616 sb->st_blocks = 0;
618 switch (fib->fib_DirEntryType)
620 case ST_PIPEFILE:
621 /* don't use S_IFIFO, we don't have a mkfifo() call ! */
622 sb->st_mode |= S_IFCHR;
623 break;
625 case ST_ROOT:
626 case ST_USERDIR:
627 case ST_LINKDIR:
628 sb->st_nlink = 1;
629 sb->st_mode |= S_IFDIR;
630 break;
632 case ST_SOFTLINK:
633 sb->st_nlink = 1;
634 sb->st_mode |= S_IFLNK;
635 break;
637 case ST_FILE:
638 case ST_LINKFILE:
639 default:
640 sb->st_nlink = 1;
641 sb->st_mode |= S_IFREG;