Adds sys_mkdir() and sys_rmdir() (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Sat, 21 Aug 2010 01:09:25 +0000 (18:09 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:52 +0000 (17:35 -0700)
Rebuild your cross-compiler.

Adds mkdir and rmdir, and has children incref their parent dir's nlink
count.  There are races with the nlink, and in general with concurrent
operations on the paths and objects.

Also unifies error handling styles in many of the do_whatever VFS
functions.

Documentation/vfs.txt
kern/include/ros/bits/syscall.h
kern/include/vfs.h
kern/src/kfs.c
kern/src/syscall.c
kern/src/vfs.c
tests/file_test.c
tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/mkdir.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/rmdir.c [new file with mode: 0644]

index 108b17a..e2f1b8a 100644 (file)
@@ -268,7 +268,7 @@ FSs just need to know how to return a char* for a symname - and not do any of
 the actual link following.  Or any of the other stuff they do.  We'll see if
 that turns out to be an issue or not...
 
-Unlinking
+Unlinking and other Link Stuff
 -------------------------
 Unlinking is just disconnecting a dentry-inode pair from the directory tree, and
 decreasing the inode's i_nlink.  Nothing else happens yet, since we need to keep
@@ -279,3 +279,13 @@ eventually the dentry's refcnt hits 0.  When it does, it normally would be up
 for caching, but we can check nlinks and just drop it.  When that happens, it
 releases the inode, which will see its nlinks is 0.  That will trigger the
 underlying FS to clear out the FS-file.
+
+For directories, you can only have one hardlink to a directory - meaning you are
+only in the directory tree in one place.  However, all of your children can get
+to you by going '../'.  We'll count these as hardlinks too.  This means that
+every child increments its parent-dir's nlink.  This is the on-disk links, not
+to be confused with the dentry->d_parent and kref() business that goes on for
+the in-memory objects.  A directory cannot be removed if nlinks > 1.  If it is
+1, then you can rmdir it, which will set its nlinks to 0.  Then its inode's
+storage space will get freed when it is deleted, like any other inode.  In
+theory.
index c837c1a..da414da 100644 (file)
@@ -49,6 +49,7 @@
 #define SYS_eth_get_mac_addr           79
 #define SYS_eth_recv_check                     80
 
+/* FS Syscalls */
 #define SYS_read                               100
 #define SYS_write                              101
 #define SYS_open                               102
 #define SYS_readlink                   115
 #define SYS_chdir                              116
 #define SYS_getcwd                             117
-#define SYS_gettimeofday               118
-#define SYS_tcgetattr                  119
-#define SYS_tcsetattr                  120
+#define SYS_mkdir                              118
+#define SYS_rmdir                              119
+
+/* Misc syscalls */
+#define SYS_gettimeofday               140
+#define SYS_tcgetattr                  141
+#define SYS_tcsetattr                  142
 
 /* Syscalls we plan to remove someday */
 #define SYS_cache_buster        200 
index 2a03c79..1e2b8d0 100644 (file)
@@ -506,8 +506,10 @@ struct file *do_file_open(char *path, int flags, int mode);
 int do_symlink(char *path, const char *symname, int mode);
 int do_link(char *old_path, char *new_path);
 int do_unlink(char *path);
-int do_file_access(char *path, int mode);
-int do_file_chmod(char *path, int mode);
+int do_access(char *path, int mode);
+int do_chmod(char *path, int mode);
+int do_mkdir(char *path, int mode);
+int do_rmdir(char *path);
 struct file *dentry_open(struct dentry *dentry, int flags);
 void file_release(struct kref *kref);
 
index cc16db5..fd54e97 100644 (file)
@@ -406,13 +406,15 @@ int kfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        return 0;
 }
 
-/* Removes from dir the directory specified by the name in dentry. */
-// TODO: note this isn't necessarily the same dentry, just using it for the
-// naming (which seems to be a common way of doing things, like in lookup() -
-// can work either way.
+/* Removes from dir the directory 'dentry.'  KFS doesn't store anything in the
+ * inode for which children it has.  It probably should, but since everything is
+ * pinned, it just relies on the dentry connections. */
 int kfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
-       return -1;
+       /* Bug check, make sure we don't have any kids in KFS */
+       struct kfs_i_info *data = (struct kfs_i_info*)dentry->d_inode->i_fs_info;
+       assert(TAILQ_EMPTY(&data->children));
+       return 0;
 }
 
 /* Used to make a generic file, based on the type and the major/minor numbers
index 8148196..7973414 100644 (file)
@@ -834,6 +834,7 @@ static intreg_t sys_open(struct proc *p, const char *path, size_t path_l,
        char *t_path = user_strdup_errno(p, path, path_l);
        if (!t_path)
                return -1;
+       mode &= ~p->fs_env.umask;
        file = do_file_open(t_path, oflag, mode);
        user_memdup_free(p, t_path);
        if (!file)
@@ -979,7 +980,7 @@ static intreg_t sys_access(struct proc *p, const char *path, size_t path_l,
        char *t_path = user_strdup_errno(p, path, path_l);
        if (!t_path)
                return -1;
-       retval = do_file_access(t_path, mode);
+       retval = do_access(t_path, mode);
        user_memdup_free(p, t_path);
        printd("Access for path: %s retval: %d\n", path, retval);
        if (retval < 0) {
@@ -1002,7 +1003,7 @@ intreg_t sys_chmod(struct proc *p, const char *path, size_t path_l, int mode)
        char *t_path = user_strdup_errno(p, path, path_l);
        if (!t_path)
                return -1;
-       retval = do_file_chmod(t_path, mode);
+       retval = do_chmod(t_path, mode);
        user_memdup_free(p, t_path);
        if (retval < 0) {
                set_errno(-retval);
@@ -1125,6 +1126,29 @@ intreg_t sys_getcwd(struct proc *p, char *u_cwd, size_t cwd_l)
        return retval;
 }
 
+intreg_t sys_mkdir(struct proc *p, const char *path, size_t path_l, int mode)
+{
+       int retval;
+       char *t_path = user_strdup_errno(p, path, path_l);
+       if (!t_path)
+               return -1;
+       mode &= ~p->fs_env.umask;
+       retval = do_mkdir(t_path, mode);
+       user_memdup_free(p, t_path);
+       return retval;
+}
+
+intreg_t sys_rmdir(struct proc *p, const char *path, size_t path_l)
+{
+       int retval;
+       char *t_path = user_strdup_errno(p, path, path_l);
+       if (!t_path)
+               return -1;
+       retval = do_rmdir(t_path);
+       user_memdup_free(p, t_path);
+       return retval;
+}
+
 intreg_t sys_gettimeofday(struct proc *p, int *buf)
 {
        static spinlock_t gtod_lock = SPINLOCK_INITIALIZER;
@@ -1248,6 +1272,8 @@ intreg_t syscall(struct proc *p, uintreg_t syscallno, uintreg_t a1,
                [SYS_readlink] = (syscall_t)sys_readlink,
                [SYS_chdir] = (syscall_t)sys_chdir,
                [SYS_getcwd] = (syscall_t)sys_getcwd,
+               [SYS_mkdir] = (syscall_t)sys_mkdir,
+               [SYS_rmdir] = (syscall_t)sys_rmdir,
                [SYS_gettimeofday] = (syscall_t)sys_gettimeofday,
                [SYS_tcgetattr] = (syscall_t)sys_tcgetattr,
                [SYS_tcsetattr] = (syscall_t)sys_tcsetattr
index 2fa4e47..739217c 100644 (file)
@@ -677,9 +677,11 @@ struct inode *get_inode(struct dentry *dentry)
  * note we don't pass this an nd, like Linux does... */
 static struct inode *create_inode(struct dentry *dentry, int mode)
 {
-       /* note it is the i_ino that uniquely identifies a file in the system.
-        * there's a diff between creating an inode (even for an in-use ino) and
-        * then filling it in, and vs creating a brand new one */
+       /* note it is the i_ino that uniquely identifies a file in the specific
+        * filesystem.  there's a diff between creating an inode (even for an in-use
+        * ino) and then filling it in, and vs creating a brand new one.
+        * get_inode() sets it to 0, and it should be filled in later in an
+        * FS-specific manner. */
        struct inode *inode = get_inode(dentry);
        if (!inode)
                return 0;
@@ -702,13 +704,16 @@ static struct inode *create_inode(struct dentry *dentry, int mode)
 
 /* Create a new disk inode in dir associated with dentry, with the given mode.
  * called when creating a regular file.  dir is the directory/parent.  dentry is
- * the dentry of the inode we are creating.  Note the lack of the nd... */
+ * the dentry of the inode we are creating.  Note the lack of the nd...
+ * Also, we do the nlink++ in here, since we want to give the FS's a chance to
+ * fail. */
 int create_file(struct inode *dir, struct dentry *dentry, int mode)
 {
        struct inode *new_file = create_inode(dentry, mode);
        if (!new_file)
                return -1;
        dir->i_op->create(dir, dentry, mode, 0);
+       dir->i_nlink++;
        kref_put(&new_file->i_kref);
        return 0;
 }
@@ -721,6 +726,7 @@ int create_dir(struct inode *dir, struct dentry *dentry, int mode)
        if (!new_dir)
                return -1;
        dir->i_op->mkdir(dir, dentry, mode);
+       dir->i_nlink++;
        /* Make sure my parent tracks me.  This is okay, since no directory (dir)
         * can have more than one dentry */
        struct dentry *parent = TAILQ_FIRST(&dir->i_dentry);
@@ -740,6 +746,7 @@ int create_symlink(struct inode *dir, struct dentry *dentry,
        if (!new_sym)
                return -1;
        dir->i_op->symlink(dir, dentry, symname);
+       dir->i_nlink++;                 /* TODO: race with this, among other things */
        kref_put(&new_sym->i_kref);
        return 0;
 }
@@ -758,6 +765,7 @@ int check_perms(struct inode *inode, int access_mode)
 void inode_release(struct kref *kref)
 {
        struct inode *inode = container_of(kref, struct inode, i_kref);
+       TAILQ_REMOVE(&inode->i_sb->s_inodes, inode, i_sb_list);
        /* If we still have links, just dealloc the in-memory inode.  if we have no
         * links, we need to delete it too (which calls destroy). */
        if (inode->i_nlink)
@@ -959,51 +967,41 @@ struct file *do_file_open(char *path, int flags, int mode)
         * final link (which may not be in 'path' in the first place. */
        error = path_lookup(path, LOOKUP_PARENT | LOOKUP_FOLLOW, nd);
        if (error) {
-               path_release(nd);
                set_errno(-error);
-               return 0;
+               goto out_path_only;
        }
        /* see if the target is there, handle accordingly */
        file_d = do_lookup(nd->dentry, nd->last.name); 
        if (!file_d) {
                if (!(flags & O_CREAT)) {
-                       path_release(nd);
                        set_errno(ENOENT);
-                       return 0;
+                       goto out_path_only;
                }
                /* Create the inode/file.  get a fresh dentry too: */
                file_d = get_dentry(nd->dentry->d_sb, nd->dentry, nd->last.name);
                parent_i = nd->dentry->d_inode;
                /* Note that the mode technically should only apply to future opens,
                 * but we apply it immediately. */
-               if (current)
-                       mode &= ~current->fs_env.umask;
-               if (create_file(parent_i, file_d, mode)) {
-                       kref_put(&file_d->d_kref);
-                       path_release(nd);
-                       return 0;
-               }
+               if (create_file(parent_i, file_d, mode))        /* sets errno */
+                       goto out_file_d;
                dcache_put(file_d);
-       } else {        /* something already exists (might be a dir) */
+       } else {        /* something already exists */
                if ((flags & O_CREAT) && (flags & O_EXCL)) {
                        /* wanted to create, not open, bail out */
-                       kref_put(&file_d->d_kref);
-                       path_release(nd);
                        set_errno(EEXIST);
-                       return 0;
+                       goto out_file_d;
                }
        }
        /* now open the file (freshly created or if it already existed).  At this
         * point, file_d is a refcnt'd dentry, regardless of which branch we took.*/
        if (flags & O_TRUNC)
                warn("File truncation not supported yet.");
-       file = dentry_open(file_d, flags);              /* sets errno */
-       if (!file) {
-               kref_put(&file_d->d_kref);
-               path_release(nd);
-               return 0;
-       }
+       file = dentry_open(file_d, flags);                              /* sets errno */
+       /* Note the fall through to the exit paths.  File is 0 by default and if
+        * dentry_open fails. */
+out_file_d:
        kref_put(&file_d->d_kref);
+out_path_only:
        path_release(nd);
        return file;
 }
@@ -1016,41 +1014,37 @@ int do_symlink(char *path, const char *symname, int mode)
        struct inode *parent_i;
        struct nameidata nd_r = {0}, *nd = &nd_r;
        int error;
+       int retval = -1;
 
        nd->intent = LOOKUP_CREATE;
        /* get the parent, but don't follow links */
        error = path_lookup(path, LOOKUP_PARENT, nd);
        if (error) {
                set_errno(-error);
-               path_release(nd);
-               return -1;
+               goto out_path_only;
        }
        /* see if the target is already there, handle accordingly */
        sym_d = do_lookup(nd->dentry, nd->last.name); 
        if (sym_d) {
                set_errno(EEXIST);
-               kref_put(&sym_d->d_kref);
-               path_release(nd);
-               return -1;
+               goto out_sym_d;
        }
        /* Doesn't already exist, let's try to make it: */
        sym_d = get_dentry(nd->dentry->d_sb, nd->dentry, nd->last.name);
        if (!sym_d) {
                set_errno(ENOMEM);
-               path_release(nd);
-               return -1;
+               goto out_path_only;
        }
        parent_i = nd->dentry->d_inode;
-       /* TODO: mode should be & ~umask. */
-       if (create_symlink(parent_i, sym_d, symname, mode)) {
-               kref_put(&sym_d->d_kref);
-               path_release(nd);
-               return -1;
-       }
+       if (create_symlink(parent_i, sym_d, symname, mode))
+               goto out_sym_d;
        dcache_put(sym_d);
+       retval = 0;                             /* Note the fall through to the exit paths */
+out_sym_d:
        kref_put(&sym_d->d_kref);
+out_path_only:
        path_release(nd);
-       return 0;
+       return retval;
 }
 
 /* Makes a hard link for the file behind old_path to new_path */
@@ -1109,6 +1103,7 @@ int do_link(char *old_path, char *new_path)
        kref_get(&inode->i_kref, 1);
        link_d->d_inode = inode;
        inode->i_nlink++;
+       parent_dir->i_nlink++;
        TAILQ_INSERT_TAIL(&inode->i_dentry, link_d, d_alias);   /* weak ref */
        dcache_put(link_d);
        retval = 0;                             /* Note the fall through to the exit paths */
@@ -1121,6 +1116,8 @@ out_path_only:
        return retval;
 }
 
