📄 fatfs.c
字号:
/****************************************************************/
/* */
/* fatfs.c */
/* DOS-C */
/* */
/* FAT File System I/O Functions */
/* */
/* Copyright (c) 1995,1998 */
/* Pasquale J. Villani */
/* All Rights Reserved */
/* */
/* This file is part of DOS-C. */
/* */
/* DOS-C is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU General Public License */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version. */
/* */
/* DOS-C 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 General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public */
/* License along with DOS-C; see the file COPYING. If not, */
/* write to the Free Software Foundation, 675 Mass Ave, */
/* Cambridge, MA 02139, USA. */
/****************************************************************/
#include "portab.h"
#include "globals.h"
#ifdef VERSION_STRINGS
BYTE *RcsId = "$Id: fatfs.c,v 1.70 2004/05/23 15:10:08 bartoldeman Exp $";
#endif
/* */
/* function prototypes */
/* */
f_node_ptr xlt_fd(COUNT);
COUNT xlt_fnp(f_node_ptr);
STATIC void save_far_f_node(f_node_ptr fnp);
STATIC f_node_ptr get_near_f_node(void);
STATIC f_node_ptr split_path(char *, char *);
BOOL find_fname(f_node_ptr, char *, int);
/* /// Added - Ron Cemer */
STATIC void merge_file_changes(f_node_ptr fnp, int collect);
/* /// Added - Ron Cemer */
STATIC int is_same_file(f_node_ptr fnp1, f_node_ptr fnp2);
/* /// Added - Ron Cemer */
STATIC void copy_file_changes(f_node_ptr src, f_node_ptr dst);
BOOL find_free(f_node_ptr);
STATIC int alloc_find_free(f_node_ptr fnp, char *path, char *fcbname);
VOID wipe_out(f_node_ptr);
CLUSTER extend(f_node_ptr);
COUNT extend_dir(f_node_ptr);
CLUSTER first_fat(f_node_ptr);
COUNT map_cluster(f_node_ptr, COUNT);
STATIC VOID shrink_file(f_node_ptr fnp);
/* FAT time notation in the form of hhhh hmmm mmmd dddd (d = double second) */
STATIC time time_encode(struct dostime *t)
{
return (t->hour << 11) | (t->minute << 5) | (t->second >> 1);
}
#ifdef WITHFAT32
CLUSTER getdstart(struct dpb FAR *dpbp, struct dirent *dentry)
{
if (!ISFAT32(dpbp))
return dentry->dir_start;
return (((CLUSTER)dentry->dir_start_high << 16) | dentry->dir_start);
}
void setdstart(struct dpb FAR *dpbp, struct dirent *dentry, CLUSTER value)
{
dentry->dir_start = (UWORD)value;
if (ISFAT32(dpbp))
dentry->dir_start_high = (UWORD)(value >> 16);
}
BOOL checkdstart(struct dpb FAR *dpbp, struct dirent *dentry, CLUSTER value)
{
if (!ISFAT32(dpbp))
return dentry->dir_start == (UWORD)value;
return (dentry->dir_start == (UWORD)value &&
dentry->dir_start_high == (UWORD)(value >> 16));
}
#endif
ULONG clus2phys(CLUSTER cl_no, struct dpb FAR * dpbp)
{
CLUSTER data =
#ifdef WITHFAT32
ISFAT32(dpbp) ? dpbp->dpb_xdata :
#endif
dpbp->dpb_data;
return ((ULONG) (cl_no - 2) << dpbp->dpb_shftcnt) + data;
}
struct dpb FAR *get_dpb(COUNT dsk)
{
register struct cds FAR *cdsp = get_cds(dsk);
if (cdsp == NULL || cdsp->cdsFlags & CDSNETWDRV)
return NULL;
return cdsp->cdsDpb;
}
/* initialize all direntry fields except for the name */
STATIC void init_direntry(struct dirent *dentry, unsigned attrib,
CLUSTER cluster)
{
struct dostime dt;
dentry->dir_size = 0l;
#ifdef WITHFAT32
dentry->dir_start_high = (UWORD)(cluster >> 16);
#else
dentry->dir_start_high = 0;
#endif
dentry->dir_start = (UWORD)cluster;
dentry->dir_attrib = attrib;
dentry->dir_case = 0;
DosGetTime(&dt);
dentry->dir_crtimems = dt.hundredth;
if (dt.second & 1)
dentry->dir_crtimems += 100;
dentry->dir_time = dentry->dir_crtime = time_encode(&dt);
dentry->dir_date = dentry->dir_crdate = dentry->dir_accdate = dos_getdate();
}
/************************************************************************/
/* */
/* Internal file handlers - open, create, read, write, close, etc. */
/* */
/************************************************************************/
/* Open a file given the path. Flags is 0 for read, 1 for write and 2 */
/* for update. */
/* Returns an long where the high word is a status code and the low */
/* word is an integer file descriptor or a negative error code */
/* see DosOpenSft(), dosfns.c for an explanation of the flags bits */
/* directory opens are allowed here; these are not allowed by DosOpenSft*/
long dos_open(char *path, unsigned flags, unsigned attrib)
{
REG f_node_ptr fnp;
char fcbname[FNAME_SIZE + FEXT_SIZE];
int status = S_OPENED;
/* First test the flags to see if the user has passed a valid */
/* file mode... */
if ((flags & O_ACCMODE) > 2)
return DE_INVLDACC;
/* first split the passed dir into comopnents (i.e. - path to */
/* new directory and name of new directory. */
if ((fnp = split_path(path, fcbname)) == NULL)
return DE_PATHNOTFND;
/* Check that we don't have a duplicate name, so if we */
/* find one, truncate it (O_CREAT). */
if (find_fname(fnp, fcbname, D_ALL | attrib))
{
if (flags & O_TRUNC)
{
/* The only permissable attribute is archive, */
/* check for any other bit set. If it is, give */
/* an access error. */
if ((fnp->f_dir.dir_attrib & (D_RDONLY | D_DIR | D_VOLID))
|| (fnp->f_dir.dir_attrib & ~D_ARCHIVE & ~attrib))
{
dir_close(fnp);
return DE_ACCESS;
}
/* Release the existing files FAT and set the */
/* length to zero, effectively truncating the */
/* file to zero. */
wipe_out(fnp);
status = S_REPLACED;
}
else if (flags & O_OPEN)
{
/* force r/o open for FCB if the file is read-only */
if ((flags & O_FCB) && (fnp->f_dir.dir_attrib & D_RDONLY))
flags = (flags & ~3) | O_RDONLY;
/* Check permissions. -- JPP */
if ((fnp->f_dir.dir_attrib & D_RDONLY) &&
((flags & O_ACCMODE) != O_RDONLY))
{
dir_close(fnp);
return DE_ACCESS;
}
}
else
{
dir_close(fnp);
return DE_FILEEXISTS;
}
}
else if (flags & O_CREAT)
{
int ret = alloc_find_free(fnp, path, fcbname);
if (ret != SUCCESS)
return ret;
/* put the fnode's name into the directory. */
memcpy(fnp->f_dir.dir_name, fcbname, FNAME_SIZE + FEXT_SIZE);
status = S_CREATED;
}
else
{
/* open: If we can't find the file, just return a not */
/* found error. */
dir_close(fnp);
return DE_FILENOTFND;
}
/* Set the fnode to the desired mode */
/* Updating the directory entry first. */
fnp->f_mode = flags & O_ACCMODE;
if (status != S_OPENED)
{
init_direntry(&fnp->f_dir, attrib, FREE);
fnp->f_flags = F_DMOD | F_DDIR;
if (!dir_write(fnp))
{
release_f_node(fnp);
return DE_ACCESS;
}
}
/* Now change to file */
fnp->f_offset = 0l;
if (status != S_OPENED)
{
fnp->f_cluster = FREE;
setdstart(fnp->f_dpb, &fnp->f_dir, FREE);
fnp->f_cluster_offset = 0;
}
fnp->f_flags = 0;
if (status != S_OPENED)
fnp->f_flags = F_DMOD;
merge_file_changes(fnp, status == S_OPENED); /* /// Added - Ron Cemer */
/* /// Moved from above. - Ron Cemer */
fnp->f_cluster = getdstart(fnp->f_dpb, &fnp->f_dir);
fnp->f_cluster_offset = 0;
save_far_f_node(fnp);
return xlt_fnp(fnp) | ((long)status << 16);
}
BOOL fcmp_wild(const char * s1, const char * s2, unsigned n)
{
for ( ; n--; ++s1, ++s2)
if (*s1 != '?' && *s1 != *s2)
return FALSE;
return TRUE;
}
COUNT dos_close(COUNT fd)
{
f_node_ptr fnp;
/* Translate the fd into a useful pointer */
fnp = xlt_fd(fd);
/* If the fd was invalid because it was out of range or the */
/* requested file was not open, tell the caller and exit */
/* note: an invalid fd is indicated by a 0 return */
if (fnp == (f_node_ptr) 0)
return DE_INVLDHNDL;
if (fnp->f_flags & F_DMOD)
{
if (!(fnp->f_flags & F_DDATE))
{
fnp->f_dir.dir_time = dos_gettime();
fnp->f_dir.dir_date = dos_getdate();
}
merge_file_changes(fnp, FALSE); /* /// Added - Ron Cemer */
}
fnp->f_flags |= F_DDIR;
dir_close(fnp);
return SUCCESS;
}
COUNT dos_commit(COUNT fd)
{
f_node_ptr fnp, fnp2;
/* Translate the fd into a useful pointer */
fnp = xlt_fd(fd);
/* If the fd was invalid because it was out of range or the */
/* requested file was not open, tell the caller and exit */
/* note: an invalid fd is indicated by a 0 return */
if (fnp == (f_node_ptr) 0)
return DE_INVLDHNDL;
fnp2 = get_f_node();
if (fnp2 == (f_node_ptr) 0)
{
release_near_f_node(fnp);
return DE_INVLDHNDL;
}
/* a copy of the fnode is closed meaning that the directory info
is updated etc, but we keep our old info */
memcpy(fnp2, fnp, sizeof(*fnp));
save_far_f_node(fnp2);
release_near_f_node(fnp);
return dos_close(xlt_fnp(fnp2));
}
/* */
/* split a path into it's component directory and file name */
/* */
f_node_ptr split_path(char * path, char * fcbname)
{
REG f_node_ptr fnp;
/* Start off by parsing out the components. */
int dirlength = ParseDosName(path, fcbname, FALSE);
if (dirlength < SUCCESS)
return (f_node_ptr) 0;
/* 11/29/99 jt
* Networking and Cdroms. You can put in here a return.
* Maybe a return of 0xDEADBEEF or something for Split or Dir_open.
* Just to let upper level Fdos know its a sft, CDS function.
* Right now for Networking there is no support for Rename, MkDir
* RmDir & Delete.
<insert code here or in dir_open. I would but it in Dir_open.
Do the redirection in Network.c>
*/
#ifdef DEBUG
if (get_cds(path[0]-'A')->cdsFlags & CDSNETWDRV)
{
printf("split path called for redirected file: `%s'\n",
fcbname);
return (f_node_ptr) 0;
}
#endif
/* Translate the path into a useful pointer */
{
char tmp = path[dirlength];
path[dirlength] = '\0';
fnp = dir_open(path);
path[dirlength] = tmp;
}
/* If the fd was invalid because it was out of range or the */
/* requested file was not open, tell the caller and exit... */
/* note: an invalid fd is indicated by a 0 return */
if (fnp == (f_node_ptr) 0 || fnp->f_count <= 0)
{
dir_close(fnp);
return (f_node_ptr) 0;
}
return fnp;
}
/* checks whether directory part of path exists */
BOOL dir_exists(char * path)
{
REG f_node_ptr fnp;
char fcbname[FNAME_SIZE + FEXT_SIZE];
if ((fnp = split_path(path, fcbname)) == NULL)
return FALSE;
dir_close(fnp);
return TRUE;
}
BOOL fcbmatch(const char *fcbname1, const char *fcbname2)
{
return memcmp(fcbname1, fcbname2, FNAME_SIZE + FEXT_SIZE) == 0;
}
STATIC BOOL find_fname(f_node_ptr fnp, char *fcbname, int attr)
{
while (dir_read(fnp) == 1)
{
if (fcbmatch(fnp->f_dir.dir_name, fcbname)
&& (fnp->f_dir.dir_attrib & ~(D_RDONLY | D_ARCHIVE | attr)) == 0)
{
return TRUE;
}
fnp->f_diroff++;
}
return FALSE;
}
/* Description.
* Remove entries with D_LFN attribute preceeding the directory entry
* pointed by fnp, fnode isn't modified (I hope).
* Return value.
* SUCCESS - completed successfully.
* DE_BLKINVLD - error occured, fnode is released.
* input: fnp with valid non-LFN directory entry, not equal to '..' or
* '.'
*/
COUNT remove_lfn_entries(f_node_ptr fnp)
{
unsigned original_diroff = fnp->f_diroff;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -