9
0
Fork 0

Add FAT rmdir and unlink

git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@247 7fd9a85b-ad96-42d3-883c-3090e2eb8679
This commit is contained in:
patacongo 2007-05-21 17:17:42 +00:00
parent 7ba3d8d5c7
commit 185f183d9a
8 changed files with 430 additions and 51 deletions

View File

@ -141,5 +141,7 @@
0.2.6 2007-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr>
* Added unlink(), mkdir(), rmdir(), and rename()
* Fixed several serious FAT errors with oflags handling (&& instead of &)
* Added FAT support for unlink() and rmdir()
* Started m68322

View File

@ -186,6 +186,9 @@
The 8th release of NuttX (nuttx-0.2.5) is available for download
from the <a href="http://sourceforge.net/project/showfiles.php?group_id=189573">SourceForge</a>
website.
The change log associated with the release is available <a href="#currentrelease">here</a>.
Unreleased changes after this release are avalable in CVS.
These unreleased changes are listed <a href="#pendingchanges">here</a>.
</p>
<table width ="100%">
@ -376,6 +379,35 @@ Other memory:
</tr>
</table>
<center><table width ="80%">
<tr>
<td><img src="favicon.ico"></td>
<td>
<a href="#olderreleases">Change Logs for Older Releases</a><br>
</td>
</tr>
<tr>
<td><img src="favicon.ico"></td>
<td>
<a href="#currentrelease">ChangeLog for Current Release</a><br>
</td>
</tr>
<tr>
<td><img src="favicon.ico"></td>
<td>
<a href="#pendingchanges">Unreleased Changes</a>
</td>
</tr>
</table></center>
<table width ="100%">
<tr bgcolor="#e4e4e4">
<td>
<a name="olderreleases>Change Logs for Older Releases</a>
</td>
</tr>
</table>
<ul><pre>
0.1.0 2007-03-09 Gregory Nutt <spudmonkey@racsa.co.cr>
@ -499,7 +531,17 @@ Other memory:
* Logic from arch/c5471 and arch/dm320 combined into arch/arm.
arch/c5471 and arch/dm320 are deprecated and will be removed
when the new c5471 and dm320 logic is verified.
</pre></ul>
<table width ="100%">
<tr bgcolor="#e4e4e4">
<td>
<a name="currentrelease">ChangeLog for Current Release</a>
</td>
</tr>
</table>
<pre><ul>
0.2.5 2007-05-19 Gregory Nutt <spudmonkey@racsa.co.cr>
* Corrected some build/configuration issues introduced with the
@ -516,10 +558,22 @@ Other memory:
* close() was not closing the underlying device.
* Added fsync()
* Added strspn() and strcspn()
</pre></ul>
<table width ="100%">
<tr bgcolor="#e4e4e4">
<td>
<a name="pendingchanges">Unreleased Changes</a>
</td>
</tr>
</table>
<pre><ul>
0.2.6 2007-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr>
* Added unlink(), mkdir(), rmdir(), and rename()
* Fixed several serious FAT errors with oflags handling (&& instead of &)
* Added FAT support for unlink() and rmdir()
* Started m68322
</pre></ul>

View File

@ -82,7 +82,11 @@
* xxd -g 1 nuttx-test.vfat.gz >some-file
*
* Then manually massaged from the gzip xxd output to zlib format. See
* http://www.faqs.org/rfcs/rfc1952.html
* http://www.faqs.org/rfcs/rfc1952.html. This amounts to:
*
* Remove all of the leading bytes through the null terminator of the file name
* Remove the last 8 bytes
* Add 0x08, 0x1d to the beginning.
*/
static const unsigned char g_vfatdata[] =

View File