+/* Unlinks path from the directory tree.  Read the Documentation for more info.
+ */
 int do_unlink(char *path)
 {
        struct dentry *dentry;
@@ -1155,7 +1152,7 @@ int do_unlink(char *path)
        }
        kref_put(&dentry->d_parent->d_kref);
        dentry->d_parent = 0;           /* so we don't double-decref it later */
-       dentry->d_inode->i_nlink--;     /* TODO: race here */
+       dentry->d_inode->i_nlink--;     /* TODO: race here, esp with a decref */
        /* At this point, the dentry is unlinked from the FS, and the inode has one
         * less link.  When the in-memory objects (dentry, inode) are going to be
         * released (after all open files are closed, and maybe after entries are
@@ -1172,7 +1169,7 @@ out_path_only:
 /* Checks to see if path can be accessed via mode.  Need to actually send the
  * mode along somehow, so this doesn't do much now.  This is an example of
  * decent error propagation from the lower levels via int retvals. */
-int do_file_access(char *path, int mode)
+int do_access(char *path, int mode)
 {
        struct nameidata nd_r = {0}, *nd = &nd_r;
        int retval = 0;
@@ -1182,7 +1179,7 @@ int do_file_access(char *path, int mode)
        return retval;
 }
 
-int do_file_chmod(char *path, int mode)
+int do_chmod(char *path, int mode)
 {
        struct nameidata nd_r = {0}, *nd = &nd_r;
        int retval = 0;
@@ -1200,6 +1197,102 @@ int do_file_chmod(char *path, int mode)
        return retval;
 }
 
