/*
* MSDOS directory handlers implementation
*
* Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
* Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.OARcorp.com/rtems/license.html.
*
* @(#) $Id$
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <rtems/libio_.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include "fat.h"
#include "fat_fat_operations.h"
#include "fat_file.h"
#include "msdos.h"
/* msdos_dir_open --
* Open fat-file which correspondes to the directory being opened and
* set offset field of file control block to zero.
*
* PARAMETERS:
* iop - file control block
* pathname - name
* flag - flags
* mode - mode
*
* RETURNS:
* RC_OK, if directory opened successfully, or -1 if error occured (errno
* set apropriately)
*/
int
msdos_dir_open(rtems_libio_t *iop, const char *pathname, unsigned32 flag,
unsigned32 mode)
{
int rc = RC_OK;
rtems_status_code sc = RTEMS_SUCCESSFUL;
msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info;
fat_file_fd_t *fat_fd = iop->file_info;
sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL)
set_errno_and_return_minus_one( EIO );
rc = fat_file_reopen(fat_fd);
if (rc != RC_OK)
{
rtems_semaphore_release(fs_info->vol_sema);
return rc;
}
iop->offset = 0;
rtems_semaphore_release(fs_info->vol_sema);
return RC_OK;
}
/* msdos_dir_close --
* Close fat-file which correspondes to the directory being closed
*
* PARAMETERS:
* iop - file control block
*
* RETURNS:
* RC_OK, if directory closed successfully, or -1 if error occured (errno
* set apropriately.
*/
int
msdos_dir_close(rtems_libio_t *iop)
{
int rc = RC_OK;
rtems_status_code sc = RTEMS_SUCCESSFUL;
msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info;
fat_file_fd_t *fat_fd = iop->file_info;
sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL)
set_errno_and_return_minus_one( EIO );
rc = fat_file_close(iop->pathinfo.mt_entry, fat_fd);
if (rc != RC_OK)
{
rtems_semaphore_release(fs_info->vol_sema);
return rc;
}
rtems_semaphore_release(fs_info->vol_sema);
return RC_OK;
}
/* msdos_dir_read --
* This routine will read the next directory entry based on the directory
* offset. The offset should be equal to -n- time the size of an
* individual dirent structure. If n is not an integer multiple of the
* sizeof a dirent structure, an integer division will be performed to
* determine directory entry that will be returned in the buffer. Count
* should reflect -m- times the sizeof dirent bytes to be placed in the
* buffer.
* If there are not -m- dirent elements from the current directory
* position to the end of the exisiting file, the remaining entries will
* be placed in the buffer and the returned value will be equal to
* -m actual- times the size of a directory entry.
*
* PARAMETERS:
* iop - file control block
* buffer - buffer provided by user
* count - count of bytes to read
*
* RETURNS:
* the number of bytes read on success, or -1 if error occured (errno
* set apropriately).
*/
ssize_t
msdos_dir_read(rtems_libio_t *iop, void *buffer, unsigned32 count)
{
int rc = RC_OK;
rtems_status_code sc = RTEMS_SUCCESSFUL;
msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info;
fat_file_fd_t *fat_fd = iop->file_info;
fat_file_fd_t *tmp_fat_fd = NULL;
struct dirent tmp_dirent;
unsigned32 start = 0;
ssize_t ret = 0;
unsigned32 cmpltd = 0;
unsigned32 j = 0, i = 0;
unsigned32 bts2rd = 0;
unsigned32 cur_cln = 0;
/*
* cast start and count - protect against using sizes that are not exact
* multiples of the -dirent- size. These could result in unexpected
* results
*/
start = iop->offset / sizeof(struct dirent);
count = (count / sizeof(struct dirent)) * sizeof(struct dirent);
/*
* optimization: we know that root directory for FAT12/16 volumes is
* sequential set of sectors and any cluster is sequential set of sectors
* too, so read such set of sectors is quick operation for low-level IO
* layer.
*/
bts2rd = (FAT_FD_OF_ROOT_DIR(fat_fd) &&
(fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) ?
fat_fd->fat_file_size :
fs_info->fat.vol.bpc;
sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL)
set_errno_and_return_minus_one(EIO);
while (count > 0)
{
/*
* fat-file is already opened by open call, so read it
* Always read directory fat-file from the beggining because of MSDOS
* directories feature :( - we should count elements currently
* present in the directory because there may be holes :)
*/
ret = fat_file_read(iop->pathinfo.mt_entry, fat_fd, (j * bts2rd),
bts2rd, fs_info->cl_buf);
if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
{
rtems_semaphore_release(fs_info->vol_sema);
set_errno_and_return_minus_one(EIO);
}
for (i = 0; i < ret; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
{
if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) ==
MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
{
rtems_semaphore_release(fs_info->vol_sema);
return cmpltd;
}
if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) ==
MSDOS_THIS_DIR_ENTRY_EMPTY)
continue;
/*
* skip active entries until get the entry to start from
*/
if (start)
{
start--;
continue;
}
/*
* Move the entry to the return buffer
*
* unfortunately there is no method to extract ino except to
* open fat-file descriptor :( ... so, open it
*/
/* get number of cluster we are working with */
rc = fat_file_ioctl(iop->pathinfo.mt_entry, fat_fd, F_CLU_NUM,
j * bts2rd, &cur_cln);
if (rc != RC_OK)
{
rtems_semaphore_release(fs_info->vol_sema);
return rc;
}
rc = fat_file_open(iop->pathinfo.mt_entry, cur_cln, i,
&tmp_fat_fd);
if (rc != RC_OK)
{
rtems_semaphore_release(fs_info->vol_sema);
return rc;
}
tmp_fat_fd->info_cln = cur_cln;
tmp_fat_fd->info_ofs = i;
/* fill in dirent structure */
/* XXX: from what and in what d_off should be computed ?! */
tmp_dirent.d_off = start + cmpltd;
tmp_dirent.d_reclen = sizeof(struct dirent);
tmp_dirent.d_ino = tmp_fat_fd->ino;
tmp_dirent.d_namlen = MSDOS_SHORT_NAME_LEN;
memcpy(tmp_dirent.d_name, MSDOS_DIR_NAME((fs_info->cl_buf + i)),
MSDOS_SHORT_NAME_LEN);
/* d_name is null-terminated */
tmp_dirent.d_name[MSDOS_SHORT_NAME_LEN] = 0;
memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent));
iop->offset = iop->offset + sizeof(struct dirent);
cmpltd += (sizeof(struct dirent));
count -= (sizeof(struct dirent));
/* inode number extracted, close fat-file */
rc = fat_file_close(iop->pathinfo.mt_entry, tmp_fat_fd);
if (rc != RC_OK)
{
rtems_semaphore_release(fs_info->vol_sema);
return rc;
}
if (count <= 0)
break;
}
j++;
}
rtems_semaphore_release(fs_info->vol_sema);
return cmpltd;
}
/* msdos_dir_write --
* no write for directory
*/
/* msdos_dir_lseek --
*
* This routine will behave in one of three ways based on the state of
* argument whence. Based on the state of its value the offset argument will
* be interpreted using one of the following methods:
*
* SEEK_SET - offset is the absolute byte offset from the start of the
* logical start of the dirent sequence that represents the
* directory
* SEEK_CUR - offset is used as the relative byte offset from the current
* directory position index held in the iop structure
* SEEK_END - N/A --> This will cause an assert.
*
* PARAMETERS:
* iop - file control block
* offset - offset
* whence - predefine directive
*
* RETURNS:
* RC_OK on success, or -1 if error occured (errno
* set apropriately).
*/
int
msdos_dir_lseek(rtems_libio_t *iop, off_t offset, int whence)
{
switch (whence)
{
case SEEK_SET:
case SEEK_CUR:
break;
/*
* Movement past the end of the directory via lseek is not a
* permitted operation
*/
case SEEK_END:
default:
set_errno_and_return_minus_one( EINVAL );
break;
}
return RC_OK;
}
/* msdos_dir_stat --
*
* This routine will obtain the following information concerning the current
* directory:
* st_dev device id
* st_ino node serial number :)
* st_mode mode extracted from the node
* st_size total size in bytes
* st_blksize blocksize for filesystem I/O
* st_blocks number of blocks allocated
* stat_mtime time of last modification
*
* PARAMETERS:
* loc - this directory
* buf - stat buffer provided by user
*
* RETURNS:
* RC_OK and filled stat buffer on success, or -1 if error occured (errno
* set apropriately).
*/
int
msdos_dir_stat(
rtems_filesystem_location_info_t *loc,
struct stat *buf
)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
msdos_fs_info_t *fs_info = loc->mt_entry->fs_info;
fat_file_fd_t *fat_fd = loc->node_access;
sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL)
set_errno_and_return_minus_one(EIO);
buf->st_dev = fs_info->fat.vol.dev;
buf->st_ino = fat_fd->ino;
buf->st_mode = S_IFDIR;
buf->st_rdev = 0ll;
buf->st_size = fat_fd->fat_file_size;
buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS;
buf->st_blksize = fs_info->fat.vol.bps;
buf->st_mtime = fat_fd->mtime;
rtems_semaphore_release(fs_info->vol_sema);
return RC_OK;
}
/* msdos_dir_truncate --
* No truncate for directory.
*
* PARAMETERS:
*
* RETURNS:
*
*/
/* msdos_dir_sync --
* The following routine does a syncronization on a MSDOS directory node.
* DIR_WrtTime, DIR_WrtDate and DIR_fileSize fields of 32 Bytes Directory
* Entry Structure(see M$ White Paper) should not be updated for
* directories, so only call to corresponding fat-file routine.
*
* PARAMETERS:
* iop - file control block
*
* RETURNS:
* RC_OK on success, or -1 if error occured (errno set apropriately).
*/
int
msdos_dir_sync(rtems_libio_t *iop)
{
int rc = RC_OK;
rtems_status_code sc = RTEMS_SUCCESSFUL;
fat_file_fd_t *fat_fd = iop->file_info;
msdos_fs_info_t *fs_info = iop->pathinfo.mt_entry->fs_info;
sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL)
set_errno_and_return_minus_one(EIO);
rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd);
rtems_semaphore_release(fs_info->vol_sema);
return rc;
}
/* msdos_dir_rmnod --
* Remove directory node.
*
* Check that this directory node is not opened as fat-file, is empty and
* not filesystem root node. If all this conditions met then delete.
*
* PARAMETERS:
* pathloc - node description
*
* RETURNS:
* RC_OK on success, or -1 if error occured (errno set apropriately).
*/
int
msdos_dir_rmnod(rtems_filesystem_location_info_t *pathloc)
{
int rc = RC_OK;
rtems_status_code sc = RTEMS_SUCCESSFUL;
msdos_fs_info_t *fs_info = pathloc->mt_entry->fs_info;
fat_file_fd_t *fat_fd = pathloc->node_access;
rtems_boolean is_empty = FALSE;
sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL)
set_errno_and_return_minus_one(EIO);
/*
* We deny attemp to delete open directory (if directory is current
* directory we assume it is open one)
*/
if (fat_fd->links_num > 1)
{
rtems_semaphore_release(fs_info->vol_sema);
set_errno_and_return_minus_one(EBUSY);
}
/*
* You cannot remove a node that still has children
*/
rc = msdos_dir_is_empty(pathloc->mt_entry, fat_fd, &is_empty);
if (rc != RC_OK)
{
rtems_semaphore_release(fs_info->vol_sema);
return rc;
}
if (!is_empty)
{
rtems_semaphore_release(fs_info->vol_sema);
set_errno_and_return_minus_one(ENOTEMPTY);
}
/*
* You cannot remove the file system root node.
*/
if (pathloc->mt_entry->mt_fs_root.node_access == pathloc->node_access)
{
rtems_semaphore_release(fs_info->vol_sema);
set_errno_and_return_minus_one(EBUSY);
}
/*
* You cannot remove a mountpoint.
* not used - mount() not implemenetd yet.
*/
/* mark file removed */
rc = msdos_set_first_char4file_name(pathloc->mt_entry, fat_fd->info_cln,
fat_fd->info_ofs,
MSDOS_THIS_DIR_ENTRY_EMPTY);
if (rc != RC_OK)
{
rtems_semaphore_release(fs_info->vol_sema);
return rc;
}
fat_file_mark_removed(pathloc->mt_entry, fat_fd);
rtems_semaphore_release(fs_info->vol_sema);
return rc;
}