[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[MiNT] FreeMiNT & MiNTlib patchset - add new support



Here's an updated patch of something I sent a while back.

The patch adds the following support...

Functions.
fdopendir()
fchdir()
dirfd()

New flags.
O_NOATIME
O_NOFOLLOW
O_DIRECTORY

I've been testing extensively with GNU tools and have fixed problems
along the way since my last patches were sent on this and they seem good
now. I've used coreutils, sed, m4, python, file, make and the list goes
on successfully with these.

Alan.


Index: sys/dosdir.c
===================================================================
RCS file: /mint/freemint/sys/dosdir.c,v
retrieving revision 1.27
diff -u -r1.27 dosdir.c
--- sys/dosdir.c	13 Jul 2007 21:32:48 -0000	1.27
+++ sys/dosdir.c	28 Jun 2008 23:47:39 -0000
@@ -1292,6 +1292,8 @@
 	dirh->next = p->p_fd->searches;
 	p->p_fd->searches = dirh;
 
+	dirh->fd = 0; /* less than MIN_OPEN */
+
 	assert(((long) dirh) > 0);
 	return (long) dirh;
 }
@@ -1389,6 +1391,21 @@
 
 	if (dirh->fc.fs)
 	{
+		/* If we've assigned a file descriptor to this cookie from
+		 * Fdirfd, then we need to ensure we close it now too.
+		 */
+		if (dirh->fd >= MIN_OPEN) {
+			FILEPTR *f;
+
+			r = GETFILEPTR (&p, &dirh->fd, &f);
+
+			if (!r) {
+				do_close(p, f);
+				DEBUG (("Removing file descriptor %d", dirh->fd));
+				FD_REMOVE (p, dirh->fd);
+			}
+		}
+
 		r = xfs_closedir (dirh->fc.fs, dirh);
 		release_cookie (&dirh->fc);
 	}
@@ -2086,3 +2103,236 @@
 	release_cookie (&fc);
 	return r;
 }