+/* Make a directory at path with mode.  Returns -1 and sets errno on errors */
+int do_mkdir(char *path, int mode)
+{
+       struct dentry *dentry;
+       struct inode *parent_i;
+       struct nameidata nd_r = {0}, *nd = &nd_r;
+       int error;
+       int retval = -1;
+
+       nd->intent = LOOKUP_CREATE;
+       /* get the parent, but don't follow links */
+       error = path_lookup(path, LOOKUP_PARENT, nd);
+       if (error) {
+               set_errno(-error);
+               goto out_path_only;
+       }
+       /* see if the target is already there, handle accordingly */
+       dentry = do_lookup(nd->dentry, nd->last.name); 
+       if (dentry) {
+               set_errno(EEXIST);
+               goto out_dentry;
+       }
+       /* Doesn't already exist, let's try to make it: */
+       dentry = get_dentry(nd->dentry->d_sb, nd->dentry, nd->last.name);
+       if (!dentry) {
+               set_errno(ENOMEM);
+               goto out_path_only;
+       }
+       parent_i = nd->dentry->d_inode;
+       if (create_dir(parent_i, dentry, mode))
+               goto out_dentry;
+       dcache_put(dentry);
+       retval = 0;                             /* Note the fall through to the exit paths */
+out_dentry:
+       kref_put(&dentry->d_kref);
+out_path_only:
+       path_release(nd);
+       return retval;
+}
+
+int do_rmdir(char *path)
+{
+       struct dentry *dentry;
+       struct inode *parent_i;
+       struct nameidata nd_r = {0}, *nd = &nd_r;
+       int error;
+       int retval = -1;
+
+       /* get the parent, following links (probably want this), and we must get a
+        * directory.  Note, current versions of path_lookup can't handle both
+        * PARENT and DIRECTORY, at least, it doesn't check that *path is a
+        * directory. */
+       error = path_lookup(path, LOOKUP_PARENT | LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
+                           nd);
+       if (error) {
+               set_errno(-error);
+               goto out_path_only;
+       }
+       /* make sure the target is already there, handle accordingly */
+       dentry = do_lookup(nd->dentry, nd->last.name); 
+       if (!dentry) {
+               set_errno(ENOENT);
+               goto out_path_only;
+       }
+       if (dentry->d_inode->i_type != FS_I_DIR) {
+               set_errno(ENOTDIR);
+               goto out_dentry;
+       }
+       /* TODO: make sure we aren't a mount or processes root (EBUSY) */
+       /* make sure we are empty.  TODO: Race with this, and anything touching
+        * i_nlink! */
+       if (dentry->d_inode->i_nlink != 1) {
+               set_errno(ENOTEMPTY);
+               goto out_dentry;
+       }
+       /* now for the removal */
+       parent_i = nd->dentry->d_inode;
+       error = parent_i->i_op->rmdir(parent_i, dentry);
+       if (error < 0) {
+               set_errno(-error);
+               goto out_dentry;
+       }
+       /* Decref ourselves, so inode_release() knows we are done */
+       dentry->d_inode->i_nlink--;
+       TAILQ_REMOVE(&nd->dentry->d_subdirs, dentry, d_subdirs_link);
+       parent_i->i_nlink--;            /* TODO: race on this, esp since its a decref */
+       /* we still have d_parent and a kref on our parent, which will go away when
+        * the in-memory dentry object goes away. */
+       retval = 0;                             /* Note the fall through to the exit paths */
+out_dentry:
+       kref_put(&dentry->d_kref);
+out_path_only:
+       path_release(nd);
+       return retval;
+}
+
 /* Opens and returns the file specified by dentry */
 struct file *dentry_open(struct dentry *dentry, int flags)
 {
@@ -1591,7 +1684,8 @@ static void print_dir(struct dentry *dentry, char *buf, int depth)
                return;
        }
        /* Print this dentry */
