compiler/clib: Removed unused arosc_init.h file.
[AROS.git] / compiler / clib / __stat.c
blob7eb96024c04e1da6458f9abda5f00da2be1b43a0
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>
15 #include "__time.h"
16 #include "__stat.h"
18 #include <sys/stat.h>
19 #include <aros/debug.h>
21 static mode_t __prot_a2u(ULONG protect);
22 static uid_t __id_a2u(UWORD id);
23 static void hashlittle2(const void *key, size_t length,
24 uint32_t *pc, uint32_t *pb);
25 static void __fill_statbuffer(
26 struct stat *sb,
27 char *buffer,
28 struct FileInfoBlock *fib,
29 int fallback_to_defaults,
30 BPTR lock);
32 int __stat(BPTR lock, struct stat *sb, BOOL filehandle)
34 struct FileInfoBlock *fib;
35 UBYTE *buffer;
36 int buffersize = 256;
37 int fallback_to_defaults = 0;
38 BOOL Examined;
40 fib = AllocDosObject(DOS_FIB, NULL);
42 if (!fib)
44 errno = __arosc_ioerr2errno(IoErr());
46 return -1;
49 Examined = filehandle
50 ? ExamineFH(lock, fib)
51 : Examine(lock, fib);
52 if (!Examined)
54 if(IoErr() == ERROR_NOT_IMPLEMENTED)
56 fallback_to_defaults = 1;
58 else
60 errno = __arosc_ioerr2errno(IoErr());
61 FreeDosObject(DOS_FIB, fib);
62 return -1;
66 /* Get the full path of the stated filesystem object and use it to
67 compute hash value */
70 BOOL GotName;
72 if(!(buffer = AllocVec(buffersize, MEMF_ANY)))
74 errno = ENOMEM;
75 FreeDosObject(DOS_FIB, fib);
76 return -1;
79 GotName = filehandle
80 ? NameFromFH(lock, buffer, buffersize)
81 : NameFromLock(lock, buffer, buffersize);
82 if(GotName)
83 break;
84 else if( IoErr() == ERROR_OBJECT_IN_USE
85 || IoErr() == ERROR_NOT_IMPLEMENTED
86 || (IoErr() == ERROR_OBJECT_NOT_FOUND && fib->fib_EntryType == ST_PIPEFILE))
88 /* We can't retrieve name because lock is an exclusive lock
89 or Examine is not implemented in this handler
90 or the lock refers to an XPIPE: file having always empty name */
91 buffer[0] = '\0';
92 break;
94 else if(IoErr() != ERROR_LINE_TOO_LONG)
96 errno = __arosc_ioerr2errno(IoErr());
97 FreeDosObject(DOS_FIB, fib);
98 FreeVec(buffer);
99 return -1;
101 FreeVec(buffer);
102 buffersize *= 2;
104 while(TRUE);
106 __fill_statbuffer(sb, (char*) buffer, fib, fallback_to_defaults, lock);
108 FreeVec(buffer);
109 FreeDosObject(DOS_FIB, fib);
111 return 0;
115 int __stat_from_path(const char *path, struct stat *sb)
117 int len;
118 char *mypath;
119 int cwdsize = FILENAME_MAX;
120 char *cwd = NULL;
121 char *abspath = NULL;
122 char *filepart = NULL;
123 char *split;
124 struct FileInfoBlock *fib = NULL;
125 BPTR lock = BNULL;
126 BPTR cwdlock;
127 int fallback_to_defaults = 0;
128 BOOL loop;
129 int res = -1;
131 /* copy path and strip trailing slash */
132 len = strlen(path);
133 if (!(mypath = AllocVec(len + 1, MEMF_ANY)))
135 errno = ENOMEM;
136 goto out;
138 strcpy(mypath, path);
139 if (len && mypath[len-1] == '/')
140 mypath[len-1] = '\0';
142 /* do we have an absolute path */
143 if (!strchr(mypath, ':'))
145 /* no, then create one */
146 cwdlock = CurrentDir(BNULL);
147 CurrentDir(cwdlock);
150 if (!(cwd = AllocVec(cwdsize, MEMF_ANY)))
152 errno = ENOMEM;
153 goto out;
156 if (NameFromLock(cwdlock, cwd, cwdsize))
157 break;
158 else if (IoErr() != ERROR_LINE_TOO_LONG)
160 errno = __arosc_ioerr2errno(IoErr());
161 goto out;
164 FreeVec(cwd);
165 cwdsize *= 2;
167 while (TRUE);
169 /* get memory for current dir + '/' + input path + zero byte */
170 len = strlen(cwd) + 1 + len + 1;
171 abspath = AllocVec(len, MEMF_ANY);
172 if (!abspath)
174 errno = ENOMEM;
175 goto out;
178 strcpy(abspath, cwd);
179 AddPart(abspath, mypath, len);
180 FreeVec(mypath);
182 else
183 abspath = mypath;
185 /* split into path part and file part */
186 split = FilePart(abspath);
187 filepart = AllocVec(strlen(split) + 1, MEMF_ANY);
188 if (!filepart)
190 errno = ENOMEM;
191 goto out;
193 strcpy(filepart, split);
194 *split = '\0';
196 if ( !(fib = AllocDosObject(DOS_FIB, NULL))
197 || !(lock = Lock(abspath, SHARED_LOCK)))
199 errno = __arosc_ioerr2errno(IoErr());
200 goto out;
203 /* examine parent directory of object to stat */
204 if (!Examine(lock, fib))
206 if (IoErr() == ERROR_NOT_IMPLEMENTED)
207 fallback_to_defaults = 1;
208 else
210 errno = __arosc_ioerr2errno(IoErr());
211 goto out;
215 if (*filepart == '\0' || fallback_to_defaults)
216 __fill_statbuffer(sb, abspath, fib, fallback_to_defaults, lock);
217 else
218 /* examine entries of parent directory until we find the object to stat */
221 loop = ExNext(lock, fib);
223 if (loop)
225 if (stricmp(fib->fib_FileName, filepart) == 0)
227 __fill_statbuffer(sb, abspath, fib, 0, lock);
228 res = 0;
229 break;
232 continue;
235 if (IoErr() != ERROR_NO_MORE_ENTRIES)
236 errno = __arosc_ioerr2errno(IoErr());
237 else
238 /* nothing found to stat */
239 errno = ENOENT;
241 while (loop);
243 out:
244 if (lock)
245 UnLock(lock);
247 if (cwd)
248 FreeVec(cwd);
250 /* if we had absolute path as input, mypath is free'd here */
251 if (abspath)
252 FreeVec(abspath);
254 if (filepart)
255 FreeVec(filepart);
257 if (fib)
258 FreeDosObject(DOS_FIB, fib);
260 return res;
264 static mode_t __prot_a2u(ULONG protect)
266 mode_t uprot = 0000;
268 if ((protect & FIBF_SCRIPT))
269 uprot |= 0111;
270 /* The following three flags are low-active! */
271 if (!(protect & FIBF_EXECUTE))
272 uprot |= 0100;
273 if (!(protect & FIBF_WRITE))
274 uprot |= 0200;
275 if (!(protect & FIBF_READ))
276 uprot |= 0400;
277 if ((protect & FIBF_GRP_EXECUTE))
278 uprot |= 0010;
279 if ((protect & FIBF_GRP_WRITE))
280 uprot |= 0020;
281 if ((protect & FIBF_GRP_READ))
282 uprot |= 0040;
283 if ((protect & FIBF_OTR_EXECUTE))
284 uprot |= 0001;
285 if ((protect & FIBF_OTR_WRITE))
286 uprot |= 0002;
287 if ((protect & FIBF_OTR_READ))
288 uprot |= 0004;
290 return uprot;
294 static uid_t __id_a2u(UWORD id)
296 switch(id)
298 case (UWORD)-1:
299 return 0;
301 case (UWORD)-2:
302 return (UWORD)-1;
304 case 0:
305 return (UWORD)-2;
307 default:
308 return id;
312 /* The hash function code below is adapted from Bob Jenkins public domain hash
313 function, see http://burtleburtle.net/bob/hash/doobs.html for details */
315 #if AROS_BIG_ENDIAN
316 # define HASH_LITTLE_ENDIAN 0
317 #else
318 # define HASH_LITTLE_ENDIAN 1
319 #endif
321 #define hashsize(n) ((uint32_t)1<<(n))
322 #define hashmask(n) (hashsize(n)-1)
323 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
325 #define mix(a,b,c) \
327 a -= c; a ^= rot(c, 4); c += b; \
328 b -= a; b ^= rot(a, 6); a += c; \
329 c -= b; c ^= rot(b, 8); b += a; \
330 a -= c; a ^= rot(c,16); c += b; \
331 b -= a; b ^= rot(a,19); a += c; \
332 c -= b; c ^= rot(b, 4); b += a; \
335 #define final(a,b,c) \
337 c ^= b; c -= rot(b,14); \
338 a ^= c; a -= rot(c,11); \
339 b ^= a; b -= rot(a,25); \
340 c ^= b; c -= rot(b,16); \
341 a ^= c; a -= rot(c,4); \
342 b ^= a; b -= rot(a,14); \
343 c ^= b; c -= rot(b,24); \
347 * hashlittle2() -- hash a variable-length key into two 32-bit values
348 * k : the key (the unaligned variable-length array of bytes)
349 * length : the length of the key, counting by bytes
350 * pc : IN: primary initval, OUT: primary hash
351 * pb : IN: secondary initval, OUT: secondary hash
353 * Returns two 32-bit hash values. This is good enough for hash table
354 * lookup with 2^^64 buckets, or if you want a second hash if you're not
355 * happy with the first, or if you want a probably-unique 64-bit ID for
356 * the key. *pc is better mixed than *pb, so use *pc first. If you want
357 * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
359 static void hashlittle2(
360 const void *key, /* the key to hash */
361 size_t length, /* length of the key */
362 uint32_t *pc, /* IN: primary initval, OUT: primary hash */
363 uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */
365 uint32_t a,b,c; /* internal state */
366 union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
368 /* Set up the internal state */
369 a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc;
370 c += *pb;
372 u.ptr = key;
373 if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
374 const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
375 #ifdef VALGRIND
376 const uint8_t *k8;
377 #endif
379 /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
380 while (length > 12)
382 a += k[0];
383 b += k[1];
384 c += k[2];
385 mix(a,b,c);
386 length -= 12;
387 k += 3;
390 /*----------------------------- handle the last (probably partial) block */
392 * "k[2]&0xffffff" actually reads beyond the end of the string, but
393 * then masks off the part it's not allowed to read. Because the
394 * string is aligned, the masked-off tail is in the same word as the
395 * rest of the string. Every machine with memory protection I've seen
396 * does it on word boundaries, so is OK with this. But VALGRIND will
397 * still catch it and complain. The masking trick does make the hash
398 * noticably faster for short strings (like English words).
400 #ifndef VALGRIND
402 switch(length)
404 case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
405 case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
406 case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
407 case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
408 case 8 : b+=k[1]; a+=k[0]; break;
409 case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
410 case 6 : b+=k[1]&0xffff; a+=k[0]; break;
411 case 5 : b+=k[1]&0xff; a+=k[0]; break;
412 case 4 : a+=k[0]; break;
413 case 3 : a+=k[0]&0xffffff; break;
414 case 2 : a+=k[0]&0xffff; break;
415 case 1 : a+=k[0]&0xff; break;
416 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
419 #else /* make valgrind happy */
421 k8 = (const uint8_t *)k;
422 switch(length)
424 case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
425 case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
426 case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
427 case 9 : c+=k8[8]; /* fall through */
428 case 8 : b+=k[1]; a+=k[0]; break;
429 case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
430 case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
431 case 5 : b+=k8[4]; /* fall through */
432 case 4 : a+=k[0]; break;
433 case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
434 case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
435 case 1 : a+=k8[0]; break;
436 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
439 #endif /* !valgrind */
441 } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
442 const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
443 const uint8_t *k8;
445 /*--------------- all but last block: aligned reads and different mixing */
446 while (length > 12)
448 a += k[0] + (((uint32_t)k[1])<<16);
449 b += k[2] + (((uint32_t)k[3])<<16);
450 c += k[4] + (((uint32_t)k[5])<<16);
451 mix(a,b,c);
452 length -= 12;
453 k += 6;
456 /*----------------------------- handle the last (probably partial) block */
457 k8 = (const uint8_t *)k;
458 switch(length)
460 case 12: c+=k[4]+(((uint32_t)k[5])<<16);
461 b+=k[2]+(((uint32_t)k[3])<<16);
462 a+=k[0]+(((uint32_t)k[1])<<16);
463 break;
464 case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
465 case 10: c+=k[4];
466 b+=k[2]+(((uint32_t)k[3])<<16);
467 a+=k[0]+(((uint32_t)k[1])<<16);
468 break;
469 case 9 : c+=k8[8]; /* fall through */
470 case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
471 a+=k[0]+(((uint32_t)k[1])<<16);
472 break;
473 case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
474 case 6 : b+=k[2];
475 a+=k[0]+(((uint32_t)k[1])<<16);
476 break;
477 case 5 : b+=k8[4]; /* fall through */
478 case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
479 break;
480 case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
481 case 2 : a+=k[0];
482 break;
483 case 1 : a+=k8[0];
484 break;
485 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
488 } else { /* need to read the key one byte at a time */
489 const uint8_t *k = (const uint8_t *)key;
491 /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
492 while (length > 12)
494 a += k[0];
495 a += ((uint32_t)k[1])<<8;
496 a += ((uint32_t)k[2])<<16;
497 a += ((uint32_t)k[3])<<24;
498 b += k[4];
499 b += ((uint32_t)k[5])<<8;
500 b += ((uint32_t)k[6])<<16;
501 b += ((uint32_t)k[7])<<24;
502 c += k[8];
503 c += ((uint32_t)k[9])<<8;
504 c += ((uint32_t)k[10])<<16;
505 c += ((uint32_t)k[11])<<24;
506 mix(a,b,c);
507 length -= 12;
508 k += 12;
511 /*-------------------------------- last block: affect all 32 bits of (c) */
512 switch(length) /* all the case statements fall through */
514 case 12: c+=((uint32_t)k[11])<<24;
515 case 11: c+=((uint32_t)k[10])<<16;
516 case 10: c+=((uint32_t)k[9])<<8;
517 case 9 : c+=k[8];
518 case 8 : b+=((uint32_t)k[7])<<24;
519 case 7 : b+=((uint32_t)k[6])<<16;
520 case 6 : b+=((uint32_t)k[5])<<8;
521 case 5 : b+=k[4];
522 case 4 : a+=((uint32_t)k[3])<<24;
523 case 3 : a+=((uint32_t)k[2])<<16;
524 case 2 : a+=((uint32_t)k[1])<<8;
525 case 1 : a+=k[0];
526 break;
527 case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
531 final(a,b,c);
532 *pc=c; *pb=b;
535 static void __fill_statbuffer(
536 struct stat *sb,
537 char *buffer,
538 struct FileInfoBlock *fib,
539 int fallback_to_defaults,
540 BPTR lock)
542 struct aroscbase *aroscbase = __GM_GetBase();
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 + aroscbase->acb_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;