+
+/*
+ * GEMDOS extension: Fchdir(fd)
+ *
+ * sets the current directory from a file descriptor
+ */
+long _cdecl
+sys_f_chdir (short fd)
+{
+	struct proc *p = get_curproc();
+	FILEPTR	*f;
+	XATTR xattr;
+	int drv;
+	struct cwd *cwd = p->p_cwd;
+	long r;
+
+	r = GETFILEPTR (&p, &fd, &f);
+	if (r) return r;
+
+	if (!(f->fc.fs))
+	{
+		DEBUG (("Ffchdir: not a valid filesystem"));
+		return ENOSYS;
+	}
+
+	r = xfs_getxattr (f->fc.fs, &(f->fc), &xattr);
+	if (r)
+	{
+		DEBUG (("Ffchdir(%i): couldn't get directory attributes", fd));
+		return r;
+	}
+
+	if (!(xattr.attr & FA_DIR))
+	{
+		DEBUG (("Ffchdir(%i): not a directory", fd));
+		return ENOTDIR;
+	}
+
+	if (denyaccess (p->p_cred->ucr, &xattr, S_IXOTH))
+	{
+		DEBUG (("Ffchdir(%i): access denied", fd));
+		return EACCES;
+	}
+
+	/* watch out for symbolic links; if c:\foo is a link to d:\bar, then
+	 * "cd c:\foo" should also change the drive to d:
+	 */
+	drv = cwd->curdrv;
+	if (drv != UNIDRV && f->fc.dev != cwd->root[drv].dev)
+	{
+		int i;
+
+		for (i = 0; i < NUM_DRIVES; i++)
+		{
+			if (cwd->root[i].dev == f->fc.dev
+				&& cwd->root[i].fs == f->fc.fs)
+			{
+				if (cwd->curdrv == drv)
+					cwd->curdrv = i;
+
+				drv = i;
+				break;
+			}
+		}
+	}
+
+	release_cookie (&cwd->curdir[drv]);
+	dup_cookie(&cwd->curdir[drv], &f->fc);
+
+	return E_OK;
+}
+
+/*
+ * GEMDOS extension: fdopendir
+ *
+ * opendir with a file descriptor
+ */
+long _cdecl
+sys_f_opendir (short fd)
+{
+	struct proc *p = get_curproc();
+	FILEPTR	*f;
+	fcookie *dir;
+	long r;
+	DIR *dirh;
+	ushort mode;
+
+	r = GETFILEPTR (&p, &fd, &f);
+	if (r) return r;
+
+	if (!(f->fc.fs))
+	{
+		DEBUG (("Ffdopendir: not a valid filesystem"));
+		return ENOSYS;
+	}
+
+	dir = &(f->fc);
+	r = dir_access (p->p_cred->ucr, dir, S_IROTH, &mode);
+	if (r)
+	{
+		DEBUG (("Ffdopendir(%i): read permission denied", fd));
+		return r;
+	}
+
+	dirh = kmalloc (sizeof (*dirh));
+	if (!dirh)
+	{
+		DEBUG (("Ffdopendir(%i): out of memory", fd));
+		return ENOMEM;
+	}
+
+	dup_cookie(&dirh->fc, dir);
+	dirh->index = 0;
+	dirh->flags = 0;
+	r = xfs_opendir (dirh->fc.fs, dirh, dirh->flags);
+	if (r)
+	{
+		DEBUG (("Ffdopendir(%i): fdopendir returned %ld", fd, r));
+		kfree (dirh);
+		return r;
+	}
+
+	/* we keep a chain of open directories so that if a process
+	 * terminates without closing them all, we can clean up
+	 */
+	dirh->next = p->p_fd->searches;
+	p->p_fd->searches = dirh;
+
+	dirh->fd = fd;
+
+	assert(((long) dirh) > 0);
+	return (long) dirh;
+}
+
+/*
+ * GEMDOS extension: fdirfd
+ *
+ * a file descriptor from DIR*
+ */
+long _cdecl
+sys_f_dirfd (long handle)
+{
+	struct proc *p = get_curproc();
+	DIR *dirh = (DIR *)handle;
+	FILEPTR *fp = NULL;
+	short fd = MIN_OPEN;
+	DIR **where;
+	long r;
+	long devsp;
+	DEVDRV *dev;
+
+	where = &p->p_fd->searches;
+	while (*where && *where != dirh)
+		where = &((*where)->next);
+
+	if (!*where)
+	{
+		DEBUG (("Fdirfd: not an open directory"));
+		return EBADF;
+	}
+
+	if (!dirh->fc.fs)
+	{
+		DEBUG (("Fdirfd: not a valid filesystem"));
+		return EBADF;
+	}
+
+	/* locate previously handed fd */
+	if (dirh->fd >= MIN_OPEN) {
+		DEBUG(("Same descriptor %d found",i));
+
+		return dirh->fd;
+	}
+
+	dev = xfs_getdev (dirh->fc.fs, &dirh->fc, &devsp);
+	if (!dev)
+	{
+		DEBUG (("Fdirfd: device driver not found (%li)", devsp));
+		return devsp ? devsp : EINTERNAL;
+	}
+
+	r = FD_ALLOC (p, &fd, MIN_OPEN);
+	if (r) goto error;
+
+	r = FP_ALLOC (p, &fp);
+	if (r) goto error;
+
+	if (dev == &fakedev)
+	{
+		/* fake BIOS devices */
+		FILEPTR *fpfake;
+
+		assert (p->p_fd);
+
+		fpfake = p->p_fd->ofiles[devsp];
+		if (!fpfake || fpfake == (FILEPTR *) 1) {
+			r = EBADF;
+			goto error;
+		}
+
+		fpfake->links--;
+		FP_FREE (fp);
+
+		fp = fpfake;
+		fpfake->links++;
+	} else {
+		fp->links = 1;
+		fp->flags = O_RDONLY;
+		fp->pos = 0;
+		fp->devinfo = devsp;
+		fp->dev = dev;
+		dup_cookie(&fp->fc, &dirh->fc);
+		r = xdd_open (fp);
+		if (r < E_OK)
+		{
+			release_cookie(&fp->fc);
+			goto error;
+		}
+	}
+
+	dirh->fd = fd;	/* associate this dirp with this fd */
+
+	/* activate the fp, default is to close non-standard files on exec */
+	FP_DONE (p, fp, fd, FD_CLOEXEC);
+
+	return fd;
+
+error:
+	if (fd >= MIN_OPEN) FD_REMOVE (p, fd);
+	if (fp) { fp->links--; FP_FREE (fp); }
+
+	return r;
+}
Index: sys/dosdir.h
===================================================================
RCS file: /mint/freemint/sys/dosdir.h,v
retrieving revision 1.7
diff -u -r1.7 dosdir.h
--- sys/dosdir.h	8 Dec 2004 11:29:12 -0000	1.7
+++ sys/dosdir.h	28 Jun 2008 23:47:39 -0000
@@ -52,6 +52,9 @@
 long _cdecl sys_d_writelabel 	(const char *path, const char *label);
 long _cdecl sys_d_chroot	(const char *dir);
 long _cdecl sys_f_stat64	(int flag, const char *name, STAT *stat);
