730 lines
16 KiB
C
730 lines
16 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)pc_dir.c 1.1 94/10/31 SMI";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1989 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/user.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/vnode.h>
|
|
#include <pcfs/pc_label.h>
|
|
#include <pcfs/pc_fs.h>
|
|
#include <pcfs/pc_dir.h>
|
|
#include <pcfs/pc_node.h>
|
|
|
|
/*
|
|
* slot structure is used by the directory search routine to return
|
|
* the results of the search. If the search is successful sl_blkno and
|
|
* sl_offset reflect the disk address of the entry and sl_ep points to
|
|
* the actual entry data in buffer sl_bp. sl_flags is set to whether the
|
|
* entry is dot or dotdot. If the search is unsuccessful sl_blkno and
|
|
* sl_offset points to an empty directory slot if there are any. Otherwise
|
|
* it is set to -1.
|
|
*/
|
|
struct slot {
|
|
enum {SL_NONE, SL_FOUND, SL_EXTEND}
|
|
sl_status; /* slot status */
|
|
daddr_t sl_blkno; /* disk block number which has entry */
|
|
int sl_offset; /* offset of entry within block */
|
|
struct buf *sl_bp; /* buffer containing entry data */
|
|
struct pcdir *sl_ep; /* pointer to entry data */
|
|
int sl_flags; /* flags (see below) */
|
|
};
|
|
#define SL_DOT 1 /* entry point to self */
|
|
#define SL_DOTDOT 2 /* entry points to parent */
|
|
|
|
/*
|
|
* Lookup a name in a directory. Return a pointer to the pc_node
|
|
* which represents the entry.
|
|
*/
|
|
int
|
|
pc_dirlook(dp, namep, pcpp)
|
|
register struct pcnode *dp; /* parent directory */
|
|
char *namep; /* name to lookup */
|
|
struct pcnode **pcpp; /* result */
|
|
{
|
|
struct vnode * vp;
|
|
struct slot slot;
|
|
int error;
|
|
|
|
PCFSDEBUG(7)
|
|
printf("pc_dirlook (dp %x name %s pcpp %x)\n", dp, namep, pcpp);
|
|
|
|
if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
|
|
return (ENOTDIR);
|
|
}
|
|
/*
|
|
* The root directory does not have "." and ".." entries,
|
|
* so they are faked here.
|
|
*/
|
|
vp = PCTOV(dp);
|
|
if (vp->v_flag & VROOT) {
|
|
if (bcmp(namep, ".", 2) == 0 || bcmp(namep, "..", 3) == 0) {
|
|
*pcpp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
|
|
(daddr_t)0, 0, (struct pcdir *)0);
|
|
return (0);
|
|
}
|
|
}
|
|
error = pc_findentry(dp, namep, &slot);
|
|
if (error == 0) {
|
|
*pcpp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
|
|
slot.sl_blkno, slot.sl_offset, slot.sl_ep);
|
|
brelse(slot.sl_bp);
|
|
} else if (error == EINVAL) {
|
|
error = ENOENT;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Enter a name in a directory.
|
|
*/
|
|
pc_direnter(dp, namep, vap, pcpp)
|
|
register struct pcnode *dp; /* directory to make entry in */
|
|
register char *namep; /* name of entry */
|
|
struct vattr *vap; /* attributes of new entry */
|
|
struct pcnode **pcpp;
|
|
{
|
|
register struct pcfs *fsp;
|
|
register int error;
|
|
struct slot slot;
|
|
struct vnode * vp;
|
|
|
|
PCFSDEBUG(7)
|
|
printf("pcdirentry(dp %x, name %s, vap %x, pcpp %x\n", dp, namep, vap, pcpp);
|
|
|
|
if (pcpp != NULL)
|
|
*pcpp = NULL;
|
|
|
|
if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
|
|
return (ENOTDIR);
|
|
}
|
|
/*
|
|
* If name is "." or "..", just look it up.
|
|
*/
|
|
if (*namep == '.') {
|
|
if (pcpp) {
|
|
error = pc_dirlook(dp, namep, pcpp);
|
|
if (error)
|
|
return (error);
|
|
}
|
|
return (EEXIST);
|
|
}
|
|
if (dp->pc_entry.pcd_attr & (PCA_HIDDEN | PCA_SYSTEM)) {
|
|
return (EPERM);
|
|
}
|
|
/*
|
|
* Make sure directory has not been removed while fs was unlocked.
|
|
*/
|
|
if (dp->pc_entry.pcd_filename[0] == PCD_ERASED) {
|
|
return (ENOENT);
|
|
}
|
|
vp = PCTOV(dp);
|
|
fsp = VFSTOPCFS(vp->v_vfsp);
|
|
error = pc_findentry(dp, namep, &slot);
|
|
if (error == 0) {
|
|
if (pcpp) {
|
|
*pcpp =
|
|
pc_getnode(fsp, slot.sl_blkno, slot.sl_offset,
|
|
slot.sl_ep);
|
|
error = EEXIST;
|
|
}
|
|
brelse(slot.sl_bp);
|
|
} else if (error == ENOENT) {
|
|
struct pcdir direntry;
|
|
|
|
/*
|
|
* The entry does not exist. Check write permission in
|
|
* directory to see if entry can be created.
|
|
*/
|
|
if (dp->pc_entry.pcd_attr & PCA_RDONLY) {
|
|
return (EPERM);
|
|
}
|
|
error = 0;
|
|
/*
|
|
* Make sure there is a slot.
|
|
*/
|
|
if (slot.sl_status == SL_NONE)
|
|
panic("pc_direnter: no slot\n");
|
|
if (slot.sl_status == SL_EXTEND) {
|
|
/*
|
|
* There is no slot in the directory, so try to
|
|
* extend it.
|
|
*/
|
|
error =
|
|
pc_balloc(dp,
|
|
pc_lblkno(fsp, slot.sl_offset),
|
|
&slot.sl_blkno);
|
|
if (error)
|
|
return (error);
|
|
slot.sl_offset = 0;
|
|
}
|
|
/*
|
|
* Make an entry from the supplied attributes.
|
|
*/
|
|
error = pc_makedirentry(dp, namep, vap, &direntry);
|
|
if (error)
|
|
return (error);
|
|
/*
|
|
* Get a pcnode for the new entry.
|
|
*/
|
|
*pcpp =
|
|
pc_getnode(fsp, slot.sl_blkno, slot.sl_offset, &direntry);
|
|
|
|
/*
|
|
* Write out the new entry in the parent directory.
|
|
*/
|
|
error = pc_syncfat(fsp);
|
|
if (! error) {
|
|
error = pc_nodeupdate(*pcpp);
|
|
}
|
|
|
|
/*
|
|
* %% why not update the mtime in dp? because '..' update
|
|
* problem. (dp could be pointing to the '..' of a child
|
|
* of the real directory, instead of the real directory itself.
|
|
* Then we end up updating '..' entry.
|
|
*/
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Template for "." and ".." directory entries.
|
|
*/
|
|
static struct {
|
|
struct pcdir t_dot; /* dot entry */
|
|
struct pcdir t_dotdot; /* dotdot entry */
|
|
} dirtemplate = {
|
|
{
|
|
". ",
|
|
" ",
|
|
PCA_DIR,
|
|
0,
|
|
"",
|
|
{0, 0},
|
|
0,
|
|
0
|
|
},
|
|
{
|
|
".. ",
|
|
" ",
|
|
PCA_DIR,
|
|
0,
|
|
"",
|
|
{0, 0},
|
|
0,
|
|
0
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Convert an attributes structure into a pc directory entry.
|
|
*/
|
|
int
|
|
pc_makedirentry(dp, namep, vap, ep)
|
|
struct pcnode *dp; /* parent directory */
|
|
char *namep; /* name of new node */
|
|
register struct vattr *vap; /* attributes of new node */
|
|
register struct pcdir *ep; /* new directory entry */
|
|
{
|
|
struct vnode * vp;
|
|
int error;
|
|
|
|
bzero((caddr_t)ep, sizeof (struct pcdir));
|
|
error = pc_parsename(namep, ep->pcd_filename, ep->pcd_ext);
|
|
if (error)
|
|
return (error);
|
|
pc_tvtopct(&time, &ep->pcd_mtime);
|
|
ep->pcd_size = 0;
|
|
ep->pcd_attr = 0;
|
|
if ((vap->va_mode & 0222) == 0)
|
|
ep->pcd_attr |= PCA_RDONLY;
|
|
if (vap->va_type == VDIR) {
|
|
register struct buf *bp;
|
|
register struct pcfs *fsp;
|
|
short cn;
|
|
|
|
vp = PCTOV(dp);
|
|
fsp = VFSTOPCFS(vp->v_vfsp);
|
|
ep->pcd_attr |= PCA_DIR;
|
|
/*
|
|
* Make dot and dotdot entries for a new directory.
|
|
*/
|
|
switch (cn = pc_alloccluster(fsp)) {
|
|
case PCF_FREECLUSTER:
|
|
return (ENOSPC);
|
|
case PCF_ERRORCLUSTER:
|
|
return (EIO);
|
|
}
|
|
bp = bread(fsp->pcfs_devvp, pc_cldaddr(fsp, cn),
|
|
fsp->pcfs_clsize);
|
|
if (bp->b_flags & (B_ERROR | B_INVAL)) {
|
|
pc_setcluster(fsp, cn, PCF_FREECLUSTER);
|
|
if (bp->b_flags & B_ERROR) {
|
|
PCFSDEBUG(1)
|
|
printf("pc_makedirentry ");
|
|
pc_diskchanged(fsp);
|
|
return (EIO);
|
|
}
|
|
brelse(bp);
|
|
return (EIO);
|
|
}
|
|
bp->b_flags |= B_NOCACHE; /* don't cache */
|
|
dirtemplate.t_dot.pcd_scluster = ep->pcd_scluster = htols(cn);
|
|
dirtemplate.t_dotdot.pcd_scluster = dp->pc_entry.pcd_scluster;
|
|
dirtemplate.t_dot.pcd_mtime =
|
|
dirtemplate.t_dotdot.pcd_mtime = ep->pcd_mtime;
|
|
bcopy((caddr_t)&dirtemplate,
|
|
bp->b_un.b_addr, sizeof (dirtemplate));
|
|
bwrite(bp);
|
|
if (bp->b_flags & B_ERROR) {
|
|
pc_diskchanged(fsp);
|
|
return (EIO);
|
|
}
|
|
} else {
|
|
ep->pcd_scluster = 0;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Remove a name from a directory.
|
|
*/
|
|
pc_dirremove(dp, namep, type)
|
|
register struct pcnode *dp;
|
|
char *namep;
|
|
enum vtype type;
|
|
{
|
|
struct slot slot;
|
|
register struct pcnode *pcp;
|
|
register int error;
|
|
struct vnode * vp;
|
|
|
|
vp = PCTOV(dp);
|
|
if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
|
|
return (ENOTDIR);
|
|
}
|
|
if (dp->pc_entry.pcd_attr & (PCA_RDONLY | PCA_HIDDEN | PCA_SYSTEM)) {
|
|
return (EPERM);
|
|
}
|
|
error = pc_findentry(dp, namep, &slot);
|
|
if (error)
|
|
return (error);
|
|
if (slot.sl_flags == SL_DOT) {
|
|
error = EINVAL;
|
|
} else if (slot.sl_flags == SL_DOTDOT) {
|
|
error = ENOTEMPTY;
|
|
} else {
|
|
pcp =
|
|
pc_getnode(VFSTOPCFS(vp->v_vfsp),
|
|
slot.sl_blkno, slot.sl_offset, slot.sl_ep);
|
|
}
|
|
if (error) {
|
|
brelse(slot.sl_bp);
|
|
return (error);
|
|
}
|
|
if (type == VDIR) {
|
|
if (pcp->pc_entry.pcd_attr & PCA_DIR) {
|
|
if (!pc_dirempty(pcp)) {
|
|
error = ENOTEMPTY;
|
|
}
|
|
} else {
|
|
error = ENOTDIR;
|
|
}
|
|
} else {
|
|
if (pcp->pc_entry.pcd_attr & PCA_DIR)
|
|
error = EISDIR;
|
|
}
|
|
if (error == 0) {
|
|
/*
|
|
* Mark the in core node and on disk entry
|
|
* as removed. The slot may then be reused.
|
|
* The files clusters will be deallocated
|
|
* when the last reference goes away.
|
|
*/
|
|
slot.sl_ep->pcd_filename[0] =
|
|
pcp->pc_entry.pcd_filename[0] = PCD_ERASED;
|
|
pcp->pc_eblkno = -1;
|
|
bwrite(slot.sl_bp);
|
|
if (slot.sl_bp->b_flags & B_ERROR) {
|
|
pc_diskchanged(VFSTOPCFS(vp->v_vfsp));
|
|
return (EIO);
|
|
} else if (type == VDIR) { /* %% delete '.', '..' now so that */
|
|
(void) pc_truncate(pcp, 0L);
|
|
if (pc_syncfat(VFSTOPCFS(vp->v_vfsp))) {
|
|
return (EIO);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
brelse(slot.sl_bp);
|
|
}
|
|
/*
|
|
* If this is the only reference release it now.
|
|
*/
|
|
#ifdef notdef
|
|
if (PCTOV(pcp)->v_count == 1) {
|
|
PCTOV(pcp)->v_count = 0;
|
|
if (!(pcp->pc_flags & PC_RELE_PEND))
|
|
pc_rele(pcp);
|
|
} else
|
|
#endif notdef
|
|
VN_RELE(PCTOV(pcp));
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Ascertain whether a directory is empty.
|
|
*/
|
|
int
|
|
pc_dirempty(pcp)
|
|
register struct pcnode *pcp;
|
|
{
|
|
struct buf *bp;
|
|
struct pcdir *ep;
|
|
register long offset;
|
|
register int boff;
|
|
register char c;
|
|
int error;
|
|
struct vnode * vp;
|
|
|
|
vp = PCTOV(pcp);
|
|
bp = NULL;
|
|
|
|
for (offset = 0; /* */; offset += sizeof (struct pcdir)) {
|
|
|
|
/*
|
|
* If offset is on a block boundary,
|
|
* read in the next directory block.
|
|
* Release previous if it exists.
|
|
*/
|
|
boff = pc_blkoff(VFSTOPCFS(vp->v_vfsp), offset);
|
|
if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
|
|
if (bp != NULL)
|
|
brelse(bp);
|
|
if (error = pc_blkatoff(pcp, offset, &bp, &ep)) {
|
|
return (error);
|
|
}
|
|
}
|
|
c = ep->pcd_filename[0];
|
|
if (c == PCD_UNUSED)
|
|
break;
|
|
if ((c != '.') && (c != PCD_ERASED)) {
|
|
brelse(bp);
|
|
return (0);
|
|
}
|
|
ep++;
|
|
}
|
|
if (bp != NULL)
|
|
brelse(bp);
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Rename a file within a directory.
|
|
* Target cannot exist (for now).
|
|
*/
|
|
pc_rename(dp, snm, tnm)
|
|
register struct pcnode *dp; /* parent directory */
|
|
char *snm; /* source file name */
|
|
char *tnm; /* target file name */
|
|
{
|
|
register struct pcnode *pcp; /* pcnode we are trying to rename */
|
|
struct slot slot;
|
|
char tfname[PCFNAMESIZE];
|
|
char tfext[PCFEXTSIZE];
|
|
register int error;
|
|
struct vnode * vp;
|
|
|
|
vp = PCTOV(dp);
|
|
PCFSDEBUG(6)
|
|
printf("pc_rename(0x%x, %s, %s)\n", dp, snm, tnm);
|
|
if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
|
|
return (ENOTDIR);
|
|
}
|
|
/*
|
|
* No dot or dotdot.
|
|
*/
|
|
if (*snm == '.' || *tnm == '.')
|
|
return (EINVAL);
|
|
/*
|
|
* Get the source node.
|
|
*/
|
|
error = pc_findentry(dp, snm, &slot);
|
|
if (error) {
|
|
return (error);
|
|
}
|
|
pcp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
|
|
slot.sl_blkno, slot.sl_offset, slot.sl_ep);
|
|
|
|
brelse(slot.sl_bp);
|
|
/*
|
|
* Parse the target name.
|
|
*/
|
|
error = pc_parsename(tnm, tfname, tfext);
|
|
if (error)
|
|
goto out;
|
|
/*
|
|
* See if source and target names are different.
|
|
*/
|
|
if (bcmp(tfname, pcp->pc_entry.pcd_filename, PCFNAMESIZE) == 0 &&
|
|
bcmp(tfext, pcp->pc_entry.pcd_ext, PCFEXTSIZE) == 0) {
|
|
goto out;
|
|
}
|
|
/*
|
|
* see if the target exists
|
|
*/
|
|
error = pc_findentry(dp, tnm, &slot);
|
|
if (error == 0) {
|
|
/*
|
|
* Target exists.
|
|
*/
|
|
brelse(slot.sl_bp);
|
|
error = EEXIST;
|
|
} else if (error == ENOENT) {
|
|
/*
|
|
* Rename the source.
|
|
*/
|
|
bcopy(tfname, pcp->pc_entry.pcd_filename, PCFNAMESIZE);
|
|
bcopy(tfext, pcp->pc_entry.pcd_ext, PCFEXTSIZE);
|
|
if (error = pc_nodeupdate(pcp)) {
|
|
return (error);
|
|
}
|
|
}
|
|
out:
|
|
/*
|
|
* If this is the only reference release it now.
|
|
*/
|
|
#ifdef notdef
|
|
if (PCTOV(pcp)->v_count == 1) {
|
|
PCTOV(pcp)->v_count = 0;
|
|
if (!(pcp->pc_flags & PC_RELE_PEND)) {
|
|
pc_rele(pcp);
|
|
}
|
|
} else
|
|
#endif notdef
|
|
VN_RELE(PCTOV(pcp));
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Search a directory for an entry.
|
|
* The directory should be locked as this routine
|
|
* will sleep on I/O while searching.
|
|
*/
|
|
static int
|
|
pc_findentry(dp, namep, slotp)
|
|
register struct pcnode *dp; /* parent directory */
|
|
char *namep; /* name to lookup */
|
|
struct slot *slotp;
|
|
{
|
|
register long offset;
|
|
struct pcdir *ep;
|
|
register int boff;
|
|
int error;
|
|
char fname[PCFNAMESIZE];
|
|
char fext[PCFEXTSIZE];
|
|
struct vnode * vp;
|
|
|
|
vp = PCTOV(dp);
|
|
PCFSDEBUG(7)
|
|
printf("pc_findentry: looking for %s in dir 0x%x\n", namep, dp);
|
|
slotp->sl_status = SL_NONE;
|
|
if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
|
|
return (ENOTDIR);
|
|
}
|
|
/*
|
|
* Verify that the dp is still valid on the disk
|
|
*/
|
|
error = pc_verify(dp);
|
|
if (error)
|
|
return (error);
|
|
error = pc_parsename(namep, fname, fext);
|
|
if (error)
|
|
return (error);
|
|
slotp->sl_bp = NULL;
|
|
for (offset = 0; /* */; ep++, offset += sizeof (struct pcdir)) {
|
|
/*
|
|
* If offset is on a block boundary,
|
|
* read in the next directory block.
|
|
* Release previous if it exists.
|
|
*/
|
|
boff = pc_blkoff(VFSTOPCFS(vp->v_vfsp), offset);
|
|
if (boff == 0 || slotp->sl_bp == NULL ||
|
|
boff >= slotp->sl_bp->b_bcount) {
|
|
if (slotp->sl_bp != NULL) {
|
|
brelse(slotp->sl_bp);
|
|
slotp->sl_bp = NULL;
|
|
}
|
|
error = pc_blkatoff(dp, offset, &slotp->sl_bp, &ep);
|
|
if (error == ENOENT && slotp->sl_status == SL_NONE) {
|
|
slotp->sl_status = SL_EXTEND;
|
|
slotp->sl_offset = offset;
|
|
}
|
|
if (error)
|
|
return (error);
|
|
}
|
|
if ((ep->pcd_filename[0] == PCD_UNUSED) ||
|
|
(ep->pcd_filename[0] == PCD_ERASED)) {
|
|
/*
|
|
* note empty slots, in case name is not found
|
|
*/
|
|
if (slotp->sl_status == SL_NONE) {
|
|
slotp->sl_status = SL_FOUND;
|
|
slotp->sl_blkno = slotp->sl_bp->b_blkno;
|
|
slotp->sl_offset = boff;
|
|
}
|
|
/*
|
|
* If unused we've hit the end of the directory
|
|
*/
|
|
if (ep->pcd_filename[0] == PCD_UNUSED)
|
|
break;
|
|
else
|
|
continue;
|
|
}
|
|
/*
|
|
* Hidden files do not participate in the search
|
|
*/
|
|
if (ep->pcd_attr & (PCA_HIDDEN | PCA_SYSTEM | PCA_LABEL))
|
|
continue;
|
|
if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
|
|
(bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
|
|
/*
|
|
* found the file
|
|
*/
|
|
if (fname[0] == '.') {
|
|
if (fname[1] == '.')
|
|
slotp->sl_flags = SL_DOTDOT;
|
|
else
|
|
slotp->sl_flags = SL_DOT;
|
|
} else {
|
|
slotp->sl_flags = 0;
|
|
}
|
|
slotp->sl_blkno = slotp->sl_bp->b_blkno;
|
|
slotp->sl_offset = boff;
|
|
slotp->sl_ep = ep;
|
|
return (0);
|
|
}
|
|
}
|
|
if (slotp->sl_bp != NULL) {
|
|
brelse(slotp->sl_bp);
|
|
slotp->sl_bp = NULL;
|
|
}
|
|
return (ENOENT);
|
|
}
|
|
|
|
/*
|
|
* Obtain the block at offset "offset" in file pcp.
|
|
*/
|
|
int
|
|
pc_blkatoff(pcp, offset, bpp, epp)
|
|
register struct pcnode *pcp;
|
|
register long offset;
|
|
struct buf **bpp;
|
|
struct pcdir **epp;
|
|
{
|
|
register struct pcfs *fsp;
|
|
register struct buf *bp;
|
|
int size;
|
|
int error;
|
|
daddr_t bn;
|
|
|
|
fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
|
|
size = pc_blksize(fsp, pcp, offset);
|
|
if (pc_blkoff(fsp, offset) >= size) {
|
|
return (ENOENT);
|
|
}
|
|
error = pc_bmap(pcp, pc_lblkno(fsp, offset), &bn, (daddr_t *)0);
|
|
if (error)
|
|
return (error);
|
|
bp = bread(fsp->pcfs_devvp, bn, size);
|
|
if (bp->b_flags & (B_ERROR | B_INVAL)) {
|
|
PCFSDEBUG(1)
|
|
printf("pc_blkatoff ");
|
|
if (bp->b_flags & B_ERROR)
|
|
pc_diskchanged(fsp);
|
|
else
|
|
brelse(bp);
|
|
return (EIO);
|
|
}
|
|
bp->b_flags |= B_NOCACHE; /* don't cache */
|
|
if (epp) {
|
|
*epp =
|
|
(struct pcdir *)(bp->b_un.b_addr + pc_blkoff(fsp, offset));
|
|
}
|
|
*bpp = bp;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Parse user filename into the pc form of "filename.extension".
|
|
* If names are too long for the format they are truncated silently.
|
|
* Tests for characters that are invalid in PCDOS and converts to upper case.
|
|
*/
|
|
static int
|
|
pc_parsename(namep, fnamep, fextp)
|
|
register char *namep;
|
|
register char *fnamep;
|
|
register char *fextp;
|
|
{
|
|
register int n;
|
|
register char c;
|
|
|
|
n = PCFNAMESIZE;
|
|
c = *namep++;
|
|
if (c == 0)
|
|
return (EINVAL);
|
|
if (c == '.') {
|
|
/*
|
|
* check for "." and "..".
|
|
*/
|
|
*fnamep++ = c;
|
|
n--;
|
|
if (c = *namep++) {
|
|
if ((c != '.') || (c = *namep)) /* ".x" or "..x" */
|
|
return (EINVAL);
|
|
*fnamep++ = '.';
|
|
n--;
|
|
}
|
|
} else {
|
|
/*
|
|
* filename up to '.'
|
|
*/
|
|
do {
|
|
if (n-- > 0) {
|
|
c = toupper(c);
|
|
if (!pc_validchar(c))
|
|
return (EINVAL);
|
|
*fnamep++ = c;
|
|
}
|
|
} while ((c = *namep++) && c != '.');
|
|
}
|
|
while (n-- > 0) { /* fill with blanks */
|
|
*fnamep++ = ' ';
|
|
}
|
|
/*
|
|
* remainder is extension
|
|
*/
|
|
n = PCFEXTSIZE;
|
|
if (c == '.') {
|
|
while ((c = *namep++) && n--) {
|
|
c = toupper(c);
|
|
if (!pc_validchar(c))
|
|
return (EINVAL);
|
|
*fextp++ = c;
|
|
}
|
|
}
|
|
while (n-- > 0) { /* fill with blanks */
|
|
*fextp++ = ' ';
|
|
}
|
|
return (0);
|
|
}
|