muimaster.library: support Listview_List in List
[AROS.git] / compiler / posixc / __stat.c
blob10f80d17bf86690f5012a81231de0df273d45d5e
1 /*
2 Copyright © 1995-2013, 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"
18 #include <sys/stat.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
24 time functions.
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(
33 struct stat *sb,
34 char *buffer,
35 struct FileInfoBlock *fib,
36 int fallback_to_defaults,
37 BPTR lock);
39 int __stat(BPTR lock, struct stat *sb, BOOL filehandle)
41 struct FileInfoBlock *fib;
42 UBYTE *buffer;
43 int buffersize = 256;
44 int fallback_to_defaults = 0;
45 BOOL Examined;
47 fib = AllocDosObject(DOS_FIB, NULL);
49 if (!fib)
51 errno = __stdc_ioerr2errno(IoErr());
53 return -1;
56 Examined = filehandle
57 ? ExamineFH(lock, fib)
58 : Examine(lock, fib);
59 if (!Examined)
61 if(IoErr() == ERROR_NOT_IMPLEMENTED ||
62 IoErr() == ERROR_ACTION_NOT_KNOWN)
64 fallback_to_defaults = 1;
66 else
68 errno = __stdc_ioerr2errno(IoErr());
69 FreeDosObject(DOS_FIB, fib);
70 return -1;
74 /* Get the full path of the stated filesystem object and use it to
75 compute hash value */
78 BOOL GotName;
80 if(!(buffer = AllocVec(buffersize, MEMF_ANY)))
82 errno = ENOMEM;
83 FreeDosObject(DOS_FIB, fib);
84 return -1;
87 GotName = filehandle
88 ? NameFromFH(lock, buffer, buffersize)
89 : NameFromLock(lock, buffer, buffersize);
90 if(GotName)
91 break;
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 */
100 buffer[0] = '\0';
101 break;
103 else if(IoErr() != ERROR_LINE_TOO_LONG)
105 errno = __stdc_ioerr2errno(IoErr());
106 FreeDosObject(DOS_FIB, fib);
107 FreeVec(buffer);
108 return -1;
110 FreeVec(buffer);
111 buffersize *= 2;
113 while(TRUE);
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?
117 if (filehandle)
119 BPTR filelock = DupLockFromFH(lock);
120 __fill_statbuffer(sb, (char*) buffer, fib, fallback_to_defaults, filelock);
121 UnLock(filelock);
123 else
125 __fill_statbuffer(sb, (char*) buffer, fib, fallback_to_defaults, lock);
128 FreeVec(buffer);
129 FreeDosObject(DOS_FIB, fib);
131 return 0;
135 int __stat_from_path(const char *path, struct stat *sb)
137 int len;
138 char *mypath;
139 int cwdsize = FILENAME_MAX;
140 char *cwd = NULL;
141 char *abspath = NULL;
142 char *filepart = NULL;
143 char *split;
144 struct FileInfoBlock *fib = NULL;
145 BPTR lock = BNULL;
146 BPTR cwdlock;
147 int fallback_to_defaults = 0;
148 BOOL loop;
149 int res = -1;
151 /* copy path and strip trailing slash */
152 len = strlen(path);
153 if (!(mypath = AllocVec(len + 1, MEMF_ANY)))
155 errno = ENOMEM;
156 goto out;
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);
167 CurrentDir(cwdlock);
170 if (!(cwd = AllocVec(cwdsize, MEMF_ANY)))
172 errno = ENOMEM;
173 goto out;
176 if (NameFromLock(cwdlock, cwd, cwdsize))
177 break;
178 else if (IoErr() != ERROR_LINE_TOO_LONG)
180 errno = __stdc_ioerr2errno(IoErr());
181 goto out;
184 FreeVec(cwd);
185 cwdsize *= 2;
187 while (TRUE);
189 /* get memory for current dir + '/' + input path + zero byte */
190 len = strlen(cwd) + 1 + len + 1;
191 abspath = AllocVec(len, MEMF_ANY);
192 if (!abspath)
194 errno = ENOMEM;
195 goto out;
198 strcpy(abspath, cwd);
199 AddPart(abspath, mypath, len);
200 FreeVec(mypath);
202 else
203 abspath = mypath;
205 /* split into path part and file part */
206 split = FilePart(abspath);
207 filepart = AllocVec(strlen(split) + 1, MEMF_ANY);
208 if (!filepart)
210 errno = ENOMEM;
211 goto out;
213 strcpy(filepart, split);
214 *split = '\0';
216 if ( !(fib = AllocDosObject(DOS_FIB, NULL))
217 || !(lock = Lock(abspath, SHARED_LOCK)))
219 errno = __stdc_ioerr2errno(IoErr());
220 goto out;
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;
229 else
231 errno = __stdc_ioerr2errno(IoErr());
232 goto out;
236 if (*filepart == '\0' || fallback_to_defaults)
237 __fill_statbuffer(sb, abspath, fib, fallback_to_defaults, lock);
238 else
239 /* examine entries of parent directory until we find the object to stat */
242 loop = ExNext(lock, fib);
244 if (loop)
246 if (stricmp(fib->fib_FileName, filepart) == 0)
248 __fill_statbuffer(sb, abspath, fib, 0, lock);
249 res = 0;
250 break;
253 continue;
256 if (IoErr() != ERROR_NO_MORE_ENTRIES)
257 errno = __stdc_ioerr2errno(IoErr());
258 else
259 /* nothing found to stat */
260 errno = ENOENT;
262 while (loop);
264 out:
265 if (lock)
266 UnLock(lock);
268 FreeVec(cwd);
270 /* if we had absolute path as input, mypath is free'd here */
271 FreeVec(abspath);
273 FreeVec(filepart);
275 if (fib)
276 FreeDosObject(DOS_FIB, fib);
278 return res;
282 static mode_t __prot_a2u(ULONG protect)
284 mode_t uprot = 0000;
286 if ((protect & FIBF_SCRIPT))
287 uprot |= 0111;
288 /* The following three flags are low-active! */
289 if (!(protect & FIBF_EXECUTE))
290 uprot |= 0100;
291 if (!(protect & FIBF_WRITE))
292 uprot |= 0200;
293 if (!(protect & FIBF_READ))
294 uprot |= 0400;
295 if ((protect & FIBF_GRP_EXECUTE))
296 uprot |= 0010;
297 if ((protect & FIBF_GRP_WRITE))
298 uprot |= 0020;
299 if ((protect & FIBF_GRP_READ))
300 uprot |= 0040;
301 if ((protect & FIBF_OTR_EXECUTE))
302 uprot |= 0001;
303 if ((protect & FIBF_OTR_WRITE))
304 uprot |= 0002;
305 if ((protect & FIBF_OTR_READ))
306 uprot |= 0004;
308 return uprot;
312 static uid_t __id_a2u(UWORD id)
314 switch(id)
316 case (UWORD)-1:
317 return 0;
319 case (UWORD)-2:
320 return (UWORD)-1;
322 case 0:
323 return (UWORD)-2;
325 default:
326 return 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 */
333 #if AROS_BIG_ENDIAN
334 # define HASH_LITTLE_ENDIAN 0
335 #else
336 # define HASH_LITTLE_ENDIAN 1
337 #endif
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))))
343 #define mix(a,b,c) \
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;
388 c += *pb;
390 u.ptr = key;
391 if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
392 const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
393 #ifdef VALGRIND
394 const uint8_t *k8;
395 #endif
397 /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
398 while (length > 12)
400 a += k[0];
401 b += k[1];
402 c += k[2];
403 mix(a,b,c);
404 length -= 12;
405 k += 3;
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).
418 #ifndef VALGRIND
420 switch(length)
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;
440 switch(length)
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 */
461 const uint8_t *k8;
463 /*--------------- all but last block: aligned reads and different mixing */
464 while (length > 12)
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);
469 mix(a,b,c);
470 length -= 12;
471 k += 6;
474 /*----------------------------- handle the last (probably partial) block */
475 k8 = (const uint8_t *)k;
476 switch(length)
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);
481 break;
482 case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
483 case 10: c+=k[4];
484 b+=k[2]+(((uint32_t)k[3])<<16);
485 a+=k[0]+(((uint32_t)k[1])<<16);
486 break;
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);
490 break;
491 case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
492 case 6 : b+=k[2];
493 a+=k[0]+(((uint32_t)k[1])<<16);
494 break;
495 case 5 : b+=k8[4]; /* fall through */
496 case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
497 break;
498 case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
499 case 2 : a+=k[0];
500 break;
501 case 1 : a+=k8[0];
502 break;
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) */
510 while (length > 12)
512 a += k[0];
513 a += ((uint32_t)k[1])<<8;
514 a += ((uint32_t)k[2])<<16;
515 a += ((uint32_t)k[3])<<24;
516 b += k[4];
517 b += ((uint32_t)k[5])<<8;
518 b += ((uint32_t)k[6])<<16;
519 b += ((uint32_t)k[7])<<24;
520 c += k[8];
521 c += ((uint32_t)k[9])<<8;
522 c += ((uint32_t)k[10])<<16;
523 c += ((uint32_t)k[11])<<24;
524 mix(a,b,c);
525 length -= 12;
526 k += 12;
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;
535 case 9 : c+=k[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;
539 case 5 : b+=k[4];
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;
543 case 1 : a+=k[0];
544 break;
545 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
549 final(a,b,c);
550 *pc=c; *pb=b;
553 static void __fill_statbuffer(
554 struct stat *sb,
555 char *buffer,
556 struct FileInfoBlock *fib,
557 int fallback_to_defaults,
558 BPTR lock)
560 uint64_t hash;
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... */
569 fib->fib_Size = 0;
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 */
587 sb->st_atime =
588 sb->st_ctime =
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;
602 else
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)
610 sb->st_blocks =
611 (1 + ((long) fib->fib_Size - 1) / sb->st_blksize) *
612 (sb->st_blksize / 512);
613 else
614 sb->st_blocks = 0;
616 switch (fib->fib_DirEntryType)
618 case ST_PIPEFILE:
619 /* don't use S_IFIFO, we don't have a mkfifo() call ! */
620 sb->st_mode |= S_IFCHR;
621 break;
623 case ST_ROOT:
624 case ST_USERDIR:
625 case ST_LINKDIR:
626 sb->st_nlink = 1;
627 sb->st_mode |= S_IFDIR;
628 break;
630 case ST_SOFTLINK:
631 sb->st_nlink = 1;
632 sb->st_mode |= S_IFLNK;
633 break;
635 case ST_FILE:
636 case ST_LINKFILE:
637 default:
638 sb->st_nlink = 1;
639 sb->st_mode |= S_IFREG;