@ -63,6 +63,7 @@ static const char g_source[] = "/dev/blkdev";
static const char g_target[] = "/mnt/fs";
static const char g_filesystemtype[] = "vfat";
static const char g_testdir1[] = "/mnt/fs/TestDir";
static const char g_testfile1[] = "/mnt/fs/TestDir/TestFile.txt";
static const char g_testfile2[] = "/mnt/fs/TestDir/WritTest.txt";
static const char g_testmsg[] = "This is a write test";
@ -93,6 +94,8 @@ int user_start(int argc, char *argv[])
int nbytes;
int ret;
/* Mount the test file system (see arch/sim/src/up_deviceimage.c */
printf("main: mounting %s filesystem at target=%s with source=%s\n",
g_filesystemtype, g_target, g_source);
@ -101,12 +104,15 @@ int user_start(int argc, char *argv[])
if (ret == 0)
{
/* Read a test file that is already on the test file system image */
printf("main: opening %s for reading\n", g_testfile1);
int fd = open(g_testfile1, O_RDONLY);
if (fd < 0)
{
printf("main: failed to open %s, errno=%d\n", g_testfile1, *get_errno_ptr());
printf("main: ERROR failed to open %s, errno=%d\n",
g_testfile1, *get_errno_ptr());
}
else
{
@ -114,7 +120,8 @@ int user_start(int argc, char *argv[])
nbytes = read(fd, buffer, 128);
if (nbytes < 0)
{
printf("main: failed to read from %s, errno=%d\n", g_testfile1, *get_errno_ptr());
printf("main: ERROR failed to read from %s, errno=%d\n",
g_testfile1, *get_errno_ptr());
}
else
{
@ -124,19 +131,23 @@ int user_start(int argc, char *argv[])
close(fd);
}
/* Write a test file into a pre-existing file on the test file system */
printf("main: opening %s for writing\n", g_testfile2);
fd = open(g_testfile2, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (fd < 0)
{
printf("main: failed to open %s for writing, errno=%d\n", g_testfile2, *get_errno_ptr());
printf("main: ERROR failed to open %s for writing, errno=%d\n",
g_testfile2, *get_errno_ptr());
}
else
{
int nbytes = write(fd, g_testmsg, strlen(g_testmsg));
if (nbytes < 0)
{
printf("main: failed to write to %s, errno=%d\n", g_testfile2, *get_errno_ptr());
printf("main: ERROR failed to write to %s, errno=%d\n",
g_testfile2, *get_errno_ptr());
}
else
{
@ -145,12 +156,15 @@ int user_start(int argc, char *argv[])
close(fd);
}
/* Read the file that we just wrote */
printf("main: opening %s for reading\n", g_testfile2);
fd = open(g_testfile2, O_RDONLY);
if (fd < 0)
{
printf("main: failed to open %s for reading, errno=%d\n", g_testfile2, *get_errno_ptr());
printf("main: ERRORfailed to open %s for reading, errno=%d\n",
g_testfile2, *get_errno_ptr());
}
else
{
@ -158,7 +172,8 @@ int user_start(int argc, char *argv[])
nbytes = read(fd, buffer, 128);
if (nbytes < 0)
{
printf("main: failed to read from %s, errno=%d\n", g_testfile2, *get_errno_ptr());
printf("main: ERROR failed to read from %s, errno=%d\n",
g_testfile2, *get_errno_ptr());
}
else
{
@ -168,8 +183,124 @@ int user_start(int argc, char *argv[])
close(fd);
}
/* Try rmdir() against a file on the directory. It should fail with ENOTDIR */
printf("main: Try rmdir(%s)\n", g_testfile1);
ret = rmdir(g_testfile1);
if (ret == 0)
{
printf("main: ERROR rmdir(%s) succeeded\n", g_testfile1);
}
else if (*get_errno_ptr() != ENOTDIR)
{
printf("main: ERROR rmdir(%s) failed with errno=%d\n",
g_testfile1, *get_errno_ptr());
}
/* Try rmdir() against the test directory. It should fail with ENOTEMPTY */
printf("main: Try rmdir(%s)\n", g_testdir1);
ret = rmdir(g_testdir1);
if (ret == 0)
{
printf("main: ERROR rmdir(%s) succeeded\n", g_testdir1);
}
else if (*get_errno_ptr() != ENOTEMPTY)
{
printf("main: ERROR rmdir(%s) failed with errno=%d\n",
g_testdir1, *get_errno_ptr());
}
/* Try unlink() against the test directory. It should fail with EISDIR */
printf("main: Try unlink(%s)\n", g_testdir1);
ret = unlink(g_testdir1);
if (ret == 0)
{
printf("main: ERROR unlink(%s) succeeded\n", g_testdir1);
}
else if (*get_errno_ptr() != EISDIR)
{
printf("main: ERROR unlink(%s) failed with errno=%d\n",
g_testdir1, *get_errno_ptr());
}
/* Try unlink() against the test file1. It should succeed. */
printf("main: Try unlink(%s)\n", g_testfile1);
ret = unlink(g_testfile1);
if (ret != 0)
{
printf("main: ERROR unlink(%s) failed with errno=%d\n",
g_testfile1, *get_errno_ptr());
}
/* Attempt to open testfile1 should fail with ENOENT */
printf("main: Try open(%s) for reading\n", g_testfile1);
fd = open(g_testfile1, O_RDONLY);
if (fd >= 0)
{
printf("main: ERROR open(%s) succeeded\n", g_testfile1);
close(fd);
}
else if (*get_errno_ptr() != ENOENT)
{
printf("main: ERROR open(%s) failed with errno=%d\n",
g_testfile1, *get_errno_ptr());
}
/* Try rmdir() against the test directory. It should still fail with ENOTEMPTY */
printf("main: Try rmdir(%s)\n", g_testdir1);
ret = rmdir(g_testdir1);
if (ret == 0)
{
printf("main: ERROR rmdir(%s) succeeded\n", g_testdir1);
}
else if (*get_errno_ptr() != ENOTEMPTY)
{
printf("main: ERROR rmdir(%s) failed with errno=%d\n",
g_testdir1, *get_errno_ptr());
}
/* Try unlink() against the test file2. It should succeed. */
printf("main: Try unlink(%s)\n", g_testfile2);
ret = unlink(g_testfile2);
if (ret != 0)
{
printf("main: ERROR unlink(%s) failed with errno=%d\n",
g_testfile2, *get_errno_ptr());
}
/* Try rmdir() against the test directory. It should now succeed. */
printf("main: Try rmdir(%s)\n", g_testdir1);
ret = rmdir(g_testdir1);
if (ret != 0)
{
printf("main: ERROR rmdir(%s) failed with errno=%d\n",
g_testdir1, *get_errno_ptr());
}
/* Unmount the file system */
printf("main: Try unmount(%s)\n", g_target);
ret = umount(g_target);
printf("main: umount() returned %d\n", ret);
if (ret != 0)
{
printf("main: ERROR umount() failed, errno %d\n", *get_errno_ptr());
}
}
fflush(stdout);

View File

@ -73,7 +73,7 @@
* Private Function Prototypes
****************************************************************************/
static int fat_open(FAR struct file *filp, const char *rel_path,
static int fat_open(FAR struct file *filp, const char *relpath,
int oflags, mode_t mode);
static int fat_close(FAR struct file *filp);
static ssize_t fat_read(FAR struct file *filp, char *buffer, size_t buflen);
@ -86,10 +86,10 @@ static int fat_sync(FAR struct file *filp);
static int fat_bind(FAR struct inode *blkdriver, const void *data,
void **handle);
static int fat_unbind(void *handle);
static int fat_unlink(struct inode *mountpt, const char *rel_path);
static int fat_mkdir(struct inode *mountpt, const char *rel_path,
static int fat_unlink(struct inode *mountpt, const char *relpath);
static int fat_mkdir(struct inode *mountpt, const char *relpath,
mode_t mode);
static int fat_rmdir(struct inode *mountpt, const char *rel_path);
static int fat_rmdir(struct inode *mountpt, const char *relpath);
static int fat_rename(struct inode *mountpt, const char *old_relpath,
const char *new_relpath);
@ -120,6 +120,7 @@ const struct mountpt_operations fat_operations =
fat_unbind,
fat_unlink,
fat_mkdir,
fat_rmdir,
fat_rename
};
@ -131,7 +132,7 @@ const struct mountpt_operations fat_operations =
* Name: fat_open
****************************************************************************/
static int fat_open(FAR struct file *filp, const char *rel_path,
static int fat_open(FAR struct file *filp, const char *relpath,
int oflags, mode_t mode)
{
struct fat_dirinfo_s dirinfo;
@ -165,13 +166,12 @@ static int fat_open(FAR struct file *filp, const char *rel_path,
/* Initialize the directory info structure */
memset(&dirinfo, 0, sizeof(struct fat_dirinfo_s));
dirinfo.fs = fs;
/* Locate the directory entry for this path */
ret = fat_finddirentry(&dirinfo, rel_path);
ret = fat_finddirentry(fs, &dirinfo, relpath);
/* Three possibililities: (1) a node exists for the rel_path and
/* Three possibililities: (1) a node exists for the relpath and
* dirinfo describes the directory entry of the entity, (2) the
* node does not exist, or (3) some error occurred.
*/
@ -206,7 +206,7 @@ static int fat_open(FAR struct file *filp, const char *rel_path,
/* Check if the caller has sufficient privileges to open the file */
readonly = ((DIR_GETATTRIBUTES(dirinfo.fd_entry) & FATATTR_READONLY) != 0);
if (((oflags && O_WRONLY) != 0) && readonly)
if (((oflags & O_WRONLY) != 0) && readonly)
{
ret = -EACCES;
goto errout_with_semaphore;
@ -236,7 +236,7 @@ static int fat_open(FAR struct file *filp, const char *rel_path,
{
/* The file does not exist. Were we asked to create it? */
if ((oflags && O_CREAT) == 0)
if ((oflags & O_CREAT) == 0)
{
/* No.. then we fail with -ENOENT */
ret = -ENOENT;
@ -303,7 +303,7 @@ static int fat_open(FAR struct file *filp, const char *rel_path,
/* In write/append mode, we need to set the file pointer to the end of the file */
if ((oflags && (O_APPEND|O_WRONLY)) == (O_APPEND|O_WRONLY))
if ((oflags & (O_APPEND|O_WRONLY)) == (O_APPEND|O_WRONLY))
{
ff->ff_position = ff->ff_size;
}
@ -1320,7 +1320,7 @@ static int fat_unbind(void *handle)
*
****************************************************************************/
static int fat_unlink(struct inode *mountpt, const char *rel_path)
static int fat_unlink(struct inode *mountpt, const char *relpath)
{
struct fat_mountpt_s *fs;
int ret;
@ -1337,15 +1337,20 @@ static int fat_unlink(struct inode *mountpt, const char *rel_path)
fat_semtake(fs);
ret = fat_checkmount(fs);
if (ret != OK)
if (ret == OK)
{
goto errout_with_semaphore;
/* If the file is open, the correct behavior is to remove the file
* name, but to keep the file cluster chain in place until the last
* open reference to the file is closed.
*/
#warning "Need to defer deleting cluster chain if the file is open"
/* Remove the file */
ret = fat_remove(fs, relpath, FALSE);
}
#warning "fat_unlink is not implemented"
ret = -ENOSYS;
errout_with_semaphore:
fat_semgive(fs);
return ret;
}
@ -1357,7 +1362,7 @@ static int fat_unlink(struct inode *mountpt, const char *rel_path)
*
****************************************************************************/
static int fat_mkdir(struct inode *mountpt, const char *rel_path, mode_t mode)
static int fat_mkdir(struct inode *mountpt, const char *relpath, mode_t mode)
{
struct fat_mountpt_s *fs;
int ret;
@ -1394,7 +1399,7 @@ static int fat_mkdir(struct inode *mountpt, const char *rel_path, mode_t mode)
*
****************************************************************************/
int fat_rmdir(struct inode *mountpt, const char *rel_path)
int fat_rmdir(struct inode *mountpt, const char *relpath)
{
struct fat_mountpt_s *fs;
int ret;
@ -1411,15 +1416,20 @@ int fat_rmdir(struct inode *mountpt, const char *rel_path)
fat_semtake(fs);
ret = fat_checkmount(fs);
if (ret != OK)
if (ret == OK)
{
goto errout_with_semaphore;
/* If the directory is open, the correct behavior is to remove the directory
* name, but to keep the directory cluster chain in place until the last
* open reference to the directory is closed.
*/
#warning "Need to defer deleting cluster chain if the directory is open"
/* Remove the directory */
ret = fat_remove(fs, relpath, TRUE);
}
#warning "fat_rmdir is not implemented"
ret = -ENOSYS;
errout_with_semaphore:
fat_semgive(fs);
return ret;
}

View File

@ -488,7 +488,6 @@ struct fat_file_s
struct fat_dirinfo_s
{
struct fat_mountpt_s *fs; /* Pointer to the parent mountpoint */
ubyte fd_name[8+3]; /* Filename -- directory format*/
#ifdef CONFIG_FAT_LCNAMES
ubyte fd_ntflags; /* NTRes lower case flags */
@ -557,15 +556,17 @@ EXTERN sint32 fat_extendchain(struct fat_mountpt_s *fs, uint32 cluster);
/* Help for traverseing directory trees */
EXTERN int fat_nextdirentry(struct fat_dirinfo_s *dirinfo);
EXTERN int fat_finddirentry(struct fat_dirinfo_s *dirinfo, const char *path);
EXTERN int fat_nextdirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
EXTERN int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo,
const char *path);
/* File creation helpers */
/* File creation and removal helpers */
EXTERN int fat_dirtruncate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
EXTERN int fat_dircreate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
EXTERN int fat_remove(struct fat_mountpt_s *fs, const char *relpath, boolean directory);
/* Mountpoint and fFile buffer cache (for partial sector accesses) */
/* Mountpoint and file buffer cache (for partial sector accesses) */
EXTERN int fat_fscacheread(struct fat_mountpt_s *fs, size_t sector);
EXTERN int fat_ffcacheflush(struct fat_mountpt_s *fs, struct fat_file_s *ff);

View File

@ -471,7 +471,7 @@ static int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *
return OK;
}
ret = fat_nextdirentry(dirinfo);
ret = fat_nextdirentry(fs, dirinfo);
if (ret < 0)
{
return ret;
@ -1583,9 +1583,8 @@ sint32 fat_extendchain(struct fat_mountpt_s *fs, uint32 cluster)
*
****************************************************************************/
int fat_nextdirentry(struct fat_dirinfo_s *dirinfo)
int fat_nextdirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
{
struct fat_mountpt_s *fs = dirinfo->fs;
unsigned int cluster;
unsigned int ndx;
@ -1674,9 +1673,9 @@ int fat_nextdirentry(struct fat_dirinfo_s *dirinfo)
*
****************************************************************************/
int fat_finddirentry(struct fat_dirinfo_s *dirinfo, const char *path)
int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo,
const char *path)
{
struct fat_mountpt_s *fs = dirinfo->fs;
size_t cluster;
ubyte *direntry = NULL;
char terminator;
@ -1778,7 +1777,7 @@ int fat_finddirentry(struct fat_dirinfo_s *dirinfo, const char *path)
/* No... get the next directory index and try again */
if (fat_nextdirentry(dirinfo) != OK)
if (fat_nextdirentry(fs, dirinfo) != OK)
{
return -ENOENT;
}
@ -1935,6 +1934,184 @@ int fat_dircreate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
return OK;
}
/****************************************************************************
* Name: fat_remove
*
* Desciption: Remove a directory or file from the file system. This
* implements both rmdir() and unlink().
*
****************************************************************************/
int fat_remove(struct fat_mountpt_s *fs, const char *relpath, boolean directory)
{
struct fat_dirinfo_s dirinfo;
uint32 dircluster;
size_t dirsector;
int ret;
/* Find the directory entry referring to the entry to be deleted */
ret = fat_finddirentry(fs, &dirinfo, relpath);
if (ret != OK)
{
/* No such path */
return -ENOENT;
}
/* Check if this is a FAT12/16 root directory */
if (dirinfo.fd_entry == NULL)
{
/* The root directory cannot be removed */
return -EPERM;
}
/* The object has to have write access to be deleted */
if ((DIR_GETATTRIBUTES(dirinfo.fd_entry) & FATATTR_READONLY) != 0)
{
/* It is a read-only entry */
return -EACCES;
}
/* Get the directory sector and cluster containing the
* entry to be deleted
*/
dirsector = fs->fs_currentsector;
dircluster =
((uint32)DIR_GETFSTCLUSTHI(dirinfo.fd_entry) << 16) |
DIR_GETFSTCLUSTLO(dirinfo.fd_entry);
/* Is this entry a directory? */
if (DIR_GETATTRIBUTES(dirinfo.fd_entry) & FATATTR_DIRECTORY)
{
/* It is a sub-directory. Check if we are be asked to remove
* a directory or a file.
*/
if (!directory)
{
/* We are asked to delete a file */
return -EISDIR;
}
/* We are asked to delete a directory. Check if this
* sub-directory is empty
*/
dirinfo.fd_currcluster = dircluster;
dirinfo.fd_currsector = fat_cluster2sector(fs, dircluster);
dirinfo.fd_index = 2;
/* Loop until either (1) an entry is found in the directory
* (error), (2) the directory is found to be empty, or (3) some
* error occurs.
*/
for (;;)
{
unsigned int subdirindex;
ubyte *subdirentry;
/* Make sure that the sector containing the of the
* subdirectory sector is in the cache
*/
ret = fat_fscacheread(fs, dirinfo.fd_currsector);
if (ret < 0)
{
return ret;
}
/* Get a reference to the next entry in the directory */
subdirindex = (dirinfo.fd_index & DIRSEC_NDXMASK(fs)) * 32;
subdirentry = &fs->fs_buffer[subdirindex];
/* Is this the last entry in the direcory? */
if (subdirentry[DIR_NAME] == DIR0_ALLEMPTY)
{
/* Yes then the directory is empty. Break out of the
* loop and delete the directory.
*/
break;
}
/* Check if the next entry refers to a file or directory */
if (subdirentry[DIR_NAME] != DIR0_EMPTY &&
!(DIR_GETATTRIBUTES(subdirentry) & FATATTR_VOLUMEID))
{
/* The directory is not empty */
return -ENOTEMPTY;
}
/* Get the next directgory entry */
ret = fat_nextdirentry(fs, &dirinfo);
if (ret < 0)
{
return ret;
}
}
}
else
{
/* It is a file. Check if we are be asked to remove a directory
* or a file.
*/
if (directory)
{
/* We are asked to remove a directory */
return -ENOTDIR;
}
}
/* Make sure that the directory containing the entry to be deleted is
* in the cache.
*/
ret = fat_fscacheread(fs, dirsector);
if (ret < 0)
{
return ret;
}
/* Mark the directory entry 'deleted' */
dirinfo.fd_entry[DIR_NAME] = DIR0_EMPTY;
fs->fs_dirty = TRUE;
/* And remove the cluster chain making up the subdirectory */
ret = fat_removechain(fs, dircluster);
if (ret < 0)
{
return ret;
}
/* Update the FSINFO sector (FAT32) */
ret = fat_updatefsinfo(fs);
if (ret < 0)
{
return ret;
}
return OK;
}
/****************************************************************************
* Name: fat_fscacheread
*
@ -2092,7 +2269,7 @@ int fat_ffcacheinvalidate(struct fat_mountpt_s *fs, struct fat_file_s *ff)
* Name: fat_updatefsinfo
*
* Desciption: Flush evertyhing buffered for the mountpoint and update
* the FSINFO sector, if appropriate
* the FSINFO sector, if appropriate
*
****************************************************************************/

View File

@ -122,7 +122,7 @@ struct mountpt_operations
* information to manage privileges.
*/
int (*open)(FAR struct file *filp, const char *rel_path,
int (*open)(FAR struct file *filp, const char *relpath,
int oflags, mode_t mode);
/* The following methods must be identical in signature and position because
@ -150,10 +150,10 @@ struct mountpt_operations
int (*bind)(FAR struct inode *blkdriver, const void *data, void **handle);
int (*unbind)(void *handle);
int (*unlink)(struct inode *mountpt, const char *rel_path);
int (*mkdir)(struct inode *mountpt, const char *rel_path, mode_t mode);
int (*rmdir)(struct inode *mountpt, const char *rel_path);
int (*rename)(struct inode *mountpt, const char *old_relpath, const char *new_relpath);
int (*unlink)(struct inode *mountpt, const char *relpath);
int (*mkdir)(struct inode *mountpt, const char *relpath, mode_t mode);
int (*rmdir)(struct inode *mountpt, const char *relpath);
int (*rename)(struct inode *mountpt, const char *oldrelpath, const char *newrelpath);
/* NOTE: More operations will be needed here to support: disk usage stats
* file stat(), file attributes, file truncation, etc.