arch/m68k-amiga: Define the gcc symbol 'start' instead of using .bss
[AROS.git] / compiler / clib / __stat.c
blobf2fc3e2dbed85cf24cdfe4e75fcd3db7a32042dc
1 /*
2 Copyright © 1995-2011, 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>
15 #include "__time.h"
16 #include "__errno.h"
17 #include "__stat.h"
19 #include <sys/stat.h>
20 #include <aros/debug.h>
22 static mode_t __prot_a2u(ULONG protect);
23 static uid_t __id_a2u(UWORD id);
24 static void hashlittle2(const void *key, size_t length,
25 uint32_t *pc, uint32_t *pb);
26 static void __fill_statbuffer(
27 struct stat *sb,
28 char *buffer,
29 struct FileInfoBlock *fib,
30 int fallback_to_defaults,
31 BPTR lock);
33 int __stat(BPTR lock, struct stat *sb, BOOL filehandle)
35 struct FileInfoBlock *fib;
36 UBYTE *buffer;
37 int buffersize = 256;
38 int fallback_to_defaults = 0;
39 BOOL Examined;
41 fib = AllocDosObject(DOS_FIB, NULL);
43 if (!fib)
45 errno = IoErr2errno(IoErr());
47 return -1;
50 Examined = filehandle
51 ? ExamineFH(lock, fib)
52 : Examine(lock, fib);
53 if (!Examined)
55 if(IoErr() == ERROR_NOT_IMPLEMENTED)
57 fallback_to_defaults = 1;
59 else
61 errno = IoErr2errno(IoErr());
62 FreeDosObject(DOS_FIB, fib);
63 return -1;
67 /* Get the full path of the stated filesystem object and use it to
68 compute hash value */
71 BOOL GotName;
73 if(!(buffer = AllocVec(buffersize, MEMF_ANY)))
75 errno = ENOMEM;
76 FreeDosObject(DOS_FIB, fib);
77 return -1;
80 GotName = filehandle
81 ? NameFromFH(lock, buffer, buffersize)
82 : NameFromLock(lock, buffer, buffersize);
83 if(GotName)
84 break;
85 else if( IoErr() == ERROR_OBJECT_IN_USE
86 || IoErr() == ERROR_NOT_IMPLEMENTED
87 || (IoErr() == ERROR_OBJECT_NOT_FOUND && fib->fib_EntryType == ST_PIPEFILE))
89 /* We can't retrieve name because lock is an exclusive lock
90 or Examine is not implemented in this handler
91 or the lock refers to an XPIPE: file having always empty name */
92 buffer[0] = '\0';
93 break;
95 else if(IoErr() != ERROR_LINE_TOO_LONG)
97 errno = IoErr2errno(IoErr());
98 FreeDosObject(DOS_FIB, fib);
99 FreeVec(buffer);
100 return -1;
102 FreeVec(buffer);
103 buffersize *= 2;
105 while(TRUE);
107 __fill_statbuffer(sb, (char*) buffer, fib, fallback_to_defaults, lock);
109 FreeVec(buffer);
110 FreeDosObject(DOS_FIB, fib);
112 return 0;
116 int __stat_from_path(const char *path, struct stat *sb)
118 int len;
119 char *mypath;
120 int cwdsize = FILENAME_MAX;
121 char *cwd = NULL;
122 char *abspath = NULL;
123 char *filepart = NULL;
124 char *split;
125 struct FileInfoBlock *fib = NULL;
126 BPTR lock = BNULL;
127 BPTR cwdlock;
128 int fallback_to_defaults = 0;
129 BOOL loop;
130 int res = -1;
132 /* copy path and strip trailing slash */
133 len = strlen(path);
134 if (!(mypath = AllocVec(len + 1, MEMF_ANY)))
136 errno = ENOMEM;
137 goto out;
139 strcpy(mypath, path);
140 if (len && mypath[len-1] == '/')
141 mypath[len-1] = '\0';
143 /* do we have an absolute path */
144 if (!strchr(mypath, ':'))
146 /* no, then create one */
147 cwdlock = CurrentDir(BNULL);
148 CurrentDir(cwdlock);
151 if (!(cwd = AllocVec(cwdsize, MEMF_ANY)))
153 errno = ENOMEM;
154 goto out;
157 if (NameFromLock(cwdlock, cwd, cwdsize))
158 break;
159 else if (IoErr() != ERROR_LINE_TOO_LONG)
161 errno = IoErr2errno(IoErr());
162 goto out;
165 FreeVec(cwd);
166 cwdsize *= 2;
168 while (TRUE);
170 /* get memory for current dir + '/' + input path + zero byte */
171 len = strlen(cwd) + 1 + len + 1;
172 abspath = AllocVec(len, MEMF_ANY);
173 if (!abspath)
175 errno = ENOMEM;
176 goto out;
179 strcpy(abspath, cwd);
180 AddPart(abspath, mypath, len);
181 FreeVec(mypath);
183 else
184 abspath = mypath;
186 /* split into path part and file part */
187 split = FilePart(abspath);
188 filepart = AllocVec(strlen(split) + 1, MEMF_ANY);
189 if (!filepart)
191 errno = ENOMEM;
192 goto out;
194 strcpy(filepart, split);
195 *split = '\0';
197 if ( !(fib = AllocDosObject(DOS_FIB, NULL))
198 || !(lock = Lock(abspath, SHARED_LOCK)))
200 errno = IoErr2errno(IoErr());
201 goto out;
204 /* examine parent directory of object to stat */
205 if (!Examine(lock, fib))
207 if (IoErr() == ERROR_NOT_IMPLEMENTED)
208 fallback_to_defaults = 1;
209 else
211 errno = IoErr2errno(IoErr());
212 goto out;
216 if (*filepart == '\0' || fallback_to_defaults)
217 __fill_statbuffer(sb, abspath, fib, fallback_to_defaults, lock);
218 else
219 /* examine entries of parent directory until we find the object to stat */
222 loop = ExNext(lock, fib);
224 if (loop)
226 if (stricmp(fib->fib_FileName, filepart) == 0)
228 __fill_statbuffer(sb, abspath, fib, 0, lock);
229 res = 0;
230 break;
233 continue;
236 if (IoErr() != ERROR_NO_MORE_ENTRIES)
237 errno = IoErr2errno(IoErr());
238 else
239 /* nothing found to stat */
240 errno = ENOENT;
242 while (loop);
244 out:
245 if (lock)
246 UnLock(lock);
248 if (cwd)
249 FreeVec(cwd);
251 /* if we had absolute path as input, mypath is free'd here */
252 if (abspath)
253 FreeVec(abspath);
255 if (filepart)
256 FreeVec(filepart);
258 if (fib)
259 FreeDosObject(DOS_FIB, fib);
261 return res;
265 static mode_t __prot_a2u(ULONG protect)
267 mode_t uprot = 0000;
269 if ((protect & FIBF_SCRIPT))
270 uprot |= 0111;
271 /* The following three flags are low-active! */
272 if (!(protect & FIBF_EXECUTE))
273 uprot |= 0100;
274 if (!(protect & FIBF_WRITE))
275 uprot |= 0200;
276 if (!(protect & FIBF_READ))
277 uprot |= 0400;
278 if ((protect & FIBF_GRP_EXECUTE))
279 uprot |= 0010;
280 if ((protect & FIBF_GRP_WRITE))
281 uprot |= 0020;
282 if ((protect & FIBF_GRP_READ))
283 uprot |= 0040;
284 if ((protect & FIBF_OTR_EXECUTE))
285 uprot |= 0001;
286 if ((protect & FIBF_OTR_WRITE))
287 uprot |= 0002;
288 if ((protect & FIBF_OTR_READ))
289 uprot |= 0004;
291 return uprot;
295 static uid_t __id_a2u(UWORD id)
297 switch(id)
299 case (UWORD)-1:
300 return 0;
302 case (UWORD)-2:
303 return (UWORD)-1;
305 case 0:
306 return (UWORD)-2;
308 default:
309 return id;
313 /* The hash function code below is adapted from Bob Jenkins public domain hash
314 function, see http://burtleburtle.net/bob/hash/doobs.html for details */
316 #if AROS_BIG_ENDIAN
317 # define HASH_LITTLE_ENDIAN 0
318 #else
319 # define HASH_LITTLE_ENDIAN 1
320 #endif
322 #define hashsize(n) ((uint32_t)1<<(n))
323 #define hashmask(n) (hashsize(n)-1)
324 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
326 #define mix(a,b,c) \
328 a -= c; a ^= rot(c, 4); c += b; \
329 b -= a; b ^= rot(a, 6); a += c; \
330 c -= b; c ^= rot(b, 8); b += a; \
331 a -= c; a ^= rot(c,16); c += b; \
332 b -= a; b ^= rot(a,19); a += c; \
333 c -= b; c ^= rot(b, 4); b += a; \
336 #define final(a,b,c) \
338 c ^= b; c -= rot(b,14); \
339 a ^= c; a -= rot(c,11); \
340 b ^= a; b -= rot(a,25); \
341 c ^= b; c -= rot(b,16); \
342 a ^= c; a -= rot(c,4); \
343 b ^= a; b -= rot(a,14); \
344 c ^= b; c -= rot(b,24); \
348 * hashlittle2() -- hash a variable-length key into two 32-bit values
349 * k : the key (the unaligned variable-length array of bytes)
350 * length : the length of the key, counting by bytes
351 * pc : IN: primary initval, OUT: primary hash
352 * pb : IN: secondary initval, OUT: secondary hash
354 * Returns two 32-bit hash values. This is good enough for hash table
355 * lookup with 2^^64 buckets, or if you want a second hash if you're not
356 * happy with the first, or if you want a probably-unique 64-bit ID for
357 * the key. *pc is better mixed than *pb, so use *pc first. If you want
358 * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
360 static void hashlittle2(
361 const void *key, /* the key to hash */
362 size_t length, /* length of the key */
363 uint32_t *pc, /* IN: primary initval, OUT: primary hash */
364 uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */
366 uint32_t a,b,c; /* internal state */
367 union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
369 /* Set up the internal state */
370 a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc;
371 c += *pb;
373 u.ptr = key;
374 if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
375 const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
376 #ifdef VALGRIND
377 const uint8_t *k8;
378 #endif
380 /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
381 while (length > 12)
383 a += k[0];
384 b += k[1];
385 c += k[2];
386 mix(a,b,c);
387 length -= 12;
388 k += 3;
391 /*----------------------------- handle the last (probably partial) block */
393 * "k[2]&0xffffff" actually reads beyond the end of the string, but
394 * then masks off the part it's not allowed to read. Because the
395 * string is aligned, the masked-off tail is in the same word as the
396 * rest of the string. Every machine with memory protection I've seen
397 * does it on word boundaries, so is OK with this. But VALGRIND will
398 * still catch it and complain. The masking trick does make the hash
399 * noticably faster for short strings (like English words).
401 #ifndef VALGRIND
403 switch(length)
405 case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
406 case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
407 case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
408 case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
409 case 8 : b+=k[1]; a+=k[0]; break;
410 case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
411 case 6 : b+=k[1]&0xffff; a+=k[0]; break;
412 case 5 : b+=k[1]&0xff; a+=k[0]; break;
413 case 4 : a+=k[0]; break;
414 case 3 : a+=k[0]&0xffffff; break;
415 case 2 : a+=k[0]&0xffff; break;
416 case 1 : a+=k[0]&0xff; break;
417 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
420 #else /* make valgrind happy */
422 k8 = (const uint8_t *)k;
423 switch(length)
425 case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
426 case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
427 case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
428 case 9 : c+=k8[8]; /* fall through */
429 case 8 : b+=k[1]; a+=k[0]; break;
430 case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
431 case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
432 case 5 : b+=k8[4]; /* fall through */
433 case 4 : a+=k[0]; break;
434 case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
435 case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
436 case 1 : a+=k8[0]; break;
437 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
440 #endif /* !valgrind */
442 } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
443 const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
444 const uint8_t *k8;
446 /*--------------- all but last block: aligned reads and different mixing */
447 while (length > 12)
449 a += k[0] + (((uint32_t)k[1])<<16);
450 b += k[2] + (((uint32_t)k[3])<<16);
451 c += k[4] + (((uint32_t)k[5])<<16);
452 mix(a,b,c);
453 length -= 12;
454 k += 6;
457 /*----------------------------- handle the last (probably partial) block */
458 k8 = (const uint8_t *)k;
459 switch(length)
461 case 12: c+=k[4]+(((uint32_t)k[5])<<16);
462 b+=k[2]+(((uint32_t)k[3])<<16);
463 a+=k[0]+(((uint32_t)k[1])<<16);
464 break;
465 case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
466 case 10: c+=k[4];
467 b+=k[2]+(((uint32_t)k[3])<<16);
468 a+=k[0]+(((uint32_t)k[1])<<16);
469 break;
470 case 9 : c+=k8[8]; /* fall through */
471 case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
472 a+=k[0]+(((uint32_t)k[1])<<16);
473 break;
474 case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
475 case 6 : b+=k[2];
476 a+=k[0]+(((uint32_t)k[1])<<16);
477 break;
478 case 5 : b+=k8[4]; /* fall through */
479 case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
480 break;
481 case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
482 case 2 : a+=k[0];
483 break;
484 case 1 : a+=k8[0];
485 break;
486 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
489 } else { /* need to read the key one byte at a time */
490 const uint8_t *k = (const uint8_t *)key;
492 /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
493 while (length > 12)
495 a += k[0];
496 a += ((uint32_t)k[1])<<8;
497 a += ((uint32_t)k[2])<<16;
498 a += ((uint32_t)k[3])<<24;
499 b += k[4];
500 b += ((uint32_t)k[5])<<8;
501 b += ((uint32_t)k[6])<<16;
502 b += ((uint32_t)k[7])<<24;
503 c += k[8];
504 c += ((uint32_t)k[9])<<8;
505 c += ((uint32_t)k[10])<<16;
506 c += ((uint32_t)k[11])<<24;
507 mix(a,b,c);
508 length -= 12;
509 k += 12;
512 /*-------------------------------- last block: affect all 32 bits of (c) */
513 switch(length) /* all the case statements fall through */
515 case 12: c+=((uint32_t)k[11])<<24;
516 case 11: c+=((uint32_t)k[10])<<16;
517 case 10: c+=((uint32_t)k[9])<<8;
518 case 9 : c+=k[8];
519 case 8 : b+=((uint32_t)k[7])<<24;
520 case 7 : b+=((uint32_t)k[6])<<16;
521 case 6 : b+=((uint32_t)k[5])<<8;
522 case 5 : b+=k[4];
523 case 4 : a+=((uint32_t)k[3])<<24;
524 case 3 : a+=((uint32_t)k[2])<<16;
525 case 2 : a+=((uint32_t)k[1])<<8;
526 case 1 : a+=k[0];
527 break;
528 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
532 final(a,b,c);
533 *pc=c; *pb=b;
536 static void __fill_statbuffer(
537 struct stat *sb,
538 char *buffer,
539 struct FileInfoBlock *fib,
540 int fallback_to_defaults,
541 BPTR lock)
543 uint64_t hash;
544 uint32_t pc = 1, pb = 1; /* initial hash values */
546 hashlittle2(buffer, strlen((char*) buffer), &pc, &pb);
547 hash = pc + (((uint64_t)pb)<<32);
549 if(fallback_to_defaults)
551 /* Empty file, not protected, as old as it can be... */
552 fib->fib_Size = 0;
553 fib->fib_NumBlocks = 0;
554 fib->fib_Date.ds_Days = 0;
555 fib->fib_Date.ds_Minute = 0;
556 fib->fib_Date.ds_Tick = 0;
557 fib->fib_OwnerUID = 0;
558 fib->fib_OwnerGID = 0;
559 fib->fib_Protection = 0;
560 /* Most probable value */
561 fib->fib_DirEntryType = ST_PIPEFILE;
564 sb->st_dev = (dev_t)((struct FileLock *)BADDR(lock))->fl_Volume;
565 sb->st_ino = hash; /* hash value will be truncated if st_ino size is
566 smaller than uint64_t, but it's ok */
567 sb->st_size = (off_t)fib->fib_Size;
568 sb->st_atime =
569 sb->st_ctime =
570 sb->st_mtime = (fib->fib_Date.ds_Days * 24*60 + fib->fib_Date.ds_Minute + __gmtoffset) * 60 +
571 fib->fib_Date.ds_Tick / TICKS_PER_SECOND + OFFSET_FROM_1970;
572 sb->st_uid = __id_a2u(fib->fib_OwnerUID);
573 sb->st_gid = __id_a2u(fib->fib_OwnerGID);
574 sb->st_mode = __prot_a2u(fib->fib_Protection);
577 struct InfoData info;
579 if (Info(lock, &info))
581 sb->st_blksize = info.id_BytesPerBlock;
583 else
585 /* The st_blksize is just a guideline anyway, so we set it
586 to 1024 in case Info() didn't succeed */
587 sb->st_blksize = 1024;
590 if(fib->fib_Size > 0 && sb->st_blksize > 0)
591 sb->st_blocks =
592 (1 + ((long) fib->fib_Size - 1) / sb->st_blksize) *
593 (sb->st_blksize / 512);
594 else
595 sb->st_blocks = 0;
597 switch (fib->fib_DirEntryType)
599 case ST_PIPEFILE:
600 /* don't use S_IFIFO, we don't have a mkfifo() call ! */
601 sb->st_mode |= S_IFCHR;
602 break;
604 case ST_ROOT:
605 case ST_USERDIR:
606 case ST_LINKDIR:
607 sb->st_nlink = 1;
608 sb->st_mode |= S_IFDIR;
609 break;
611 case ST_SOFTLINK:
612 sb->st_nlink = 1;
613 sb->st_mode |= S_IFLNK;
614 break;
616 case ST_FILE:
617 case ST_LINKFILE:
618 default:
619 sb->st_nlink = 1;
620 sb->st_mode |= S_IFREG;