152 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|   (c) Copyright 1992 Eric Backus
 | |
| 
 | |
|   This software may be used freely so long as this copyright notice is
 | |
|   left intact.  There is no warrantee on this software.
 | |
| */
 | |
| 
 | |
| #include <sys/stat.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| 
 | |
| #include "dos.h"
 | |
| #include <errno.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| extern int	_stat_assist(const char *, struct stat *);
 | |
| extern void	_fixpath(const char *, char *);
 | |
| 
 | |
| struct path_list
 | |
| {
 | |
|     struct path_list	*next;
 | |
|     char		*path;
 | |
|     int			inode;
 | |
| };
 | |
| 
 | |
| static int
 | |
| fixinode(const char *path, struct stat *buf)
 | |
| {
 | |
|     static struct path_list	*path_list[1256];
 | |
|     /* Start the inode count at three, since root path should be two */
 | |
|     static int			inode_count = 3;
 | |
| 
 | |
|     struct path_list		*path_ptr, *prev_ptr;
 | |
|     const char			*p;
 | |
|     int				hash;
 | |
| 
 | |
|     /* Skip over device and leading '/' */
 | |
|     if (path[1] == ':' && path[2] == '/') path += 3;
 | |
| 
 | |
|     /* We could probably use a better hash than this */
 | |
|     p = path;
 | |
|     hash = 0;
 | |
|     while (*p != '\0') hash += *p++;
 | |
|     hash = hash & 0xff;
 | |
| 
 | |
|     /* Have we seen this string? */
 | |
|     path_ptr = path_list[hash];
 | |
|     prev_ptr = path_ptr;
 | |
|     while (path_ptr)
 | |
|     {
 | |
| 	if (strcmp(path, path_ptr->path) == 0) break;
 | |
| 	prev_ptr = path_ptr;
 | |
| 	path_ptr = path_ptr->next;
 | |
|     }
 | |
| 
 | |
|     if (path_ptr)
 | |
| 	/* Same string, so same inode */
 | |
| 	buf->st_ino = path_ptr->inode;
 | |
|     else
 | |
|     {
 | |
| 	/* New string with same hash code */
 | |
| 	path_ptr = malloc(sizeof *path_ptr);
 | |
| 	if (path_ptr == NULL)
 | |
| 	  {
 | |
| 	    errno = ENOMEM;
 | |
| 	    return -1;
 | |
| 	  }
 | |
| 	path_ptr->next = NULL;
 | |
| 	path_ptr->path = strdup(path);
 | |
| 	if (path_ptr->path == NULL)
 | |
| 	  {
 | |
| 	    errno = ENOMEM;
 | |
| 	    return -1;
 | |
| 	  }
 | |
| 	path_ptr->inode = inode_count;
 | |
| 	if (prev_ptr)
 | |
| 	    prev_ptr->next = path_ptr;
 | |
| 	else
 | |
| 	    path_list[hash] = path_ptr;
 | |
| 	buf->st_ino = inode_count;
 | |
| 	inode_count++;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| stat(const char *path, struct stat *buf)
 | |
| {
 | |
|     static int	stat_called_before = 0;
 | |
|     char	p[1090];	/* Should be p[PATH_MAX+1] */
 | |
|     int		status;
 | |
| 
 | |
|     /* Normalize the path */
 | |
|     _fixpath(path, p);
 | |
| 
 | |
|     /* Work around strange bug with stat and time */
 | |
|     if (!stat_called_before)
 | |
|     {
 | |
| 	stat_called_before = 1;
 | |
| 	(void) time((time_t *) 0);
 | |
|     }
 | |
| 
 | |
|     /* Check for root path */
 | |
|     if (strcmp(p, "/") == 0 || strcmp(p + 1, ":/") == 0)
 | |
|     {
 | |
| 	/* Handle root path as special case, stat_assist doesn't like
 | |
| 	   the root directory. */
 | |
| 	if (p[1] == ':')
 | |
| 	{
 | |
| 	    if (p[0] >= 'a' && p[0] <= 'z')
 | |
| 		buf->st_dev = p[0] - 'a';
 | |
| 	    else
 | |
| 		buf->st_dev = p[0] - 'A';
 | |
| 	}
 | |
| 	else
 | |
| 	    buf->st_dev = -1;	/* No device? */
 | |
| 	buf->st_ino = 2;	/* Root path always inode 2 */
 | |
| 	buf->st_mode = S_IFDIR | S_IREAD | S_IWRITE | S_IEXEC;
 | |
| 	buf->st_nlink = 1;
 | |
| 	buf->st_uid = getuid();
 | |
| 	buf->st_gid = getgid();
 | |
| 	buf->st_rdev = buf->st_dev;
 | |
| 	buf->st_size = 0;
 | |
| 	buf->st_atime = 0;
 | |
| 	buf->st_mtime = 0;
 | |
| 	buf->st_ctime = 0;
 | |
| 	buf->st_blksize = 512;	/* Not always correct? */
 | |
| 	status = 0;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
| 	status = _stat_assist(p, buf);
 | |
| 
 | |
| 	/* Make inode numbers unique */
 | |
| 	if (status == 0) status = fixinode(p, buf);
 | |
| 
 | |
| 	/* The stat_assist does something weird with st_dev, but sets
 | |
| 	   st_rdev to the drive number.  Fix st_dev. */
 | |
| 	buf->st_dev = buf->st_rdev;
 | |
| 
 | |
| 	/* Make all files owned by ourself. */
 | |
| 	buf->st_uid = getuid();
 | |
| 	buf->st_gid = getgid();
 | |
| 
 | |
| 	/* Make all directories writable.  They always are in DOS, but
 | |
| 	   stat_assist doesn't think so. */
 | |
| 	if (S_ISDIR(buf->st_mode)) buf->st_mode |= S_IWRITE;
 | |
|     }
 | |
| 
 | |
|     return status;
 | |
| }
 |