-       printk("%s%s/\n", buf, dentry->d_name.name);
+       printk("%s%s/ nlink: %d\n", buf, dentry->d_name.name,
+              dentry->d_inode->i_nlink);
        if (depth >= 32)
                return;
        /* Set buffer for our kids */
index 610a1ea..2a58aee 100644 (file)
@@ -155,7 +155,19 @@ int main()
        printf("Trying a chmod\n");
        retval = chmod("/dir1/dir1-1/f1-1.txt", S_IRWXO);
        if (retval < 0)
-               printf("WARNING! chmod failed with %d!\n", retval);
+               printf("WARNING! chmod failed with %d!\n", errno);
 
+       /* Try adding a directory or two! */
+       printf("Add dir3 and dir4, then remove dir4\n");
+       retval = mkdir("/dir3", S_IRWXU | S_IRWXG | S_IRWXO);
+       if (retval < 0)
+               printf("WARNING! mkdir failed with %d!\n", errno);
+       retval = mkdir("/dir4", S_IRWXU | S_IRWXG | S_IRWXO);
+       if (retval < 0)
+               printf("WARNING! mkdir failed with %d!\n", errno);
+       retval = rmdir("/dir4");
+       if (retval < 0)
+               printf("WARNING! rmdir failed with %d!\n", errno);
        breakpoint();
+
 }
diff --git a/tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/mkdir.c b/tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/mkdir.c
new file mode 100644 (file)
index 0000000..a68c5d7
--- /dev/null
@@ -0,0 +1,39 @@
+/* Copyright (C) 1991, 1995, 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <ros/syscall.h>
+
+/* Create a directory named PATH with protections MODE.  */
+int
+__mkdir (path, mode)
+     const char *path;
+     mode_t mode;
+{
+  if (path == NULL)
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+  return ros_syscall(SYS_mkdir, path, strlen(path), mode, 0, 0);
+}
+weak_alias (__mkdir, mkdir)
diff --git a/tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/rmdir.c b/tools/compilers/gcc-glibc/glibc-2.11.1-ros/sysdeps/ros/rmdir.c
new file mode 100644 (file)
index 0000000..7485493
--- /dev/null
@@ -0,0 +1,38 @@
+/* Copyright (C) 1991, 1995, 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <string.h>
+#include <ros/syscall.h>
+
+/* Remove the directory PATH.  */
+int
+__rmdir (path)
+     const char *path;
+{
+  if (path == NULL)
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+
+  return ros_syscall(SYS_rmdir, path, strlen(path), 0, 0, 0);
+}
+weak_alias (__rmdir, rmdir)