+long _cdecl sys_f_chdir		(short fd);
+long _cdecl sys_f_opendir	(short fd);
+long _cdecl sys_f_dirfd		(long handle);
 
 
 # endif /* _dosdir_h */
Index: sys/fatfs.c
===================================================================
RCS file: /mint/freemint/sys/fatfs.c,v
retrieving revision 1.42
diff -u -r1.42 fatfs.c
--- sys/fatfs.c	13 Jul 2007 21:32:48 -0000	1.42
+++ sys/fatfs.c	28 Jun 2008 23:47:40 -0000
@@ -8073,9 +8073,9 @@
 		return EACCES;
 	}
 
-	if (c->info.attr & FA_LABEL || c->info.attr & FA_DIR)
+	if (c->info.attr & FA_LABEL || ((c->info.attr & FA_DIR) && ((f->flags & O_RWMODE) != O_RDONLY)))
 	{
-		FAT_DEBUG (("fatfs_open: leave failure, not a valid file"));
+		FAT_DEBUG (("fatfs_open: leave failure, not a valid file or read-only directory"));
 		return EACCES;
 	}
 
@@ -8154,8 +8154,13 @@
 static long _cdecl
 fatfs_read (FILEPTR *f, char *buf, long bytes)
 {
+	COOKIE *c = (COOKIE *) f->fc.index;
+
 	FAT_DEBUG (("fatfs_read [%s]: enter (bytes = %li)", ((COOKIE *) f->fc.index)->name, bytes));
 
+	if (c->info.attr & FA_DIR)
+		return EISDIR;
+
 	if ((((FILE *) f->devinfo)->mode & O_RWMODE) == O_WRONLY)
 	{
 		FAT_DEBUG (("fatfs_read: leave failure (bad mode)"));
Index: sys/filesys.c
===================================================================
RCS file: /mint/freemint/sys/filesys.c,v
retrieving revision 1.40
diff -u -r1.40 filesys.c
--- sys/filesys.c	15 Jun 2008 08:57:43 -0000	1.40
+++ sys/filesys.c	28 Jun 2008 23:47:41 -0000
@@ -979,20 +979,6 @@
 		while (DIRSEP (*path))
 			path++;
 
-		/* if there's nothing left in the path, we can break here
-		 *
-		 * fna: I think this is an error, e.g.
-		 *      looking up: /foo/bar/ should result in an error, or?
-		 *      at least there is no lastname, so clear it
-		 */
-		if (!*path)
-		{
-			PATH2COOKIE_DB (("relpath2cookie: no more path, breaking (1)"));
-			*lastname = '\0'; /* no lastname */
-			*res = dir;
-			break;
-		}
-
 		/* next, peel off the next name in the path
 		 */
 		{
@@ -1009,18 +995,25 @@
 				c = *++path;
 			}
 
-			*s = 0;
+			*s = '\0';
 		}
 
 		/* if there are no more names in the path, and we don't want
 		 * to actually look up the last name, then we're done
 		 */
-		if (dolast == 0 && !*path)
+		if (dolast == 0)
 		{
-			PATH2COOKIE_DB (("relpath2cookie: no more path, breaking (2)"));
-			*res = dir;
-			PATH2COOKIE_DB (("relpath2cookie: *res = [%lx, %i]", res->fs, res->dev));
-			break;
+			register char *s = path;
+
+			while (DIRSEP (*s))
+				s++;
+				
+			if (!*s) {
+				PATH2COOKIE_DB (("relpath2cookie: no more path, breaking"));
+				*res = dir;
+				PATH2COOKIE_DB (("relpath2cookie: *res = [%lx, %i]", res->fs, res->dev));
+				break;
+			}
 		}
 
 		if (cwd->root_dir)
@@ -1074,6 +1067,9 @@
 		}
 		else if (r)
 		{
+#if 0
+	/* this fails for directories which are ffff/gggg/ in tar */
+	/* so let's return the true error ENOENT */
 			if (r == ENOENT && *path)
 			{
 				/* the "file" we didn't find was treated as a
@@ -1081,6 +1077,7 @@
 				 */
 				r = ENOTDIR;
 			}
+#endif
 			release_cookie (&dir);
 			break;
 		}
