FreeDOS/kernel/fatdir.c

483 lines
15 KiB
C

/****************************************************************/
/* */
/* fatdir.c */
/* DOS-C */
/* */
/* FAT File System dir Functions */
/* */
/* Copyright (c) 1995 */
/* 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
static BYTE *fatdirRcsId =
"$Id: fatdir.c 1561 2011-04-08 15:35:23Z bartoldeman $";
#endif
/* Description.
* Initialize a fnode so that it will point to the directory with
* dirstart starting cluster; in case of passing dirstart == 0
* fnode will point to the start of a root directory */
VOID dir_init_fnode(f_node_ptr fnp, CLUSTER dirstart)
{
/* reset the directory flags */
fnp->f_sft_idx = 0xff;
fnp->f_dmp = &sda_tmp_dm;
if (fnp == &fnode[1])
fnp->f_dmp = &sda_tmp_dm_ren;
fnp->f_offset = 0l;
fnp->f_cluster_offset = 0;
/* root directory */
#ifdef WITHFAT32
if (dirstart == 0)
if (ISFAT32(fnp->f_dpb))
dirstart = fnp->f_dpb->dpb_xrootclst;
#endif
fnp->f_cluster = fnp->f_dmp->dm_dircluster = dirstart;
}
f_node_ptr dir_open(register const char *dirname, BOOL split, f_node_ptr fnp)
{
int i;
char *fcbname;
/* determine what drive and dpb we are using... */
fnp->f_dpb = get_dpb(dirname[0]-'A');
/* Perform all directory common handling after all special */
/* handling has been performed. */
/* truename() already did a media check() */
/* Walk the directory tree to find the starting cluster */
/* */
/* Start from the root directory (dirstart = 0) */
/* The CDS's cdsStartCls may be used to shorten the search
beginning at the CWD, see mapPath() and CDS.H in order
to enable this behaviour there.
-- 2001/09/04 ska*/
dir_init_fnode(fnp, 0);
fnp->f_dmp->dm_entry = 0;
dirname += 2; /* Assume FAT style drive */
fcbname = fnp->f_dmp->dm_name_pat;
while(*dirname != '\0')
{
/* skip the path seperator */
++dirname;
/* don't continue if we're at the end: this check is */
/* for root directories, the only fully-qualified path */
/* names that end in a \ */
if (*dirname == '\0')
break;
/* Convert the name into an absolute name for */
/* comparison... */
dirname = ConvertNameSZToName83(fcbname, dirname);
/* do not continue if we split the filename off and are */
/* at the end */
if (split && *dirname == '\0')
break;
/* Now search through the directory to */
/* find the entry... */
i = FALSE;
while (dir_read(fnp) == 1)
{
if (!(fnp->f_dir.dir_attrib & D_VOLID) &&
fcbmatch(fcbname, fnp->f_dir.dir_name))
{
i = TRUE;
break;
}
fnp->f_dmp->dm_entry++;
}
if (!i || !(fnp->f_dir.dir_attrib & D_DIR))
{
return (f_node_ptr) 0;
}
else
{
/* make certain we've moved off */
/* root */
dir_init_fnode(fnp, getdstart(fnp->f_dpb, &fnp->f_dir));
fnp->f_dmp->dm_entry = 0;
}
}
return fnp;
}
/* swap internal and external delete flags */
STATIC void swap_deleted(char *name)
{
if (name[0] == DELETED || name[0] == EXT_DELETED)
name[0] ^= EXT_DELETED - DELETED; /* 0xe0 */
}
/* Description.
* Read next consequitive directory entry, pointed by fnp.
* If some error occures the other critical
* fields aren't changed, except those used for caching.
* The fnp->f_dmp->dm_entry always corresponds to the directory entry
* which has been read.
* Return value.
* 1 - all OK, directory entry having been read is not empty.
* 0 - Directory entry is empty.
* DE_SEEK - Attempt to read beyound the end of the directory.
* DE_BLKINVLD - Invalid block.
* Note. Empty directory entries always resides at the end of the directory. */
COUNT dir_read(REG f_node_ptr fnp)
{
struct buffer FAR *bp;
REG UWORD secsize = fnp->f_dpb->dpb_secsize;
unsigned sector;
unsigned entry = fnp->f_dmp->dm_entry;
/* can't have more than 65535 directory entries */
if (entry >= 65535U)
return DE_SEEK;
/* Determine if we hit the end of the directory. If we have, */
/* bump the offset back to the end and exit. If not, fill the */
/* dirent portion of the fnode, set the SFT_FCLEAN bit and leave,*/
/* but only for root directories */
if (fnp->f_dmp->dm_dircluster == 0)
{
if (entry >= fnp->f_dpb->dpb_dirents)
return DE_SEEK;
fnp->f_dirsector = entry / (secsize / DIRENT_SIZE) +
fnp->f_dpb->dpb_dirstrt;
}
else
{
/* Do a "seek" to the directory position */
fnp->f_offset = entry * (ULONG)DIRENT_SIZE;
/* Search through the FAT to find the block */
/* that this entry is in. */
if (map_cluster(fnp, XFR_READ) != SUCCESS)
return DE_SEEK;
/* Compute the block within the cluster and the */
/* offset within the block. */
sector = (UBYTE)(fnp->f_offset / secsize) & fnp->f_dpb->dpb_clsmask;
fnp->f_dirsector = clus2phys(fnp->f_cluster, fnp->f_dpb) + sector;
/* Get the block we need from cache */
}
bp = getblock(fnp->f_dirsector, fnp->f_dpb->dpb_unit);
#ifdef DISPLAY_GETBLOCK
printf("DIR (dir_read)\n");
#endif
/* Now that we have the block for our entry, get the */
/* directory entry. */
if (bp == NULL)
return DE_BLKINVLD;
bp->b_flag &= ~(BFR_DATA | BFR_FAT);
bp->b_flag |= BFR_DIR | BFR_VALID;
fnp->f_diridx = entry % (secsize / DIRENT_SIZE);
getdirent(&bp->b_buffer[fnp->f_diridx * DIRENT_SIZE], &fnp->f_dir);
swap_deleted(fnp->f_dir.dir_name);
/* and for efficiency, stop when we hit the first */
/* unused entry. */
/* either returns 1 or 0 */
return (fnp->f_dir.dir_name[0] != '\0');
}
/* Description.
* Writes directory entry pointed by fnp to disk. In case of erroneous
* situation fnode is released.
* The caller should set
* 1. F_DMOD flag if original directory entry was modified.
* Return value.
* TRUE - all OK.
* FALSE - error occured (fnode is released).
*/
BOOL dir_write_update(REG f_node_ptr fnp, BOOL update)
{
struct buffer FAR *bp;
UBYTE FAR *vp;
/* Update the entry if it was modified by a write or create... */
if (!update || (fnp->f_flags & (SFT_FCLEAN|SFT_FDATE)) != SFT_FCLEAN)
{
bp = getblock(fnp->f_dirsector, fnp->f_dpb->dpb_unit);
/* Now that we have a block, transfer the directory */
/* entry into the block. */
if (bp == NULL)
return FALSE;
#ifdef DISPLAY_GETBLOCK
printf("DIR (dir_write)\n");
#endif
swap_deleted(fnp->f_dir.dir_name);
vp = &bp->b_buffer[fnp->f_diridx * DIRENT_SIZE];
if (update)
{
/* only update fields that are also in the SFT, for dos_close/commit */
fmemcpy(&vp[DIR_NAME], fnp->f_dir.dir_name, FNAME_SIZE + FEXT_SIZE);
fputbyte(&vp[DIR_ATTRIB], fnp->f_dir.dir_attrib);
fputword(&vp[DIR_TIME], fnp->f_dir.dir_time);
fputword(&vp[DIR_DATE], fnp->f_dir.dir_date);
fputword(&vp[DIR_START], fnp->f_dir.dir_start);
#ifdef WITHFAT32
if (ISFAT32(fnp->f_dpb))
fputword(&vp[DIR_START_HIGH], fnp->f_dir.dir_start_high);
#endif
fputlong(&vp[DIR_SIZE], fnp->f_dir.dir_size);
}
else
{
putdirent(&fnp->f_dir, vp);
}
swap_deleted(fnp->f_dir.dir_name);
bp->b_flag &= ~(BFR_DATA | BFR_FAT);
bp->b_flag |= BFR_DIR | BFR_DIRTY | BFR_VALID;
}
/* Clear buffers after directory write or DOS close */
return flush_buffers(fnp->f_dpb->dpb_unit);
}
#ifndef IPL
COUNT dos_findfirst(UCOUNT attr, BYTE * name)
{
REG f_node_ptr fnp;
REG dmatch *dmp = &sda_tmp_dm;
/* printf("ff %Fs\n", name);*/
/* The findfirst/findnext calls are probably the worst of the */
/* DOS calls. They must work somewhat on the fly (i.e. - open */
/* but never close). The near fnodes now work this way. Every */
/* time a directory is searched, we will initialize the DOS */
/* dirmatch structure and then for every find, we will open the */
/* current directory, do a seek and read. */
/* first: findfirst("D:\\") returns DE_NFILES */
if (name[3] == '\0')
return DE_NFILES;
/* Now open this directory so that we can read the */
/* fnode entry and do a match on it. */
if ((fnp = split_path(name, &fnode[0])) == NULL)
return DE_PATHNOTFND;
/* Now search through the directory to find the entry... */
/* Special handling - the volume id is only in the root */
/* directory and only searched for once. So we need to open */
/* the root and return only the first entry that contains the */
/* volume id bit set (while ignoring LFN entries). */
/* RBIL: ignore ReaDONLY and ARCHIVE bits but DEVICE ignored too*/
/* For compatibility with bad search requests, only treat as */
/* volume search if only volume bit set, else ignore it. */
if ((attr & ~(D_RDONLY | D_ARCHIVE | D_DEVICE)) == D_VOLID)
/* if ONLY label wanted redirect search to root dir */
dir_init_fnode(fnp, 0);
/* Now further initialize the dirmatch structure. */
dmp->dm_drive = name[0] - 'A';
dmp->dm_attr_srch = attr;
return dos_findnext();
}
/*
BUGFIX TE 06/28/01
when using FcbFindXxx, the only information available is
the cluster number + entrycount. everything else MUST\
be recalculated.
a good test for this is MSDOS CHKDSK, which now (seems too) work
*/
COUNT dos_findnext(void)
{
REG f_node_ptr fnp;
REG dmatch *dmp;
/* Select the default to help non-drive specified path */
/* searches... */
fnp = &fnode[0];
dmp = &sda_tmp_dm;
fnp->f_dpb = get_dpb(dmp->dm_drive);
if (media_check(fnp->f_dpb) < 0)
return DE_NFILES;
dir_init_fnode(fnp, dmp->dm_dircluster);
/* Search through the directory to find the entry, but do a */
/* seek first. */
/* Loop through the directory */
while (dir_read(fnp) == 1)
{
++dmp->dm_entry;
if (fnp->f_dir.dir_name[0] != DELETED
&& (fnp->f_dir.dir_attrib & D_LFN) != D_LFN)
{
if (fcmp_wild(dmp->dm_name_pat, fnp->f_dir.dir_name, FNAME_SIZE + FEXT_SIZE))
{
/*
MSD Command.com uses FCB FN 11 & 12 with attrib set to 0x16.
Bits 0x21 seem to get set some where in MSD so Rd and Arc
files are returned.
RdOnly + Archive bits are ignored
*/
/* Test the attribute as the final step */
/* It's either a special volume label search or an */
/* attribute inclusive search. The attribute inclusive search */
/* can also find volume labels if you set e.g. D_DIR|D_VOLUME */
UBYTE attr_srch;
attr_srch = dmp->dm_attr_srch & ~(D_RDONLY | D_ARCHIVE | D_DEVICE);
if (attr_srch == D_VOLID)
{
if (!(fnp->f_dir.dir_attrib & D_VOLID))
continue;
}
else if (~attr_srch & (D_DIR | D_SYSTEM | D_HIDDEN | D_VOLID) &
fnp->f_dir.dir_attrib)
continue;
/* If found, transfer it to the dmatch structure */
memcpy(&SearchDir, &fnp->f_dir, sizeof(struct dirent));
/* return the result */
return SUCCESS;
}
}
}
#ifdef DEBUG
printf("dos_findnext: %11s\n", fnp->f_dir.dir_name);
#endif
/* return the result */
return DE_NFILES;
}
#endif
/*
this receives a name in 11 char field NAME+EXT and builds
a zeroterminated string
unfortunately, blanks are allowed in filenames. like
"test e", " test .y z",...
so we have to work from the last blank backward
*/
void ConvertName83ToNameSZ(BYTE FAR * destSZ, BYTE FAR * srcFCBName)
{
int loop;
int noExtension = FALSE;
if (*srcFCBName == '.')
{
noExtension = TRUE;
}
fmemcpy(destSZ, srcFCBName, FNAME_SIZE);
srcFCBName += FNAME_SIZE;
for (loop = FNAME_SIZE; --loop >= 0;)
{
if (destSZ[loop] != ' ')
break;
}
destSZ += loop + 1;
if (!noExtension) /* not for ".", ".." */
{
for (loop = FEXT_SIZE; --loop >= 0;)
{
if (srcFCBName[loop] != ' ')
break;
}
if (loop >= 0)
{
*destSZ++ = '.';
fmemcpy(destSZ, srcFCBName, loop + 1);
destSZ += loop + 1;
}
}
*destSZ = '\0';
}
#if 0
/*
returns the asciiSZ length of a 8.3 filename
*/
int FileName83Length(BYTE * filename83)
{
BYTE buff[13];
ConvertName83ToNameSZ(buff, filename83);
return strlen(buff);
}
#endif
/* this routine converts a name portion of a fully qualified path */
/* name, so . and .. are not allowed, only straightforward 8+3 names */
const char *ConvertNameSZToName83(char *fcbname, const char *dirname)
{
int i;
memset(fcbname, ' ', FNAME_SIZE + FEXT_SIZE);
for (i = 0; i < FNAME_SIZE + FEXT_SIZE; i++, dirname++)
{
char c = *dirname;
if (c == '.')
i = FNAME_SIZE - 1;
else if (c != '\0' && c != '\\')
fcbname[i] = c;
else
break;
}
return dirname;
}