1028 lines
27 KiB
C
1028 lines
27 KiB
C
/*
|
|
*=============================================================================
|
|
* File: dos_node.c
|
|
* Purpose: DOS filesystem file operations.
|
|
*=============================================================================
|
|
*/
|
|
#include "dos_fs.h"
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <rpc/rpc.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
|
|
extern uid_t uid;
|
|
extern gid_t gid;
|
|
extern int debug;
|
|
|
|
static char dot[] = ".";
|
|
static char dotdot[] = "..";
|
|
static char dotname[] = ". ";
|
|
static char dotdotname[] = ".. ";
|
|
char nullext[] = " ";
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_readnode()
|
|
* This routine is used to find information about the specified
|
|
* file from disk directory entry.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_readnode(dnode_t *dp)
|
|
{
|
|
file_t f;
|
|
dfile_t *df;
|
|
dfs_t *dfs;
|
|
int error;
|
|
u_short offset;
|
|
u_short cluster;
|
|
|
|
dfs = dp->d_dfs;
|
|
if (dp->d_dir != NULL)
|
|
vfat_dir_write(dp);
|
|
dp->d_uid = UID;
|
|
dp->d_gid = GID;
|
|
|
|
/*
|
|
* Fake up a dnode for the root
|
|
*/
|
|
if (dp->d_fno == ROOTFNO) {
|
|
dp->d_ftype = NFDIR;
|
|
dp->d_dir = (dfile_t *) safe_malloc(dfs->dfs_rootsize);
|
|
lseek(dfs->dfs_fd, dfs->dfs_rootaddr, 0);
|
|
if (read(dfs->dfs_fd, &dp->d_dir[2],
|
|
dfs->dfs_rootsize-2*DIR_ENTRY_SIZE) < 0)
|
|
return (errno);
|
|
/*
|
|
* Make up entries for "." and ".."
|
|
*/
|
|
df = dp->d_dir;
|
|
strncpy(df->df_name, dotname, 8);
|
|
strncpy(df->df_ext, nullext, 3);
|
|
df++;
|
|
strncpy(df->df_name, dotdotname, 8);
|
|
strncpy(df->df_ext, nullext, 3);
|
|
|
|
dp->d_mode = READ_MODE | WRITE_MODE | SEARCH_MODE;
|
|
dp->d_pfno = ROOTFNO;
|
|
dp->d_size = dfs->dfs_rootsize;
|
|
dp->d_time = time(NULL);
|
|
return (0);
|
|
}
|
|
f.f_offset = offset = OFFSET(dp->d_fno);
|
|
f.f_cluster = cluster = CLUSTER(dp->d_fno);
|
|
if (cluster == ROOT_CLUSTER){
|
|
xtract_fp_from_root(dfs, (dfile_t *) dfs->dfs_root->d_dir,
|
|
offset, &f);
|
|
}
|
|
else {
|
|
error = vfat_dir_lookup(dp, &f);
|
|
if (error == -1){
|
|
error = vfat_dirent_read_disk(dfs, &f);
|
|
if (error){
|
|
/* Unable to read file entry */
|
|
return (error);
|
|
}
|
|
}
|
|
}
|
|
df = &f.f_entries[0];
|
|
dp->d_start = DF_START(df);
|
|
to_unixtime(df->df_date, df->df_time, &dp->d_time);
|
|
if (df->df_attribute & ATTRIB_DIR) {
|
|
dp->d_ftype = NFDIR;
|
|
dp->d_mode = READ_MODE | WRITE_MODE | SEARCH_MODE;
|
|
dp->d_size = subdir_size(dp);
|
|
} else {
|
|
dp->d_ftype = NFREG;
|
|
if (df->df_attribute & ATTRIB_RDONLY)
|
|
dp->d_mode = READ_MODE | SEARCH_MODE;
|
|
else dp->d_mode = READ_MODE | WRITE_MODE | SEARCH_MODE;
|
|
dp->d_size = DF_SIZE(df);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_access()
|
|
* This routine is used to check the access mode
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_access(dnode_t *dp, struct authunix_parms *aup, int amode)
|
|
{
|
|
if (aup->aup_uid == 0)
|
|
return 0;
|
|
if ((dp->d_mode & amode) == amode)
|
|
return 0;
|
|
return (EACCES);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_getattr()
|
|
* This routine is used to get file attributes
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_getattr(dnode_t *dp, fattr *fa)
|
|
{
|
|
fa->type = dp->d_ftype;
|
|
fa->mode = dp->d_mode;
|
|
switch (fa->type){
|
|
case NFREG:
|
|
fa->mode |= NFSMODE_REG;
|
|
break;
|
|
case NFDIR:
|
|
fa->mode |= NFSMODE_DIR;
|
|
break;
|
|
case NFBLK:
|
|
fa->mode |= NFSMODE_BLK;
|
|
break;
|
|
case NFCHR:
|
|
fa->mode |= NFSMODE_CHR;
|
|
break;
|
|
case NFLNK:
|
|
fa->mode |= NFSMODE_LNK;
|
|
break;
|
|
case NFSOCK:
|
|
fa->mode |= NFSMODE_SOCK;
|
|
break;
|
|
}
|
|
fa->nlink = 1;
|
|
fa->uid = dp->d_uid;
|
|
fa->gid = dp->d_gid;
|
|
fa->size = dp->d_size;
|
|
fa->blocksize = 512;
|
|
fa->rdev = 0;
|
|
fa->blocks = (dp->d_size + 511) / 512;
|
|
fa->fsid = dp->d_dfs->dfs_dev;
|
|
fa->fileid = dp->d_fno;
|
|
fa->atime.seconds = dp->d_time;
|
|
fa->atime.useconds = 0;
|
|
fa->mtime.seconds = dp->d_time;
|
|
fa->mtime.useconds = 0;
|
|
fa->ctime.seconds = dp->d_time;
|
|
fa->ctime.useconds = 0;
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_setattr()
|
|
* This routine is used to set file attributes
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_setattr(dnode_t *dp, sattr *sa)
|
|
{
|
|
int archive_attrib = 0;
|
|
|
|
if (sa->uid != -1)
|
|
dp->d_uid = (uid_t) UID;
|
|
if (sa->gid != -1)
|
|
dp->d_gid = (gid_t) GID;
|
|
|
|
if (sa->mode != -1 && !(sa->mode & WRITE_MODE))
|
|
dp->d_mode = DEFAULTMODE;
|
|
else dp->d_mode = DEFAULTMODE;
|
|
|
|
if (sa->mtime.seconds != -1)
|
|
dp->d_time = sa->mtime.seconds;
|
|
if (sa->size != -1 && dp->d_ftype != NFDIR && dp->d_size != sa->size) {
|
|
archive_attrib = ATTRIB_ARCHIVE;
|
|
dp->d_size = sa->size;
|
|
}
|
|
timeoutp = &timeout;
|
|
return (set_attribute(dp, archive_attrib));
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_lookup()
|
|
* This routine is used to find whether the specified file is one of the
|
|
* entries in the given directory. if so, allocate a dnode for it
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_lookup(dnode_t *dp, char *name, dnode_t **cdp)
|
|
{
|
|
dfs_t *dfs = dp->d_dfs;
|
|
int error;
|
|
file_t f;
|
|
|
|
printf(" dos_lookup(): file = %s\n", name);
|
|
|
|
if (strcmp(name, dot) == 0)
|
|
return dos_getnode(dfs, dp->d_fno, cdp);
|
|
if (strcmp(name, dotdot) == 0)
|
|
return dos_getnode(dfs, dp->d_pfno, cdp);
|
|
error = vfat_dir_getent_lname(dp, name, &f);
|
|
if (error)
|
|
return (error);
|
|
error = dos_getnode(dfs, FNO(f.f_cluster, f.f_offset), cdp);
|
|
if (error)
|
|
return (error);
|
|
(*cdp)->d_pfno = dp->d_fno;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_create()
|
|
* This routine is used to create a file. A directory entry as well as
|
|
* at least one cluster is allocated for the new file.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_create(dnode_t *dp, char *name, sattr *sa,
|
|
dnode_t **cdp, struct authunix_parms *aup)
|
|
{
|
|
file_t f;
|
|
dfile_t *df;
|
|
dfs_t *dfs;
|
|
time_t mtime;
|
|
u_int size;
|
|
u_short ccount, cluster;
|
|
int error, sflag;
|
|
char sname[MSDOS_NAME];
|
|
|
|
printf(" dos_create(): file = %s\n", name);
|
|
|
|
dfs = dp->d_dfs;
|
|
if (strcmp(name, dot) == 0 || strcmp(name, dotdot) == 0)
|
|
return (EACCES);
|
|
/*
|
|
* Search parent directory to see if
|
|
* a file of this name already exists.
|
|
*/
|
|
error = vfat_dir_getent_lname(dp, name, &f);
|
|
if (error == 0){
|
|
/*
|
|
* If file already exists, merely change attr
|
|
* If file's a directory, error.
|
|
* If file's a read only file, error.
|
|
*/
|
|
if (IS_DIR(f))
|
|
return (EACCES);
|
|
if (IS_RDONLY(f))
|
|
return (EACCES);
|
|
|
|
error = dos_getnode(dfs, FNO(f.f_cluster, f.f_offset), cdp);
|
|
if (error)
|
|
return (error);
|
|
(*cdp)->d_pfno = dp->d_fno;
|
|
return (dos_setattr(*cdp, sa));
|
|
}
|
|
|
|
if (error != ENOENT){
|
|
/* A file of this name exists */
|
|
return (EEXIST);
|
|
}
|
|
|
|
error = vfat_create_sname(dp, name, sname, &sflag);
|
|
if (error == ERROR){
|
|
/* Unable to map this long name to a short one */
|
|
return (EINVAL);
|
|
}
|
|
|
|
vfat_dirent_packname(&f, name, sname, sflag);
|
|
vfat_dirent_print(&f);
|
|
|
|
error = vfat_dir_freent(dp, f.f_nentries, &f.f_cluster, &f.f_offset);
|
|
if (error){
|
|
/* Unable to dig up enough space in directory file */
|
|
return (ENOSPC);
|
|
}
|
|
|
|
if (sa->size == -1)
|
|
size = 0;
|
|
else size = sa->size;
|
|
ccount = (size + dfs->dfs_clustersize - 1) / dfs->dfs_clustersize;
|
|
if (ccount == 0)
|
|
ccount = 1;
|
|
if (ccount > dfs->dfs_freeclustercount){
|
|
/* Unable to accomodate this file */
|
|
return (ENOSPC);
|
|
}
|
|
cluster = vfat_free_fat(dfs);
|
|
if (cluster == 0){
|
|
/* Unable to allocate free cluster */
|
|
return (ENOSPC);
|
|
}
|
|
|
|
error = vfat_update_fat(dfs);
|
|
if (error){
|
|
/* Unable to update fat */
|
|
return (EINVAL);
|
|
}
|
|
|
|
df = &f.f_entries[0];
|
|
df->df_attribute = ATTRIB_ARCHIVE;
|
|
if (sa->mode != -1 && !(sa->mode & WRITE_MODE))
|
|
df->df_attribute |= ATTRIB_RDONLY;
|
|
|
|
df->df_size[0] = (size & 0xFF);
|
|
df->df_size[1] = (size >> 8) & 0xFF;
|
|
df->df_size[2] = (size >> 16) & 0xFF;
|
|
df->df_size[3] = (size >> 24) & 0xFF;
|
|
|
|
df->df_start[0] = (cluster & 0xFF);
|
|
df->df_start[1] = (cluster >> 8);
|
|
|
|
mtime = time(NULL);
|
|
to_dostime(df->df_date, df->df_time, &mtime);
|
|
bzero(df->df_reserved, 10);
|
|
|
|
error = vfat_dir_writent(dp, &f);
|
|
if (error){
|
|
/* Unable to write directory entry */
|
|
return (EINVAL);
|
|
}
|
|
|
|
error = dos_getnode(dfs, FNO(f.f_cluster, f.f_offset), cdp);
|
|
if (error){
|
|
/* Unable to obtain a node from node cache */
|
|
return (error);
|
|
}
|
|
|
|
(*cdp)->d_pfno = dp->d_fno;
|
|
(*cdp)->d_uid = UID;
|
|
(*cdp)->d_gid = GID;
|
|
|
|
if (sa->mode != -1 && !(sa->mode & WRITE_MODE))
|
|
(*cdp)->d_mode = DEFAULTMODE;
|
|
else (*cdp)->d_mode = DEFAULTMODE;
|
|
|
|
dp->d_time = mtime;
|
|
timeoutp = &timeout;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_remove()
|
|
* This routine is used to delete a file. The directory entry associated with
|
|
* this file is wiped out.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_remove(dnode_t *dp, char *name)
|
|
{
|
|
int error;
|
|
file_t f;
|
|
dnode_t *cdp;
|
|
|
|
/*
|
|
* Don't trash "." and ".."
|
|
* Check to see that it's not a directory remove.
|
|
* Get node associated with this file entry.
|
|
* Delete this file entry from the parent directory.
|
|
*/
|
|
if (!strcmp(name, dot) || !strcmp(name, dotdot))
|
|
return (EACCES);
|
|
error = vfat_dir_getent_lname(dp, name, &f);
|
|
if (error)
|
|
return (ENOENT);
|
|
|
|
if (IS_DIR(f))
|
|
return (EISDIR);
|
|
|
|
error = dos_getnode(dp->d_dfs, FNO(f.f_cluster, f.f_offset), &cdp);
|
|
if (error)
|
|
return (error);
|
|
|
|
error = vfat_dir_delent(dp, &f);
|
|
if (error){
|
|
dos_putnode(cdp);
|
|
return (error);
|
|
}
|
|
|
|
dos_purgenode(cdp);
|
|
dp->d_time = time(NULL);
|
|
timeoutp = &timeout;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_mkdir()
|
|
* This routine is used to create a directory.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_mkdir(dnode_t *dp, char *name, sattr *sa,
|
|
dnode_t **cdp, struct authunix_parms *aup)
|
|
{
|
|
dfs_t *dfs;
|
|
file_t f;
|
|
dfile_t *df;
|
|
time_t mtime;
|
|
u_short cluster;
|
|
int error, sflag;
|
|
char sname[MSDOS_NAME];
|
|
|
|
printf(" dos_mkdir(): directory = %s\n", name);
|
|
|
|
dfs = dp->d_dfs;
|
|
if (!strcmp(name, dot) || !strcmp(name, dotdot))
|
|
return (EACCES);
|
|
|
|
error = vfat_dir_getent_lname(dp, name, &f);
|
|
if (error != ENOENT){
|
|
/* A file/directory of this name already exists */
|
|
return (EEXIST);
|
|
}
|
|
|
|
error = vfat_create_sname(dp, name, sname, &sflag);
|
|
if (error == ERROR){
|
|
/* Unable to map this long name to a short one */
|
|
return (EINVAL);
|
|
}
|
|
vfat_dirent_packname(&f, name, sname, sflag);
|
|
|
|
error = vfat_dir_freent(dp, f.f_nentries, &f.f_cluster, &f.f_offset);
|
|
if (error){
|
|
/* Unable to dig up enough space in directory file */
|
|
return (ENOSPC);
|
|
}
|
|
|
|
vfat_dirent_print(&f);
|
|
|
|
cluster = vfat_free_fat(dfs);
|
|
if (cluster == 0){
|
|
/* Unable to allocate new cluster in fat */
|
|
return (ENOSPC);
|
|
}
|
|
|
|
error = vfat_update_fat(dfs);
|
|
if (error){
|
|
/* Unable to update fat */
|
|
return (EINVAL);
|
|
}
|
|
|
|
df = &f.f_entries[0];
|
|
df->df_attribute = ATTRIB_DIR;
|
|
df->df_size[0] = 0;
|
|
df->df_size[1] = 0;
|
|
df->df_size[2] = 0;
|
|
df->df_size[3] = 0;
|
|
df->df_start[0] = (cluster & 0xFF);
|
|
df->df_start[1] = (cluster >> 8);
|
|
|
|
mtime = time(NULL);
|
|
to_dostime(df->df_date, df->df_time, &mtime);
|
|
bzero(df->df_reserved, 10);
|
|
|
|
error = vfat_dir_writent(dp, &f);
|
|
if (error){
|
|
/* Unable to write directory entry */
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* Create a "." and ".." in subdirectory */
|
|
bzero(dfs->dfs_buf, dfs->dfs_clustersize);
|
|
df = (dfile_t *) dfs->dfs_buf;
|
|
*df = f.f_entries[0];
|
|
strncpy(df->df_name, dotname, 8);
|
|
strncpy(df->df_ext, nullext, 3);
|
|
|
|
df++;
|
|
*df = f.f_entries[0];
|
|
strncpy(df->df_name, dotdotname, 8);
|
|
strncpy(df->df_ext, nullext, 3);
|
|
|
|
if (dp->d_fno == ROOTFNO){
|
|
df->df_start[0] = 0;
|
|
df->df_start[1] = 0;
|
|
}
|
|
else {
|
|
df->df_start[0] = (dp->d_start & 0xFF);
|
|
df->df_start[1] = (dp->d_start >> 8);
|
|
}
|
|
|
|
error = vfat_clust_writ(dfs, DISK_ADDR(dfs, cluster), dfs->dfs_buf);
|
|
if (error){
|
|
/* Unable to write directory cluster */
|
|
return (EINVAL);
|
|
}
|
|
|
|
error = dos_getnode(dfs, FNO(f.f_cluster, f.f_offset), cdp);
|
|
if (error){
|
|
/* Unable to obtain a node from node cache */
|
|
return (error);
|
|
}
|
|
|
|
(*cdp)->d_pfno = dp->d_fno;
|
|
(*cdp)->d_uid = UID;
|
|
(*cdp)->d_gid = GID;
|
|
(*cdp)->d_mode = (sa->mode != (u_int)-1) ? sa->mode : DEFAULTMODE;
|
|
|
|
dp->d_time = mtime;
|
|
timeoutp = &timeout;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_rmdir()
|
|
* This routine is used to remove a directory.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_rmdir(dnode_t *dp, char *name)
|
|
{
|
|
file_t f;
|
|
dfile_t *df;
|
|
dnode_t *cdp;
|
|
dfs_t *dfs;
|
|
time_t mtime;
|
|
u_int size;
|
|
u_short ccount, cluster;
|
|
int i, entry_count, error;
|
|
|
|
dfs = dp->d_dfs;
|
|
cdp = NULL;
|
|
printf("dos_rmdir()");
|
|
if (!strcmp(name, dot) || !strcmp(name, dotdot))
|
|
return (EACCES);
|
|
|
|
error = vfat_dir_getent_lname(dp, name, &f);
|
|
if (error == ENOENT){
|
|
/* Unable to find specified directory */
|
|
return (ENOENT);
|
|
}
|
|
|
|
if (!IS_DIR(f)){
|
|
/* Unable to find specified directory */
|
|
return (ENOTDIR);
|
|
}
|
|
|
|
error = dos_getnode(dp->d_dfs, FNO(f.f_cluster, f.f_offset), &cdp);
|
|
if (error){
|
|
/* Unable to find node in node cache */
|
|
return (error);
|
|
}
|
|
|
|
cdp->d_pfno = dp->d_fno;
|
|
if (cdp->d_dir == NULL){
|
|
error = vfat_dir_read(cdp);
|
|
if (error)
|
|
goto rmdir_done;
|
|
}
|
|
/*
|
|
* See if directory is empy or not.
|
|
* Entries "." and ".." are allowed.
|
|
*/
|
|
for (i = cdp->d_size / DIR_ENTRY_SIZE, df = cdp->d_dir, entry_count = 0;
|
|
i > 0; i--, df++) {
|
|
if (df->df_name[0] == LAST_ENTRY)
|
|
break;
|
|
if (df->df_name[0] == REUSABLE_ENTRY)
|
|
continue;
|
|
if (++entry_count > 2) {
|
|
error = ENOTEMPTY;
|
|
goto rmdir_done;
|
|
}
|
|
}
|
|
|
|
error = vfat_dir_delent(dp, &f);
|
|
if (error){
|
|
/* Unable to delete directory entry */
|
|
goto rmdir_done;
|
|
}
|
|
|
|
dos_purgenode(cdp);
|
|
cdp = NULL;
|
|
|
|
dp->d_time = time(NULL);
|
|
|
|
rmdir_done:
|
|
if (cdp)
|
|
dos_putnode(cdp);
|
|
timeoutp = &timeout;
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_readdir()
|
|
* This routine is used to read a directory
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_readdir(dnode_t *dp, nfscookie cookie, u_int count,
|
|
entry *entries, u_int *eof, u_int *nread)
|
|
{
|
|
dfs_t *dfs;
|
|
file_t f;
|
|
dfile_t *df;
|
|
u_short cluster;
|
|
entry *ent, *o_entries;
|
|
int entsize, error;
|
|
u_int i, limit, offset;
|
|
char sname[MSDOS_NAME];
|
|
char lname[NFS_MAXNAMLEN+1];
|
|
|
|
printf("dos_readdir()");
|
|
ent = NULL;
|
|
dfs = dp->d_dfs;
|
|
o_entries = entries;
|
|
offset = *(u_int *) cookie;
|
|
limit = dp->d_size/DIR_ENTRY_SIZE;
|
|
*eof = 0;
|
|
|
|
if (offset >= limit){
|
|
/* No entries could be read */
|
|
*nread = 0;
|
|
*eof = 1;
|
|
return (0);
|
|
}
|
|
if (dp->d_dir == NULL){
|
|
error = vfat_dir_read(dp);
|
|
if (error)
|
|
return (error);
|
|
}
|
|
df = &dp->d_dir[offset];
|
|
/*
|
|
* For root cluster, we only look at this cluster.
|
|
* For other clusters, we look at all clusters in dir.
|
|
*/
|
|
if (dp->d_fno == ROOTFNO)
|
|
cluster = ROOT_CLUSTER;
|
|
else cluster = to_cluster(dp, offset);
|
|
/*
|
|
* Start scanning all the directory entries in dir.
|
|
*/
|
|
while (count > 0 && offset < limit){
|
|
if (df->df_name[0] == LAST_ENTRY) {
|
|
*eof = 1;
|
|
break;
|
|
}
|
|
if (df->df_name[0] == REUSABLE_ENTRY || IS_LABEL(df)){
|
|
/* We've encountered either a blank */
|
|
/* directory entry or volume label */
|
|
df++;
|
|
offset++;
|
|
if (cluster != ROOT_CLUSTER){
|
|
if (offset % DIR_ENTRY_PER_CLUSTER(dfs) == 0)
|
|
cluster = (*dfs->dfs_readfat)(dfs, cluster);
|
|
}
|
|
continue;
|
|
}
|
|
f.f_offset = offset;
|
|
f.f_cluster = cluster;
|
|
copy_dir_to_fp(dp, &f);
|
|
/*
|
|
* We've just read in one directory entry into "f".
|
|
* Position df correctly to point to next entry.
|
|
* Position offset correctly to point to next entry.
|
|
* Unpack the lname/sname from "f". Convert (name, ext)
|
|
* to unix format sname.
|
|
*/
|
|
df = f.f_nentries+df+1;
|
|
offset = f.f_nentries+offset+1;
|
|
vfat_dirent_upackname(&f, lname, sname);
|
|
vfat_dirent_print(&f);
|
|
to_unixname(f.f_entries[0].df_name,
|
|
f.f_entries[0].df_ext, sname);
|
|
entsize = sizeof(entry)+RNDUP(strlen(lname)+4);
|
|
if (count < entsize)
|
|
break;
|
|
count = count-entsize;
|
|
ent = entries;
|
|
entries = (entry *) ((char *) ent+entsize);
|
|
if (!strcmp(lname, dot))
|
|
ent->fileid = dp->d_fno;
|
|
else if (!strcmp(lname, dotdot))
|
|
ent->fileid = dp->d_pfno;
|
|
else ent->fileid = FNO(cluster, offset)-1;
|
|
ent->name = strcpy((char *) ent+sizeof(entry), lname);
|
|
ent->nextentry = entries;
|
|
*(u_int *) ent->cookie = offset;
|
|
/*
|
|
* Proceed to the next cluster if
|
|
* we're not looking at the root dir.
|
|
*/
|
|
if (cluster != ROOT_CLUSTER){
|
|
if (offset % DIR_ENTRY_PER_CLUSTER(dfs) == 0)
|
|
cluster = (*dfs->dfs_readfat)(dfs, cluster);
|
|
}
|
|
}
|
|
/* Calculate num. bytes read */
|
|
*nread = (char *) entries - (char *) o_entries;
|
|
if (ent)
|
|
ent->nextentry = 0;
|
|
return (0);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* symbolic link not implemented by DOS */
|
|
int dos_readlink(dnode_t *dp, u_int count, char *data)
|
|
{
|
|
return ENXIO;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_read()
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_read(dnode_t *dp, u_int offset, u_int count,
|
|
char *data, u_int *nread)
|
|
{
|
|
u_int bcount;
|
|
u_short cluster;
|
|
dfs_t *dfs;
|
|
int head;
|
|
int i, j;
|
|
u_short ncluster;
|
|
int clusteraddr, rcode;
|
|
|
|
dfs = dp->d_dfs;
|
|
/*
|
|
* Make sure there is something to read
|
|
*/
|
|
if (offset >= dp->d_size) {
|
|
*nread = 0;
|
|
return 0;
|
|
}
|
|
/*
|
|
* Don't try to read pass end of file
|
|
*/
|
|
if (offset + count > dp->d_size)
|
|
count = dp->d_size - offset;
|
|
/*
|
|
* Skip to the right cluster
|
|
*/
|
|
cluster = dp->d_start;
|
|
for (i = offset / dfs->dfs_clustersize; i > 0; i--)
|
|
cluster = (*dfs->dfs_readfat)(dfs, cluster);
|
|
*nread = 0;
|
|
/*
|
|
* Take care of non-cluster-aligned read
|
|
*/
|
|
if ((head = offset % dfs->dfs_clustersize) != 0) {
|
|
clusteraddr = DISK_ADDR(dfs, cluster);
|
|
if ((rcode = verify_cache(dfs, clusteraddr, CACHE_READ)) != 0)
|
|
return (rcode);
|
|
bcount = dfs->dfs_clustersize - head;
|
|
if (count < bcount)
|
|
bcount = count;
|
|
bcopy(&trkcache.bufp[clusteraddr-trkcache.trkbgnaddr+head],
|
|
&data[*nread], bcount);
|
|
count -= bcount;
|
|
*nread += bcount;
|
|
cluster = (*dfs->dfs_readfat)(dfs, cluster);
|
|
}
|
|
while (count != 0) {
|
|
clusteraddr = DISK_ADDR(dfs, cluster);
|
|
if ((rcode = verify_cache(dfs, clusteraddr, CACHE_READ)) != 0)
|
|
return (rcode);
|
|
if (count < dfs->dfs_clustersize) {
|
|
bcopy(&trkcache.bufp[clusteraddr-trkcache.trkbgnaddr],
|
|
&data[*nread], count);
|
|
*nread += count;
|
|
break;
|
|
}
|
|
bcopy(&trkcache.bufp[clusteraddr-trkcache.trkbgnaddr],
|
|
&data[*nread], dfs->dfs_clustersize);
|
|
count -= dfs->dfs_clustersize;
|
|
*nread += dfs->dfs_clustersize;
|
|
cluster = (*dfs->dfs_readfat)(dfs, cluster);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_write()
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_write(dnode_t *dp, u_int offset, u_int count, char *data)
|
|
{
|
|
u_int bcount;
|
|
u_short cluster, ncluster;
|
|
dfs_t *dfs;
|
|
int i, j;
|
|
int error, head;
|
|
int clusteraddr, rcode;
|
|
|
|
|
|
dfs = dp->d_dfs;
|
|
/*
|
|
* Expand the file if necessary
|
|
*/
|
|
if (offset + count > dp->d_size)
|
|
dp->d_size = offset + count;
|
|
dp->d_time = time(NULL);
|
|
if (error = set_attribute(dp, ATTRIB_ARCHIVE))
|
|
return error;
|
|
/*
|
|
* Skip to the right cluster
|
|
*/
|
|
cluster = dp->d_start;
|
|
for (i = offset / dfs->dfs_clustersize; i > 0; i--)
|
|
cluster = (*dfs->dfs_readfat)(dfs, cluster);
|
|
/*
|
|
* Take care of non-cluster-aligned write
|
|
*/
|
|
if ((head = offset % dfs->dfs_clustersize) != 0) {
|
|
bcount = dfs->dfs_clustersize - head;
|
|
if (count < bcount)
|
|
bcount = count;
|
|
clusteraddr = DISK_ADDR(dfs, cluster);
|
|
if ((rcode = verify_cache(dfs, clusteraddr, CACHE_WRITE)) != 0)
|
|
return rcode;
|
|
bcopy(&data[0],
|
|
&trkcache.bufp[clusteraddr-trkcache.trkbgnaddr+head],
|
|
bcount);
|
|
set_dirty(clusteraddr);
|
|
count -= bcount;
|
|
offset = bcount;
|
|
cluster = (*dfs->dfs_readfat)(dfs, cluster);
|
|
|
|
} else offset = 0;
|
|
bcount = dfs->dfs_clustersize;
|
|
while (count != 0) {
|
|
clusteraddr = DISK_ADDR(dfs, cluster);
|
|
if ((rcode = verify_cache(dfs, clusteraddr, CACHE_WRITE)) != 0)
|
|
return rcode;
|
|
if (count < dfs->dfs_clustersize) {
|
|
bcopy(&data[offset],
|
|
&trkcache.bufp[clusteraddr-trkcache.trkbgnaddr], count);
|
|
set_dirty(clusteraddr);
|
|
break;
|
|
}
|
|
bcopy(&data[offset],
|
|
&trkcache.bufp[clusteraddr-trkcache.trkbgnaddr],
|
|
bcount);
|
|
set_dirty(clusteraddr);
|
|
cluster = (*dfs->dfs_readfat)(dfs, cluster);
|
|
count -= bcount;
|
|
offset += bcount;
|
|
}
|
|
timeoutp = &timeout;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
* dos_rename()
|
|
* This routine is used to rename a file.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
int dos_rename(dnode_t *dp, char *name, dnode_t *tdp, char *tname)
|
|
{
|
|
file_t f;
|
|
file_t tf;
|
|
dfile_t *df;
|
|
dfs_t *dfs;
|
|
u_long fno;
|
|
u_long tfno;
|
|
time_t mtime;
|
|
dnode_t *cdp = NULL;
|
|
dnode_t *ctdp = NULL;
|
|
dfile_t dfile;
|
|
int error, sflag;
|
|
int target_file_exists;
|
|
char sname[MSDOS_NAME];
|
|
|
|
printf(" dos_rename(): file1 = %s, file2 = %s\n", name, tname);
|
|
|
|
dfs = dp->d_dfs;
|
|
if (!strcmp(name, dot) || !strcmp(name, dotdot) ||
|
|
!strcmp(tname, dot) || !strcmp(tname, dotdot))
|
|
return (EACCES);
|
|
|
|
if (!strcmp(name, tname) && dp == tdp)
|
|
return (ENOTUNIQ);
|
|
|
|
error = vfat_dir_getent_lname(dp, name, &f);
|
|
if (error == ENOENT){
|
|
/* Unable to get file entry for "name" */
|
|
return (ENOENT);
|
|
}
|
|
|
|
error = vfat_dir_getent_lname(tdp, tname, &tf);
|
|
if (error == 0){
|
|
/*
|
|
* Target file exists
|
|
*/
|
|
if (IS_DIR(tf)){
|
|
/* This target file is a directory */
|
|
return (EACCES);
|
|
}
|
|
/*
|
|
* Delete target file
|
|
*/
|
|
tfno = FNO(tf.f_cluster, tf.f_offset);
|
|
error = dos_getnode(dfs, tfno, &ctdp);
|
|
if (error)
|
|
return (error);
|
|
|
|
ctdp->d_pfno = tdp->d_fno;
|
|
|
|
error = vfat_dir_delent(tdp, &tf);
|
|
if (error)
|
|
goto rename_done;
|
|
|
|
dos_purgenode(ctdp);
|
|
}
|
|
else if (error != ENOENT){
|
|
/*
|
|
* We couldn't obtain the file entry associated
|
|
* with "tname", but we ran into some other error.
|
|
*/
|
|
return (error);
|
|
}
|
|
/*
|
|
* Create short name for this new name.
|
|
* Pack the old attributes into this file entry.
|
|
*/
|
|
error = vfat_create_sname(tdp, tname, sname, &sflag);
|
|
if (error){
|
|
/* Unable to map this long name to a short one */
|
|
return (EINVAL);
|
|
}
|
|
|
|
vfat_dirent_packname(&tf, tname, sname, sflag);
|
|
vfat_copy_attributes(&f, &tf);
|
|
/*
|
|
* Obtain free entries in parent directory
|
|
*/
|
|
error = vfat_dir_freent(tdp, tf.f_nentries, &tf.f_cluster,&tf.f_offset);
|
|
if (error){
|
|
/* Unable to obtain free entries in parent directory */
|
|
return (ENOSPC);
|
|
}
|
|
|
|
vfat_dirent_print(&tf);
|
|
|
|
df = tf.f_entries;
|
|
bzero(df->df_reserved, 10);
|
|
|
|
if (!IS_DIR(f))
|
|
df->df_attribute |= ATTRIB_ARCHIVE;
|
|
|
|
fno = FNO(f.f_cluster, f.f_offset);
|
|
error = dos_getnode(dfs, fno, &cdp);
|
|
if (error){
|
|
/* Unable to obtain node from node cache */
|
|
return (error);
|
|
}
|
|
cdp->d_pfno = dp->d_fno;
|
|
/*
|
|
* Write file entry corresponding to new name
|
|
*/
|
|
error = vfat_dir_writent(tdp, &tf);
|
|
if (error){
|
|
/* Unable to write directory entry */
|
|
goto rename_done;
|
|
}
|
|
|
|
tfno = FNO(tf.f_cluster, tf.f_offset);
|
|
error = dos_getnode(dfs, tfno, &ctdp);
|
|
if (error){
|
|
/* Unable to obtain node from node cache */
|
|
goto rename_done;
|
|
}
|
|
|
|
ctdp->d_pfno = tdp->d_fno;
|
|
ctdp->d_uid = cdp->d_uid;
|
|
ctdp->d_gid = cdp->d_gid;
|
|
ctdp->d_mode = cdp->d_mode;
|
|
/*
|
|
* Nullify file entry corresponding to old name
|
|
*/
|
|
error = vfat_dir_nulent(dp, &f);
|
|
if (error){
|
|
/* Unable to nullify old file entry */
|
|
goto rename_done;
|
|
}
|
|
|
|
dos_purgenode(cdp);
|
|
cdp = NULL;
|
|
|
|
dp->d_time = time(NULL);
|
|
if (dp != tdp)
|
|
tdp->d_time = dp->d_time;
|
|
rename_done:
|
|
if (cdp)
|
|
dos_putnode(cdp);
|
|
if (ctdp)
|
|
dos_putnode(ctdp);
|
|
timeoutp = &timeout;
|
|
return (error);
|
|
}
|
|
|
|
/* link not implemented by DOS */
|
|
int dos_link(dnode_t *dp, dnode_t *tdp, char *name)
|
|
{
|
|
return ENXIO;
|
|
}
|
|
|
|
/* symbolic link not implemented by DOS */
|
|
int dos_symlink(dnode_t *dp, char *name, sattr *sa, char *path)
|
|
{
|
|
return ENXIO;
|
|
}
|
|
|