Index: sys/k_fds.c
===================================================================
RCS file: /mint/freemint/sys/k_fds.c,v
retrieving revision 1.20
diff -u -r1.20 k_fds.c
--- sys/k_fds.c	13 Jul 2007 21:32:48 -0000	1.20
+++ sys/k_fds.c	28 Jun 2008 23:47:41 -0000
@@ -362,9 +366,9 @@
 
 	DEBUG(("do_open(%s): mode 0x%x", name, xattr.mode));
 
-	/* we don't do directories
+	/* we don't do directories other than read-only
 	 */
-	if (S_ISDIR(xattr.mode))
+	if (S_ISDIR(xattr.mode) && ((rwmode & O_RWMODE) != O_RDONLY))
 	{
 		DEBUG(("do_open(%s): file is a directory", name));
 		release_cookie (&dir);
Index: sys/ramfs.c
===================================================================
RCS file: /mint/freemint/sys/ramfs.c,v
retrieving revision 1.14
diff -u -r1.14 ramfs.c
--- sys/ramfs.c	13 Jul 2007 21:32:49 -0000	1.14
+++ sys/ramfs.c	28 Jun 2008 23:47:42 -0000
@@ -2036,8 +2036,10 @@
 
 	if (!IS_REG (c))
 	{
-		RAM_DEBUG (("ramfs: ram_open: leave failure, not a valid file"));
-		return EACCES;
+		if (!(IS_DIR (c) && ((f->flags & O_RWMODE) == O_RDONLY))) {
+			RAM_DEBUG (("ramfs: ram_open: leave failure, not a valid file"));
+			return EACCES;
+		}
 	}
 
 	if (((f->flags & O_RWMODE) == O_WRONLY)
@@ -2277,6 +2279,9 @@
 	register long chunk;
 	register long done = 0;		/* processed characters */
 
+	if (IS_DIR (c))
+		return EISDIR;
+
 	if (!table)
 	{
 		RAM_DEBUG (("ramfs: ram_read: table doesn't exist!"));
@@ -2368,7 +2373,8 @@
 		f->pos += bytes;
 	}
 
-	if (!((c->s->flags & MS_NOATIME)
+	if (!((f->flags & O_NOATIME) 
+	        || (c->s->flags & MS_NOATIME)
 		|| (c->s->flags & MS_RDONLY)
 		|| IS_IMMUTABLE (c)))
 	{
Index: sys/syscall_vectors.c
===================================================================
RCS file: /mint/freemint/sys/syscall_vectors.c,v
retrieving revision 1.33
diff -u -r1.33 syscall_vectors.c
--- sys/syscall_vectors.c	2 Aug 2004 21:19:20 -0000	1.33
+++ sys/syscall_vectors.c	28 Jun 2008 23:47:42 -0000
@@ -519,9 +519,9 @@
 	/* 0x17f */		sys_enosys,		/* sys_munmap */
 
 	/* 0x180 */		sys_f_chown16,	/* 1.16 */
-	/* 0x181 */		sys_enosys,		/* reserved */
-	/* 0x182 */		sys_enosys,		/* reserved */
-	/* 0x183 */		sys_enosys,		/* reserved */
+	/* 0x181 */		sys_f_chdir,	/* 1.17 */
+	/* 0x182 */		sys_f_opendir,	/* 1.17 */
+	/* 0x183 */		sys_f_dirfd,	/* 1.17 */
 	/* 0x184 */		sys_enosys,		/* reserved */
 	/* 0x185 */		sys_enosys,		/* reserved */
 	/* 0x186 */		sys_enosys,		/* reserved */
Index: sys/syscalls.master
===================================================================
RCS file: /mint/freemint/sys/syscalls.master,v
retrieving revision 1.27
diff -u -r1.27 syscalls.master
--- sys/syscalls.master	2 Jun 2006 08:14:32 -0000	1.27
+++ sys/syscalls.master	28 Jun 2008 23:47:42 -0000
@@ -518,9 +518,9 @@
 
 0x180	Fchown16	(const char *name, short uid, short gid,
 			 short follow) /* since 1.16 */
-0x181	undefined
-0x182	undefined
-0x183	undefined
+0x181	Fchdir		(short fd) /* since 1.17 */
+0x182	Ffdopendir	(short fd) /* since 1.17 */
+0x183	Fdirfd		(long handle) /* since 1.17 */
 0x184	undefined
 0x185	undefined
 0x186	undefined
Index: sys/mint/fcntl.h
===================================================================
RCS file: /mint/freemint/sys/mint/fcntl.h,v
retrieving revision 1.2
diff -u -r1.2 fcntl.h
--- sys/mint/fcntl.h	13 Jun 2001 20:21:42 -0000	1.2
+++ sys/mint/fcntl.h	28 Jun 2008 23:47:43 -0000
@@ -47,7 +47,7 @@
 # define O_EXEC		0x00000003	/* execute file; used by kernel only */
 # endif
 
-/* 0x04 is for future expansion */
+# define O_NOATIME	0x00000004	/* Do not set atime.  */
 # define O_APPEND	0x00000008	/* all writes go to end of file */
 
 /* file sharing modes (not POSIX) */
Index: sys/mint/fsops.h
===================================================================
RCS file: /mint/freemint/sys/mint/fsops.h,v
retrieving revision 1.3
diff -u -r1.3 fsops.h
--- sys/mint/fsops.h	22 Jul 2004 18:23:42 -0000	1.3
+++ sys/mint/fsops.h	28 Jun 2008 23:47:43 -0000
@@ -37,6 +37,7 @@
 				/* NOTE: this must be at least 45 bytes */
 	DIR	*next;		/* linked together so we can close them
 				 * on process termination */
+	short	fd;		/* associated fd, for use with dirfd */
 };
 
 struct devdrv
Index: sys/xfs/ext2fs/ext2dev.c
===================================================================
RCS file: /mint/freemint/sys/xfs/ext2fs/ext2dev.c,v
retrieving revision 1.9
diff -u -r1.9 ext2dev.c
--- sys/xfs/ext2fs/ext2dev.c	13 Jul 2007 21:32:53 -0000	1.9
+++ sys/xfs/ext2fs/ext2dev.c	28 Jun 2008 23:47:43 -0000
@@ -72,8 +72,11 @@
 	
 	if (!EXT2_ISREG (le2cpu16 (c->in.i_mode)))
 	{
-		DEBUG (("Ext2-FS [%c]: e_open: not a regular file (#%ld)", f->fc.dev+'A', c->inode));
-		return EACCES;
+		if (!(EXT2_ISDIR (le2cpu16 (c->in.i_mode)) && 
+		     ((f->flags & O_RWMODE) == O_RDONLY))) {
+			DEBUG (("Ext2-FS [%c]: e_open: not a regular file or read-only directory (#%ld)", f->fc.dev+'A', c->inode));
+			return EACCES;
+		}
 	}
 	
 	if (((f->flags & O_RWMODE) == O_WRONLY)
@@ -392,13 +395,16 @@
 	
 	long todo;		/* characters remaining */
 	long done;		/* characters processed */
-	
+
 	ulong block = f->pos >> EXT2_BLOCK_SIZE_BITS (s);
 	ulong offset = f->pos & EXT2_BLOCK_SIZE_MASK (s);
-	
+
 	DEBUG (("Ext2-FS [%c]: e_read: enter (#%li: pos = %li, bytes = %li [%lu, %lu])", f->fc.dev+'A', c->inode, f->pos, bytes, block, offset));
+
+	if (EXT2_ISDIR (le2cpu16 (c->in.i_mode)))
+		return EISDIR;
 	
-	todo = MIN (c->i_size - f->pos, bytes);
+	todo = MIN ((long)(c->i_size - f->pos), bytes);
 	done = 0;
 	
 	if (todo == 0)
@@ -516,7 +522,10 @@
 	}
 	
 out:
-	if (!((s->s_flags & MS_NOATIME) || (s->s_flags & MS_RDONLY) || IS_IMMUTABLE (c)))
+	if (!((f->flags & O_NOATIME) 
+	     || (s->s_flags & MS_NOATIME) 
+	     || (s->s_flags & MS_RDONLY) 
+	     || IS_IMMUTABLE (c)))
 	{
 		c->in.i_atime = cpu2le32 (CURRENT_TIME);
 		mark_inode_dirty (c);
Index: sys/xfs/minixfs/minixdev.c
===================================================================
RCS file: /mint/freemint/sys/xfs/minixfs/minixdev.c,v
retrieving revision 1.6
diff -u -r1.6 minixdev.c
--- sys/xfs/minixfs/minixdev.c	13 Jul 2007 22:48:36 -0000	1.6
+++ sys/xfs/minixfs/minixdev.c	28 Jun 2008 23:47:43 -0000
@@ -63,8 +63,10 @@
 	
 	if (!IS_REG (rip))
 	{
-		DEBUG (("Minix-FS (%c): m_open: not a regular file.", f->fc.dev+'A'));
-		return EACCES;
+		if (!(IS_DIR (rip) && ((f->flags & O_RWMODE) == O_RDONLY))) {
+			DEBUG (("Minix-FS (%c): m_open: not a regular file.", f->fc.dev+'A'));
+			return EACCES;
+		}
 	}
 	
 	/* Set up f_cache structure */
@@ -306,6 +308,9 @@
 	}
 	
 	read_inode (f->fc.index, &rip, f->fc.dev);
+
+	if (IS_DIR (rip))
+		return EISDIR;
 	
 	if (mode == READ)
 	{
@@ -468,7 +473,8 @@
 	}
 	
 out:
-	__update_rip (f->fc.index, &rip, f->fc.dev, f->pos, mode);
+	if (!(f->flags & O_NOATIME))
+		__update_rip (f->fc.index, &rip, f->fc.dev, f->pos, mode);
 	
 	return done;	
 }
Index: dirent/SRCFILES
===================================================================
RCS file: /mint/mintlib/dirent/SRCFILES,v
retrieving revision 1.1
diff -u -r1.1 SRCFILES
--- dirent/SRCFILES	18 Jan 2001 02:17:08 -0000	1.1
+++ dirent/SRCFILES	29 Jun 2008 13:08:26 -0000
@@ -7,6 +7,8 @@
 SRCFILES = \
 	alphasort.c \
 	closedir.c \
+	dirfd.c \
+	fdopendir.c \
 	opendir.c \
 	readdir.c \
 	scandir.c \
Index: include/unistd.h
===================================================================
RCS file: /mint/mintlib/include/unistd.h,v
retrieving revision 1.12
diff -u -r1.12 unistd.h
--- include/unistd.h	27 Jun 2008 13:58:05 -0000	1.12
+++ include/unistd.h	29 Jun 2008 13:08:26 -0000
@@ -392,12 +392,10 @@
 extern int __chdir (__const char *__path) __THROW;
 
 #if defined __USE_BSD || defined __USE_XOPEN_EXTENDED
-#ifndef __MINT__
 /* Change the process's working directory to the one FD is open on.  */
 extern int fchdir (int __fd) __THROW;
 extern int __fchdir (int __fd) __THROW;
 #endif
-#endif
 
 /* Get the pathname of the current working directory,
    and put it in SIZE bytes of BUF.  Returns NULL if the
Index: include/bits/fcntl.h
===================================================================
RCS file: /mint/mintlib/include/bits/fcntl.h,v
retrieving revision 1.1
diff -u -r1.1 fcntl.h
--- include/bits/fcntl.h	19 Jul 2001 21:00:50 -0000	1.1
+++ include/bits/fcntl.h	29 Jun 2008 13:08:26 -0000
@@ -36,6 +36,12 @@
 #define	O_EXCL		0x800		/* error if file exists */
 #define O_NOCTTY	0x4000		/* do not open new controlling tty */
 
+#ifdef __USE_GNU
+# define O_NOATIME	0x04		/* Do not set atime.  */
+# define O_DIRECTORY	0x10000		/* Must be a directory.	 */
+# define O_NOFOLLOW	0x20000		/* Do not follow links.	 */
+#endif
+
 /* File status flags for `open' and `fcntl'.  */
 #define	O_APPEND	0x1000		/* position at EOF */
 #define _REALO_APPEND	0x08		/* this is what MiNT uses */
Index: include/mint/mintbind.h
===================================================================
RCS file: /mint/mintlib/include/mint/mintbind.h,v
retrieving revision 1.14
diff -u -r1.14 mintbind.h
--- include/mint/mintbind.h	22 Dec 2005 09:35:54 -0000	1.14
+++ include/mint/mintbind.h	29 Jun 2008 13:08:26 -0000
@@ -606,6 +606,12 @@
 /* 0x17f */
 #define Fchown16(name, uid, gid, follow_links) \
 		trap_1_wlwww(0x180, (long)(name), (short)(uid), (short)(gid), (short)follow_links)
+#define Fchdir(fh) \
+		trap_1_ww(0x181, (short)(fh))
+#define Ffdopendir(fh) \
+		trap_1_ww(0x182, (short)(fh))
+#define Fdirfd(handle) \
+		trap_1_wl(0x183, (long)(handle))
 
 __END_DECLS
 
Index: include/sys/dirent.h
===================================================================
RCS file: /mint/mintlib/include/sys/dirent.h,v
retrieving revision 1.4
diff -u -r1.4 dirent.h
--- include/sys/dirent.h	14 Mar 2002 20:48:59 -0000	1.4
+++ include/sys/dirent.h	29 Jun 2008 13:08:27 -0000
@@ -69,6 +69,14 @@
 extern DIR *__opendir (__const char *__name) __THROW;
 extern DIR *opendir (__const char *__name) __THROW;
 
+#ifdef __USE_GNU
+/* Same as opendir, but open the stream on the file descriptor FD.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+extern DIR *fdopendir (int __fd);
+#endif
+
 /* Close the directory stream DIRP.
    Return 0 if successful, -1 if not.  */
 extern int __closedir (DIR *__dirp) __THROW;
@@ -104,6 +112,9 @@
 
 #if defined __USE_BSD || defined __USE_MISC
 
+/* Return the file descriptor used by DIRP.  */
+extern int dirfd (DIR *__dirp) __THROW __nonnull ((1));
+
 /* Scan the directory DIR, calling SELECTOR on each directory entry.
    Entries for which SELECT returns nonzero are individually malloc'd,
    sorted using qsort with CMP, and collected in a malloc'd array in
Index: syscall/syscalls.master
===================================================================
RCS file: /mint/mintlib/syscall/syscalls.master,v
retrieving revision 1.4
diff -u -r1.4 syscalls.master
--- syscall/syscalls.master	22 Dec 2005 09:37:57 -0000	1.4
+++ syscall/syscalls.master	29 Jun 2008 13:08:31 -0000
@@ -518,9 +518,9 @@
 
 0x180	Fchown16	(const char *name, short uid, short gid,
 			 short follow) /* since 1.16 */
-0x181	undefined
-0x182	undefined
-0x183	undefined
+0x181	Fchdir		(short fd) /* since 1.17 */
+0x182	Ffdopendir	(short fd) /* since 1.17 */
+0x183	Fdirfd		(long handle) /* since 1.17 */
 0x184	undefined
 0x185	undefined
 0x186	undefined
Index: unix/SRCFILES
===================================================================
RCS file: /mint/mintlib/unix/SRCFILES,v
retrieving revision 1.13
diff -u -r1.13 SRCFILES
--- unix/SRCFILES	16 Jan 2004 11:15:27 -0000	1.13
+++ unix/SRCFILES	29 Jun 2008 13:08:31 -0000
@@ -18,6 +18,7 @@
 	dup.c \
 	dup2.c \
 	execve.c \
+	fchdir.c \
 	fchmod.c \
 	fchown.c \
 	fcntl.c \
Index: unix/open.c
===================================================================
RCS file: /mint/mintlib/unix/open.c,v
retrieving revision 1.7
diff -u -r1.7 open.c
--- unix/open.c	27 Jun 2008 13:55:50 -0000	1.7
+++ unix/open.c	29 Jun 2008 13:08:31 -0000
@@ -106,25 +110,42 @@
 			if (iomode & O_APPEND)
 				iomode |= _REALO_APPEND;
 		}
+
+		if (__mint >= 0x111) 
+			modemask |= O_NOFOLLOW | O_NOATIME;
+
 	} else {
 		modemask = O_ACCMODE;
 	}
 	
-	rv = __quickstat (filename, &sb, 0);
+	if (iomode & O_NOFOLLOW)
+		rv = __quickstat (filename, &sb, 1);
+	else
+		rv = __quickstat (filename, &sb, 0);
+
 	/* The code used to call Fstat.  Emulate this here.  */
 	if (rv != 0)
 		rv = -errno;
 	
 	if (rv == 0)		/* file exists */
 	{
-		if (S_ISDIR (sb.st_mode)) {
-	    		/* FIXME: It is actually not forbidden to open a 
-	    		   directory for reading only.  What should we return 
-	    		   then?  */
-	    		__set_errno (EISDIR);
-	    		return -1;
+		if (S_ISLNK (sb.st_mode) && (iomode & O_NOFOLLOW)) {
+			__set_errno(ELOOP);
+			return -1;
 		}
 		
+		if (S_ISDIR (sb.st_mode)) {
+			if ((iomode & O_ACCMODE) != O_RDONLY) {
+	    			__set_errno (EISDIR);
+	    			return -1;
+			}
+		} else {
+			if (iomode & O_DIRECTORY) {
+				__set_errno(ENOTDIR);
+				return -1;
+			}
+		}
+
 		if ((iomode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
 			__set_errno (EEXIST);
 			/* return __SMALLEST_VALID_HANDLE - 1; */
--- /dev/null	2008-06-16 20:45:19.083000907 +0100
+++ unix/fchdir.c	2008-05-09 10:04:23.021838418 +0100
@@ -0,0 +1,37 @@
+/*
+ * Fchdir implemented by Alan Hourihane <alanh@fairlite.co.uk>
+ */
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <support.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <mint/mintbind.h>
+
+#include "lib.h"
+
+int
+__fchdir (int fd)
+{
+	int r;
+
+	if (fd < 0) {
+		__set_errno(EBADF);
+		return -1;
+	}
+
+	r = Fchdir(fd);
+	if (r < 0) {
+		__set_errno(-r);
+		return -1;
+	}
+
+	return 0;
+}
+weak_alias (__fchdir, fchdir)
--- /dev/null	2008-06-16 20:45:19.083000907 +0100
+++ dirent/fdopendir.c	2008-02-22 09:51:46.605614796 +0000
@@ -0,0 +1,39 @@
+/*
+ * Ffdopendir implemented by Alan Hourihane <alanh@fairlite.co.uk>
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <limits.h>
+#include <dirent.h>
+#include <errno.h>
+#include <mint/mintbind.h>
+#include "lib.h"
+
+DIR *
+__fdopendir (int fd)
+{
+	DIR *d;
+	long r;
+
+	d = malloc(sizeof(DIR));
+	if (!d) {
+		__set_errno (ENOMEM);
+		return d;
+	}
+
+	r = Ffdopendir(fd);
+	if (r < 0) {
+		free(d);
+		__set_errno(-r);
+		return NULL;
+	}
+
+	d->handle = r;
+	d->buf.d_off = 0;
+	d->status = 0;
+
+	return d;
+}
+weak_alias (__fdopendir, fdopendir)
--- /dev/null	2008-06-16 20:45:19.083000907 +0100
+++ dirent/dirfd.c	2008-02-22 09:52:36.011614852 +0000
@@ -0,0 +1,33 @@
+/*
+ * Fdirfd implemented by Alan Hourihane <alanh@fairlite.co.uk>
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <limits.h>
+#include <dirent.h>
+#include <errno.h>
+#include <mint/mintbind.h>
+#include "lib.h"
+
+int
+__dirfd (DIR *dir)
+{
+	int r;
+	
+	if (!dir) {
+		__set_errno(EBADF);
+		return -1;
+	}
+
+	r = Fdirfd(dir->handle);
+
+	if (r < 0) {
+		__set_errno(-r);
+		return -1;
+	}
+
+	return r;
+}
+weak_alias (__dirfd, dirfd)