mirror of
https://github.com/rvtr/twl_wrapsdk.git
synced 2025-10-31 06:11:10 -04:00
git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/twl_wrapsdk/trunk@96 4ee2a332-4b2b-5046-8439-1ba90f034370
1091 lines
39 KiB
C
1091 lines
39 KiB
C
/*
|
|
* EBS - RTFS (Real Time File Manager)
|
|
*
|
|
* Copyright EBS Inc. 1987-2003
|
|
* All rights reserved.
|
|
* This code may not be redistributed in source or linkable object form
|
|
* without the consent of its author.
|
|
*/
|
|
/* APIREALT.C - Contains user level real time and special user level
|
|
file and FAT manipuation code.
|
|
|
|
The following routines are included:
|
|
|
|
pc_custer_size - Return the number of bytes per cluster for a drive
|
|
po_extend_file - Extend a file by with contiguous clusters.
|
|
po_chsize - Truncate or extend an open file.
|
|
pc_get_file_extents - Get the list of block segments that make up a file
|
|
pc_raw_read - Read blocks directly from a disk
|
|
pc_raw_write - Write blocks directly to a disk
|
|
pc_get_free_list - Get a list free cluster segments on the drive
|
|
|
|
*/
|
|
|
|
#include <rtfs.h>
|
|
|
|
/* PC_CLUSTER_SIZE - Return the number of bytes per cluster for a drive
|
|
|
|
Description
|
|
This function will return the cluster size mounted device
|
|
named in the argument.
|
|
|
|
|
|
Returns
|
|
The cluster size or zero if the device is not mounted.
|
|
|
|
errno is set to one of the following
|
|
0 - No error
|
|
PEINVALIDDRIVEID- Drive name is invalid
|
|
*****************************************************************************/
|
|
int pc_cluster_size(byte *drive) /*__apifn__*/
|
|
{
|
|
int driveno;
|
|
int ret_val;
|
|
DDRIVE *pdrive;
|
|
CHECK_MEM(int, 0) /* Make sure memory is initted */
|
|
|
|
rtfs_set_errno(0); /* pc_cluster_size: clear error status */
|
|
/* Get the drive and make sure it is mounted */
|
|
driveno = check_drive_name_mount(drive);
|
|
if (driveno < 0)
|
|
{
|
|
/* errno was set by check_drive */
|
|
return(0);
|
|
}
|
|
pdrive = pc_drno2dr(driveno);
|
|
ret_val = pdrive->bytespcluster;
|
|
release_drive_mount(driveno);/* Release lock, unmount if aborted */
|
|
return(ret_val);
|
|
}
|
|
|
|
/****************************************************************************
|
|
|
|
Name:
|
|
|
|
po_extend_file - Extend a file by with contiguous clusters.
|
|
|
|
Summary:
|
|
|
|
BOOLEAN po_extend_file(PCFD fd, dword_bytes, dword_*new_bytes, dword start_cluster,
|
|
int method)
|
|
|
|
Description
|
|
Given a file descriptor, n_bytes bytes and method, extend the file and
|
|
update the file size.
|
|
If the file can be extended by n_bytes contiguous bytes it will be done.
|
|
Otherwise if there is no contiguos region left on the disk that can
|
|
contain n_bytes the routine DOES NOT extend the file but does return
|
|
the length of the next largest contiguous region that is avaliable.
|
|
With this scheme an application can request to extend the file by a
|
|
given contiguous amount. If that is not possible the routine will return
|
|
the largest contiguous region available, the application can then decide
|
|
if it wishes to use this region. Please read the notes below for an
|
|
important discussion about the limitations of this routine.
|
|
|
|
The special method PC_FIXED_FIT may be used to extend the file beginning
|
|
at a specific cluster. With this method, start_cluster must be provided.
|
|
It is used as the starting point for the allocation. With this method it
|
|
is possible to precisely assign locations on the disk to file section.
|
|
This may be used for example to create interleaved files where several
|
|
files share a disk segment with interleaving clusters.
|
|
|
|
|
|
Method may be one of the following:
|
|
PC_FIRST_FIT - The first chain in which the extension will fit
|
|
PC_BEST_FIT - The smallest chain in which the extension will fit
|
|
PC_WORST_FIT - The largest chain in which the extension will fit
|
|
PC_FIXED_FIT - Extend n_clusters from start cluster
|
|
|
|
Please note the following issues and limitations.
|
|
|
|
. PC_FIRST_FIT is significantly faster than the others
|
|
. If the current end of file is not on a cluster boundary
|
|
then the region to be tested will start at the cluster
|
|
immediatley following the last cluster in the file and the
|
|
routine will allocate from the segment that starts with
|
|
that cluster or it will return the number of contiguous
|
|
bytes available starting at that cluster.
|
|
. If possible you should allocate space in contiguouos regions
|
|
that are a multiple of the drive s cluster size.
|
|
. If the PC_FIXED_FIT option is selected a start cluster must
|
|
must be supplied, n_bytes must be an even multiple of cluster
|
|
size and the file to extend must be either zero
|
|
sized or the end of file must be on a cluster boundary.
|
|
|
|
Returns
|
|
FALSE if an error occured.
|
|
TRUE if an error did not occur.
|
|
|
|
Returns n_bytes in *new_bytes if the file was extended. Otherwise it
|
|
returns the largest contiguous chain of bytes available in *new_bytes.
|
|
If it n_bytes is not returned the files was not extended.
|
|
|
|
errno is set to one of these values
|
|
0 - No error
|
|
PEBADF - Invalid file descriptor
|
|
PEACCES - File is read only
|
|
PEINVALIDPARMS - Invalid or inconsistent arguments
|
|
An ERTFS system error
|
|
*****************************************************************************/
|
|
|
|
BOOLEAN po_extend_file(PCFD fd, dword n_bytes, dword *new_bytes, dword start_cluster, int method) /* __apifn__ */
|
|
{
|
|
BOOLEAN ret_val;
|
|
CLUSTERTYPE clno;
|
|
dword n_alloced;
|
|
dword ltemp;
|
|
CLUSTERTYPE n_clusters;
|
|
CLUSTERTYPE largest_chain;
|
|
CLUSTERTYPE first_cluster;
|
|
CLUSTERTYPE last_cluster_in_chain;
|
|
CLUSTERTYPE i;
|
|
dword alloced_size;
|
|
dword new_alloced_size;
|
|
dword new_file_size;
|
|
dword range_check;
|
|
dword clusters_in_chain;
|
|
|
|
PC_FILE *pfile;
|
|
DDRIVE *pdr;
|
|
CHECK_MEM(BOOLEAN, 0) /* Make sure memory is initted */
|
|
|
|
if (!n_bytes)
|
|
{
|
|
ret_val = TRUE;
|
|
*new_bytes = 0;
|
|
goto return_error;
|
|
}
|
|
|
|
rtfs_set_errno(0); /* po_extend_file: clear error status */
|
|
|
|
/* Assume error to start */
|
|
ret_val = FALSE;
|
|
/* Get the file structure and semaphore lock the drive */
|
|
pfile = pc_fd2file(fd, PO_WRONLY|PO_RDWR);
|
|
/* Make sure we have write privileges. Make sure we got a count */
|
|
if (!pfile)
|
|
{ /* fd2file set errno */
|
|
goto return_error;
|
|
}
|
|
first_cluster = 0;
|
|
/* From here on we exit through alloc_done so we will unlock these resources */
|
|
pdr = pfile->pobj->pdrive;
|
|
/* Make sure our file pointer is ok */
|
|
if (!_synch_file_ptrs(pfile))
|
|
{ /* synch file pointer set errno */
|
|
goto alloc_done;
|
|
}
|
|
new_file_size = pfile->pobj->finode->fsize + n_bytes;
|
|
/* Fail if it exceeds MAX_FILE_SIZE or it wraps 32 bit offset max */
|
|
if (new_file_size > RTFS_MAX_FILE_SIZE || new_file_size < pfile->pobj->finode->fsize)
|
|
{
|
|
rtfs_set_errno(PETOOLARGE); /* po_extend_file: new size too large */
|
|
goto alloc_done;
|
|
}
|
|
new_alloced_size = (new_file_size + pdr->byte_into_cl_mask) &
|
|
~(pdr->byte_into_cl_mask);
|
|
if (new_alloced_size < new_file_size)
|
|
new_alloced_size = 0xffffffff;
|
|
|
|
/* Round the file size up to its cluster size by adding in clustersize-1
|
|
and masking off the low bits */
|
|
alloced_size = (pfile->pobj->finode->fsize + pdr->byte_into_cl_mask) &
|
|
~(pdr->byte_into_cl_mask);
|
|
if (alloced_size < pfile->pobj->finode->fsize)
|
|
alloced_size = 0xffffffff;
|
|
|
|
if (method == PC_FIXED_FIT)
|
|
{
|
|
/* Check for errors. If the old size was not on a cluster
|
|
boundary or the new size is not on a cluster boundary
|
|
or now start cluster was provided then its an error
|
|
for the FIXED_FIT option
|
|
*/
|
|
if ( ( new_alloced_size != new_file_size) ||
|
|
(alloced_size != pfile->pobj->finode->fsize) ||
|
|
!start_cluster)
|
|
{
|
|
/* If using fixed fit and not on a cluster boundary
|
|
it is an error */
|
|
rtfs_set_errno(PEINVALIDPARMS); /* po_extend_file: fixed fit and not on a cluster boundary */
|
|
goto alloc_done;
|
|
}
|
|
}
|
|
n_clusters = pc_chain_length(pdr, new_file_size) -
|
|
pc_chain_length(pdr, pfile->pobj->finode->fsize);
|
|
|
|
if (n_clusters)
|
|
{ /* We need space so try to allocate */
|
|
/* Find the end of the files chain */
|
|
last_cluster_in_chain = pfile->fptr_cluster;
|
|
clno = pfile->fptr_cluster;
|
|
{
|
|
dword ltemp,fptr_mod_cluster;
|
|
/* Round fptr down to cluster boundary */
|
|
fptr_mod_cluster = pfile->fptr & ~(pdr->byte_into_cl_mask);
|
|
/* Subtract from the rounded up alloced_size value */
|
|
ltemp = alloced_size-fptr_mod_cluster;
|
|
clusters_in_chain = ltemp >> (int) (pfile->pobj->pdrive->log2_secpalloc+9);
|
|
}
|
|
if (clusters_in_chain)
|
|
{
|
|
if ((clno < 2) || (clno > pdr->maxfindex) )
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER);
|
|
goto alloc_done;
|
|
}
|
|
range_check = 0;
|
|
while (clno != 0xffffffff)
|
|
{
|
|
if (++range_check > clusters_in_chain)
|
|
break;
|
|
last_cluster_in_chain = clno;
|
|
clno = FATOP(pdr)->fatop_clnext(pdr , clno); /* File */
|
|
if (!clno) /* clnext set errno */
|
|
goto alloc_done;
|
|
}
|
|
if (range_check != clusters_in_chain)
|
|
{
|
|
rtfs_set_errno(PEINVALIDCLUSTER);
|
|
goto alloc_done;
|
|
}
|
|
if (get_errno())
|
|
goto alloc_done; /* If FATOP set errno we have trouble */
|
|
}
|
|
n_alloced = 0;
|
|
largest_chain = 0;
|
|
if ((method == PC_FIXED_FIT) || (alloced_size != pfile->pobj->finode->fsize))
|
|
{
|
|
/* If the end of file is not on a cluster boundary
|
|
see how many are avalailable that are contiguous
|
|
with the last cluster in our file */
|
|
/* Also have special code here since FIXED_FIT Works this way */
|
|
if (method == PC_FIXED_FIT)
|
|
clno = (CLUSTERTYPE) start_cluster;
|
|
else
|
|
clno = (CLUSTERTYPE) (last_cluster_in_chain + 1);
|
|
|
|
ltemp = n_clusters;
|
|
/* Walk up the FAT linearly. try for n_clusters */
|
|
while (ltemp)
|
|
{
|
|
if (!FATOP(pdr)->fatop_faxx(pdr, clno, &i) || i != 0) /* File */
|
|
break;
|
|
else
|
|
{
|
|
largest_chain++;
|
|
ltemp--;
|
|
clno++;
|
|
}
|
|
}
|
|
if (get_errno())
|
|
goto alloc_done; /* If FATOP set errno we have trouble */
|
|
if (!ltemp)
|
|
{
|
|
/* We got a contiguous region */
|
|
n_alloced = n_clusters;
|
|
largest_chain = 0;
|
|
/* That starts here */
|
|
if (method == PC_FIXED_FIT)
|
|
first_cluster = (CLUSTERTYPE) start_cluster;
|
|
else
|
|
first_cluster = (CLUSTERTYPE) last_cluster_in_chain + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Now allocate clusters. To find the free space we look in three
|
|
regions until we find space:
|
|
1 we look from the last cluster in the file to the end of the fat
|
|
(skip 1 if there is no chain)
|
|
2 we look from the beginning of the data area to the end of the fat
|
|
3 we look from the beginning of the fat area to the end of the fat
|
|
*/
|
|
|
|
clno = last_cluster_in_chain;
|
|
if (!clno)
|
|
clno = pdr->free_contig_base;
|
|
while (clno)
|
|
{
|
|
n_alloced = pc_find_contig_clusters(pdr, clno, &first_cluster, n_clusters, method);
|
|
if (n_alloced == 0xfffffffful)
|
|
{
|
|
/* Fat operations have set errno */
|
|
ret_val = FALSE;
|
|
goto alloc_done;
|
|
}
|
|
else if ((CLUSTERTYPE)n_alloced >= n_clusters)
|
|
break; /* We got our chain */
|
|
else
|
|
{
|
|
/* We did not get enough space. keep track of the biggest chain.
|
|
Do not need to store first_cluster since we will not alocate chains
|
|
smaller than what we need */
|
|
if (largest_chain < (CLUSTERTYPE)n_alloced)
|
|
largest_chain = (CLUSTERTYPE)n_alloced;
|
|
}
|
|
/* If we were searching between from the end of the file and end of fat
|
|
look from the beginning of the file data area */
|
|
if (clno == last_cluster_in_chain)
|
|
{
|
|
if (last_cluster_in_chain == pdr->free_contig_base)
|
|
clno = 2;
|
|
else
|
|
clno = pdr->free_contig_base;
|
|
}
|
|
/* If we were searching between the beginning of the file data area
|
|
and end of fat look from the fat */
|
|
else if (clno == pdr->free_contig_base)
|
|
{
|
|
if (pdr->free_contig_base == 2)
|
|
break;
|
|
else
|
|
clno = 2;
|
|
}
|
|
else /* We have looked everywhere. No luck */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((CLUSTERTYPE)n_alloced < n_clusters)
|
|
{
|
|
/* We did not get what we asked for so we return the biggest free
|
|
contiguous chain available in bytes plus whatever is left
|
|
in the last cluster */
|
|
*new_bytes = (largest_chain << (pdr->log2_secpalloc+9));
|
|
if (alloced_size > pfile->pobj->finode->fsize) /* Avoid wrap */
|
|
*new_bytes += (alloced_size - pfile->pobj->finode->fsize);
|
|
goto alloc_done;
|
|
}
|
|
/* else */
|
|
|
|
/* We found a large enough contiguos group of clusters */
|
|
/* Turn them into a chain */
|
|
clno = first_cluster;
|
|
for (i = 0; i < (n_clusters-1); i++, clno++)
|
|
{
|
|
/* Link the current cluster to the next one */
|
|
/* If it fails FATOP will set errno */
|
|
if (!FATOP(pdr)->fatop_pfaxx(pdr, clno, (CLUSTERTYPE) (clno+1) )) /* File */
|
|
goto alloc_done;
|
|
/* 2-19-99 - Bug fix. was not decrementing free cluster count */
|
|
pdr->known_free_clusters = (long)(pdr->known_free_clusters - 1);
|
|
}
|
|
|
|
/* Terminate the list */
|
|
if (!FATOP(pdr)->fatop_pfaxxterm(pdr, clno)) /* File */
|
|
{ /* If it fails FATOP will set errno */
|
|
goto alloc_done;
|
|
}
|
|
else
|
|
{
|
|
/* 2-19-99 - Bug fix. was not decrementing free cluster count */
|
|
pdr->known_free_clusters = (long)(pdr->known_free_clusters - 1);
|
|
}
|
|
if (last_cluster_in_chain)
|
|
{
|
|
/* The file already has clusters in it. Append our new chain */
|
|
if (!FATOP(pdr)->fatop_pfaxx(pdr, last_cluster_in_chain, first_cluster)) /* File */
|
|
{ /* If it fails FATOP will set errno */
|
|
goto alloc_done;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Put our chain into the directory entry */
|
|
pc_pfinode_cluster(pfile->pobj->pdrive,pfile->pobj->finode,first_cluster);
|
|
/* Use synch_pointers to set our file pointers up */
|
|
pfile->fptr_cluster = 0; /* This is already true but... */
|
|
pfile->fptr_block = 0;
|
|
pfile->fptr = 0;
|
|
}
|
|
/* Flush the fat */
|
|
if (!FATOP(pdr)->fatop_flushfat(pdr->driveno))
|
|
{ /* If it fails FATOP will set errno */
|
|
goto alloc_done;
|
|
}
|
|
}
|
|
|
|
/* If we get here it works and we should update the file size */
|
|
*new_bytes = n_bytes;
|
|
ret_val = TRUE;
|
|
|
|
pfile->pobj->finode->fsize = new_file_size;
|
|
/* Write the directory entry. Set archive & date */
|
|
if (!pc_update_inode(pfile->pobj, TRUE, TRUE) )
|
|
{ /* Update inode set errno */
|
|
ret_val = FALSE;
|
|
goto alloc_done;
|
|
}
|
|
/* call synch to take care of both the eof condition and the case where
|
|
we just alloced the beginning of the chain */
|
|
if (!_synch_file_ptrs(pfile))
|
|
{ /* synch file pointer set errno */
|
|
ret_val = FALSE;
|
|
goto alloc_done;
|
|
}
|
|
/* All code exits through here. ret_val determines if the function was
|
|
successful. If -1 it is an error. */
|
|
alloc_done:
|
|
if (!release_drive_mount_write(pdr->driveno))/* Release lock, unmount if aborted */
|
|
ret_val = FALSE;
|
|
return_error:
|
|
return(ret_val);
|
|
}
|
|
|
|
/******************************************************************************
|
|
PC_FIND_CONTIG_CLUSTERS - Find at least MIN_CLUSTER clusters.
|
|
|
|
|
|
Description
|
|
Using the provided method, search the FAT from start_pt to the
|
|
end for a free contiguous chain of at least MIN_CLUSTERS. If less
|
|
than MIN_CLUSTERS are found the largest free chain in the region is
|
|
returned.
|
|
|
|
There are three possible methods:
|
|
PC_FIRST_FIT - The first free chain >= MIN_CLUSTERS is returned
|
|
PC_BEST_FIT - The smallest chain >= MIN_CLUSTERS is returned
|
|
PC_WORST_FIT - The largest chain >= MIN_CLUSTERS is returned
|
|
|
|
Choose the method that will work best for you.
|
|
|
|
Note: PC_FIRST_FIT is significantly faster faster than the others
|
|
|
|
NOTE: The chain is not created. The caller must convert the
|
|
clusters to an allocated chain.
|
|
|
|
Returns
|
|
Returns the number of contiguous clusters found up to MIN_CLUSTERS.
|
|
*pchain contains the cluster number at the beginning of the chain.
|
|
On error return 0xffff
|
|
Example:
|
|
Get the largest free chain on the disk:
|
|
large = pc_find_contig_clusters(pdr, 2, &chain, 0xffff, PC_FIRST_FIT);
|
|
|
|
*****************************************************************************/
|
|
/* Note: the FAT is locked before this call is made */
|
|
|
|
dword pc_find_contig_clusters(DDRIVE *pdr, CLUSTERTYPE startpt, CLUSTERTYPE *pchain, CLUSTERTYPE min_clusters, int method) /* __apifn__ */
|
|
{
|
|
CLUSTERTYPE i;
|
|
CLUSTERTYPE value;
|
|
CLUSTERTYPE best_chain;
|
|
CLUSTERTYPE best_size;
|
|
CLUSTERTYPE chain_start;
|
|
CLUSTERTYPE chain_size;
|
|
CLUSTERTYPE largest_size;
|
|
CLUSTERTYPE largest_chain;
|
|
CLUSTERTYPE endpt;
|
|
|
|
best_chain = 0;
|
|
best_size = 0;
|
|
chain_start = 0;
|
|
chain_size = 0;
|
|
largest_size = 0;
|
|
largest_chain = 0;
|
|
endpt = pdr->maxfindex;
|
|
|
|
for (i = startpt; i <= endpt; i++)
|
|
{
|
|
if (!FATOP(pdr)->fatop_faxx(pdr, i, &value) ) /* File */
|
|
return(0xfffffffful); /* IO error .. oops */
|
|
if (value == 0)
|
|
{
|
|
/* Cluster is free. Run some tests on it. */
|
|
if (chain_start)
|
|
{
|
|
/* We are in a contiguous region already. Bump the count */
|
|
chain_size++;
|
|
}
|
|
else
|
|
{
|
|
/* Just starting a contiguous region */
|
|
chain_size = 1;
|
|
chain_start = i;
|
|
}
|
|
/* If using first fit see if we crossed the threshold */
|
|
if (method == PC_FIRST_FIT)
|
|
{
|
|
if (chain_size >= min_clusters)
|
|
{
|
|
best_chain = chain_start;
|
|
best_size = chain_size;
|
|
break;
|
|
}
|
|
}
|
|
} /* if value == 0*/
|
|
/* Did we just finish scanning a contiguous chain ?? */
|
|
if (chain_size && ((value != 0) || (i == endpt)) )
|
|
{
|
|
/* Remember the largest chain */
|
|
if (chain_size > largest_size)
|
|
{
|
|
largest_size = chain_size;
|
|
largest_chain = chain_start;
|
|
}
|
|
if (method == PC_BEST_FIT)
|
|
{
|
|
if (chain_size == min_clusters)
|
|
{
|
|
/* The chain is exactly the size we need take it. */
|
|
best_chain = chain_start;
|
|
best_size = chain_size;
|
|
break;
|
|
}
|
|
if (chain_size > min_clusters)
|
|
{
|
|
if (!best_chain || (chain_size < best_size))
|
|
{
|
|
/* Chain is closest to what we need so far note it. */
|
|
best_size = chain_size;
|
|
best_chain = chain_start;
|
|
}
|
|
}
|
|
} /* if BEST_FIT */
|
|
else if (method == PC_WORST_FIT)
|
|
{
|
|
if (chain_size >= min_clusters)
|
|
{
|
|
if (!best_chain || chain_size > best_size)
|
|
{
|
|
best_size = chain_size;
|
|
best_chain = chain_start;
|
|
}
|
|
}
|
|
} /* if WORST_FIT */
|
|
/*
|
|
* else if (method == PC_BEST_FIT)
|
|
* ;
|
|
*/
|
|
chain_size = 0;
|
|
chain_start = 0;
|
|
} /* if (chain_size && ((value != 0) || (i == endpt)) ) */
|
|
} /* for (i = startpt; i <= endpt; i++) */
|
|
|
|
/* If we have a best chain return it here. Else return the largest chain */
|
|
if (best_chain)
|
|
{
|
|
*pchain = best_chain;
|
|
return((dword)best_size);
|
|
}
|
|
else
|
|
{
|
|
*pchain = largest_chain;
|
|
return((dword)largest_size);
|
|
}
|
|
|
|
}
|
|
|
|
/**************************************************************************
|
|
PO_CHSIZE - Truncate or extend an open file.
|
|
|
|
Description
|
|
Given a file handle and a new file size either extend the file or
|
|
truncate it. If the current file pointer is still within the range
|
|
of the file it is unmoved, otherwise it is moved to the end of file.
|
|
|
|
Note:
|
|
This is not an ATOMIC file system operation. It uses other API calls
|
|
po_lseek, po_truncate and po_write to size, truncate and extend the
|
|
file.
|
|
|
|
Returns
|
|
Returns 0 on suucess -1 on error
|
|
|
|
errno is set with one of these values
|
|
0 - No error
|
|
PEBADF - Invalid file descriptor
|
|
PEACCES - File is read only
|
|
PEINVALIDPARMS - Invalid or inconsistent arguments
|
|
An ERTFS system error
|
|
*****************************************************************************/
|
|
|
|
int po_chsize(PCFD fd, dword offset) /*__apifn__*/
|
|
{
|
|
dword orig_fp;
|
|
dword eof_fp;
|
|
dword n_to_extend;
|
|
dword n_to_try;
|
|
dword ltemp;
|
|
int ret_val;
|
|
|
|
rtfs_set_errno(0); /* po_chsize: clear error status. routines that we call will set errno on errors */
|
|
|
|
if (!po_ulseek(fd, 0, &orig_fp, PSEEK_CUR))
|
|
return(-1);
|
|
if (!po_ulseek(fd, 0,&eof_fp, PSEEK_END))
|
|
return(-1);
|
|
|
|
/* If size is unchanged just return */
|
|
if (eof_fp == offset)
|
|
{
|
|
ret_val = 0;
|
|
}
|
|
/* If offset < eof we truncate */
|
|
else if (eof_fp > offset)
|
|
{
|
|
if (!po_truncate(fd, offset))
|
|
return(-1);
|
|
/* Restore the file pointer if we can */
|
|
if (orig_fp < offset)
|
|
po_ulseek(fd, orig_fp,<emp, PSEEK_SET);
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
if (offset > RTFS_MAX_FILE_SIZE)
|
|
{
|
|
rtfs_set_errno(PETOOLARGE); /* po_extend_file: new size too large */
|
|
return(-1);
|
|
}
|
|
|
|
/* Have to extend the file */
|
|
n_to_extend = offset - eof_fp;
|
|
ret_val= 0; /* Assume it worked at first */
|
|
while (n_to_extend)
|
|
{
|
|
/* Try to extend the file. po_extend_file will either extend the
|
|
file and return the value we sent in or it will not extend the
|
|
file and return a hint of what should work */
|
|
n_to_try = n_to_extend;
|
|
while (n_to_try)
|
|
{
|
|
if (!po_extend_file(fd, n_to_try, <emp, 0, PC_FIRST_FIT))
|
|
{
|
|
ret_val = -1;
|
|
n_to_extend = 0; /* Force break from the outer loop */
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (ltemp == n_to_try)
|
|
{
|
|
n_to_extend -= ltemp;/* We extended by n_to_try */
|
|
n_to_try = 0; /* Break from inner loop */
|
|
}
|
|
else
|
|
n_to_try = ltemp; /* We could not extend but ltemp
|
|
should to work so try again*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!ret_val)
|
|
{
|
|
/* restore seek pointer and return */
|
|
if (!po_ulseek(fd, orig_fp, <emp, PSEEK_SET) || ltemp != orig_fp)
|
|
return(-1);
|
|
}
|
|
return(ret_val);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Name:
|
|
|
|
pc_get_file_extents - Get the list of block segments that make up a file
|
|
|
|
|
|
Summary:
|
|
|
|
#include <rtfs.h>
|
|
|
|
int pc_get_file_extents(PCFD fd, int infolistsize,
|
|
FILESEGINFO *plist, BOOLEAN raw)
|
|
|
|
Where FILESEGINFO is a structure defined as:
|
|
|
|
typedef struct fileseginfo {
|
|
long block; Block number of the current extent
|
|
long nblocks; Number of blocks in the extent
|
|
} FILESEGINFO;
|
|
|
|
And infolistsize is the number of elements in the storage pointed to
|
|
by plist.
|
|
|
|
If raw is TRUE the blocks are reported as block offsets from the physical
|
|
base of the drive. Otherwise the block offset origin is the begining of
|
|
the partition. Set raw to TRUE if you will be using the resultant list
|
|
to set up DMA transfers to or from the disk.
|
|
|
|
Description:
|
|
This routines traverse the cluster chain of the open file fd and logs
|
|
into the list at plist the block location and length in blocks of each
|
|
segment of the file.
|
|
The block numbers and block length information can then be used to
|
|
read and write the file directly using pc_raw_read() and pc_raw_write()
|
|
or the information may be used to set up DMA transfers to or from the
|
|
raw block locations.
|
|
If the file contains more extents than will fit in plist as indicated
|
|
by infolistsize then the list is not updated beyond infolistsize elements
|
|
but the count is updated and returned so the list size may be adjusted
|
|
and the routine may be called again.
|
|
|
|
Returns
|
|
Returns the number of extents in the file or -1 on error.
|
|
|
|
errno is set to one of the following
|
|
|
|
0 - No error
|
|
PEBADF - Invalid file descriptor
|
|
An ERTFS system error
|
|
****************************************************************************/
|
|
|
|
int pc_get_file_extents(PCFD fd, int infolistsize, FILESEGINFO *plist, BOOLEAN raw) /* __apifn__ */
|
|
{
|
|
int p_errno;
|
|
int end_of_chain;
|
|
int ret_val;
|
|
int n_segments;
|
|
CLUSTERTYPE first_cluster;
|
|
CLUSTERTYPE next_cluster;
|
|
CLUSTERTYPE n_clusters_to_seek;
|
|
CLUSTERTYPE n_clusters;
|
|
long partition_start;
|
|
PC_FILE *pfile;
|
|
DDRIVE *pdrive;
|
|
CHECK_MEM(int, -1) /* Make sure memory is initted */
|
|
|
|
p_errno = 0;
|
|
rtfs_set_errno(0); /* pc_get_file_extents: clear error status. */
|
|
|
|
/* Get the file structure and semaphore lock the drive */
|
|
pfile = pc_fd2file(fd, 0);
|
|
if (!pfile)
|
|
{ /* pc_fd2file set errno */
|
|
ret_val = -1;
|
|
goto return_error;
|
|
}
|
|
pdrive = pfile->pobj->pdrive;
|
|
|
|
partition_start = pdrive->partition_base;
|
|
|
|
ret_val = 0; /* Assume Zero segments to start */
|
|
if (pfile->pobj->finode->fsize)
|
|
{
|
|
n_segments = 0;
|
|
/* How many clusters in the file */
|
|
n_clusters_to_seek = (CLUSTERTYPE)
|
|
((pfile->pobj->finode->fsize+(pdrive->secpalloc<<9)-1) >> (pdrive->log2_secpalloc+9));
|
|
first_cluster = pc_finode_cluster(pdrive,pfile->pobj->finode);
|
|
|
|
/* Traverse the file one chain at a time. Log start point (in blocks)
|
|
and the length in blocks of each segment */
|
|
while (n_clusters_to_seek)
|
|
{
|
|
/* Get the chain length and the start of the next chain */
|
|
end_of_chain = 0;
|
|
n_clusters = FATOP(pdrive)->fatop_get_chain(pdrive, first_cluster,
|
|
&next_cluster, n_clusters_to_seek, &end_of_chain);
|
|
if (!n_clusters)
|
|
{
|
|
if (!get_errno()) /* If errno set already use that */
|
|
p_errno = PEINVALIDCLUSTER;
|
|
n_segments = -1;
|
|
break;
|
|
}
|
|
|
|
n_segments += 1;
|
|
if (n_segments <= infolistsize)
|
|
{
|
|
plist->block = pc_cl2sector(pdrive,first_cluster);
|
|
if (raw)
|
|
plist->block += partition_start;
|
|
plist->nblocks = n_clusters << pdrive->log2_secpalloc;
|
|
plist++;
|
|
}
|
|
n_clusters_to_seek = (CLUSTERTYPE) (n_clusters_to_seek - n_clusters);
|
|
/* Check for corrupted file
|
|
If there are more to seek but the cluster pointer
|
|
is at the end this means that the file size is
|
|
longer than the actual chain length so set errno
|
|
to PEINVALIDCLUSTER and force error return */
|
|
if (n_clusters_to_seek && end_of_chain)
|
|
{
|
|
p_errno = PEINVALIDCLUSTER;
|
|
n_segments = -1;
|
|
break;
|
|
}
|
|
else
|
|
first_cluster = next_cluster;
|
|
}
|
|
ret_val = n_segments;
|
|
}
|
|
release_drive_mount(pdrive->driveno);/* Release lock, unmount if aborted */
|
|
return_error:
|
|
if (p_errno)
|
|
rtfs_set_errno(p_errno);
|
|
return(ret_val);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Name:
|
|
pc_raw_read - Read blocks directly from a disk
|
|
|
|
Summary:
|
|
|
|
#include <rtfs.h>
|
|
|
|
int pc_raw_read(int driveno, byte *buf, long blockno, int nblocks, BOOLEAN raw_io)
|
|
|
|
Description:
|
|
Attempt to read nblocks blocks starting at blockno. If raw_io is TRUE
|
|
then blockno is the offset from the beginning of the disk itself. If
|
|
raw_io is FALSE then blockno is the offset from the beginning of the
|
|
partition.
|
|
This routine may be used in conjunction with pc_get_file_extents()
|
|
to find and read blocks without the additional overhead incurred
|
|
when calling po_read().
|
|
|
|
The maximum allowable value for nblocks is 128.
|
|
|
|
Note: It is possible to read any range of blocks in the disk.
|
|
|
|
Returns:
|
|
|
|
Returns 0 if the read succeeded or -1 on error.
|
|
|
|
errno is set to one of the following
|
|
0 - No error
|
|
PEINVALIDDRIVEID - Driveno is incorrect
|
|
PEINVALIDPARMS - Invalid or inconsistent arguments
|
|
PEIOERRORREAD - The read operation failed
|
|
An ERTFS system error
|
|
*****************************************************************************/
|
|
|
|
int pc_raw_read(int driveno, byte *buf, long blockno, int nblocks, BOOLEAN raw_io) /*__apifn__*/
|
|
{
|
|
CHECK_MEM(int, -1) /* Make sure memory is initted */
|
|
|
|
rtfs_set_errno(0); /* pc_raw_read: clear error status. */
|
|
|
|
/* return -1 if bad arguments */
|
|
if (!nblocks || !buf || (nblocks > 128))
|
|
{
|
|
rtfs_set_errno(PEINVALIDPARMS);
|
|
return(-1);
|
|
}
|
|
if (!check_drive_number_mount(driveno)) /* Check set errno */
|
|
return(-1);
|
|
/* READ */
|
|
if (!devio_read(driveno, (dword) blockno, buf, (word)nblocks, raw_io))
|
|
{
|
|
rtfs_set_errno(PEIOERRORREAD);
|
|
release_drive_mount(driveno);/* Release lock, unmount if aborted */
|
|
return(-1);
|
|
}
|
|
release_drive_mount(driveno);/* Release lock, unmount if aborted */
|
|
return(0);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Name:
|
|
pc_raw_write - Write blocks directly to a disk
|
|
|
|
Summary:
|
|
|
|
#include <rtfs.h>
|
|
|
|
int pc_raw_write(int driveno, byte *buf, long blockno, int nblocks, BOOLEAN raw_io)
|
|
|
|
Description:
|
|
Attempt to write nblocks blocks starting at blockno. If raw_io is TRUE
|
|
then blockno is the offset from the beginning of the disk itself. If
|
|
raw_io is FALSE then blockno is the offset from the beginning of the
|
|
partition.
|
|
This routine may be used in conjunction with pc_get_file_extents()
|
|
to find and read blocks without the additional overhead incurred
|
|
when calling po_read().
|
|
|
|
The maximum allowable value for nblocks is 128.
|
|
|
|
Note: It is possible to write any range of blocks in the disk.
|
|
|
|
|
|
Returns
|
|
Returns 0 if the write succeeded or -1 on error.
|
|
|
|
errno is set to one of the following
|
|
0 - No error
|
|
PEINVALIDDRIVEID - Driveno is incorrect
|
|
PEINVALIDPARMS - Invalid or inconsistent arguments
|
|
PEIOERRORWRITE - The read operation failed
|
|
An ERTFS system error
|
|
*****************************************************************************/
|
|
|
|
int pc_raw_write(int driveno, byte *buf, long blockno, int nblocks, BOOLEAN raw_io) /*__apifn__*/
|
|
{
|
|
CHECK_MEM(int, -1) /* Make sure memory is initted */
|
|
|
|
rtfs_set_errno(0); /* pc_raw_write: clear error status. */
|
|
|
|
/* return -1 if bad arguments */
|
|
if (!nblocks || !buf || (nblocks > 128))
|
|
{
|
|
rtfs_set_errno(PEINVALIDPARMS);
|
|
return(-1);
|
|
}
|
|
/* we will use release_drive_mount() not release_drive_mount_write()
|
|
since we did not change file system structures */
|
|
|
|
if (!check_drive_number_mount(driveno)) /* Check set errno */
|
|
return(-1);
|
|
if (!devio_write(driveno, (dword) blockno, buf, (word)nblocks, raw_io))
|
|
{
|
|
rtfs_set_errno(PEIOERRORWRITE);
|
|
release_drive_mount(driveno);/* Release lock, unmount if aborted */
|
|
return(-1);
|
|
}
|
|
release_drive_mount(driveno);/* Release lock, unmount if aborted */
|
|
return(0);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Name:
|
|
|
|
pc_get_free_list - Get a list free cluster segments on the drive
|
|
|
|
|
|
Summary:
|
|
|
|
#include <rtfs.h>
|
|
|
|
int pc_get_free_list(byte *drivename, int listsize, FREELISTINFO *plist, long threshhold)
|
|
|
|
Where
|
|
|
|
drivename is a valid drive specifier for example C:. An empty string
|
|
denotes the current workin drive.
|
|
|
|
|
|
FREELISTINFO is a structure defined as:
|
|
|
|
typedef struct freelistinfo {
|
|
CLUSTERTYPE cluster; Cluster where the free region starts
|
|
long nclusters; Number of free clusters the free segment
|
|
} FREELISTINFO;
|
|
|
|
listsize is the number of elements in the storage pointed to
|
|
by plist.
|
|
|
|
|
|
threshhold is the smallest contiguous free region to report.
|
|
This is provided to allow the caller to exclude free chains
|
|
that are too snall to be interesting. Setting this to a higher
|
|
value also reduces the number of entries in plist that will
|
|
be used up. The value of threshold must be at least 1. If it
|
|
is one then every free cluster segment is reported.
|
|
|
|
Description:
|
|
This routines traverses the file allocation table of the drive.
|
|
It places in the results structure the starting point and size
|
|
of each free segment
|
|
|
|
The free list information may then be used by po_extend_file() to
|
|
allocate specific clusters for specific files.
|
|
If the FAT contains more free extents than will fit in plist as indicated
|
|
by listsize then the list is not updated beyond listsize elements
|
|
but the count is updated and returned so the list size may be adjusted
|
|
and the routine may be called again.
|
|
|
|
Returns
|
|
Returns the number of free segments or -1 on error.
|
|
|
|
errno is set to one of the following
|
|
0 - No error
|
|
PEINVALIDDRIVEID - Driveno is incorrect
|
|
PEINVALIDPARMS - Invalid or inconsistent arguments
|
|
An ERTFS system error
|
|
****************************************************************************/
|
|
|
|
|
|
int pc_get_free_list(int driveno, int listsize, FREELISTINFO *plist, long threshhold) /* __apifn__ */
|
|
{
|
|
CLUSTERTYPE clno;
|
|
CLUSTERTYPE value;
|
|
int index;
|
|
long region_size;
|
|
DDRIVE *pdr;
|
|
int ret_val;
|
|
int p_errno;
|
|
CHECK_MEM(PCFD, -1) /* Make sure memory is initted */
|
|
|
|
ret_val = 0;
|
|
p_errno = 0;
|
|
index = 0;
|
|
region_size = 0;
|
|
|
|
rtfs_set_errno(0); /* pc_get_free_list: clear error status. */
|
|
|
|
if (threshhold < 1)
|
|
{
|
|
rtfs_set_errno(PEINVALIDPARMS);
|
|
return(-1);
|
|
}
|
|
|
|
/* Get the drive and make sure it is mounted */
|
|
if (!check_drive_number_mount(driveno)) /* Check set errno */
|
|
return(-1);
|
|
|
|
pdr = pc_drno2dr(driveno);
|
|
for (clno = 2; clno <= pdr->maxfindex; clno++)
|
|
{
|
|
if (!FATOP(pdr)->fatop_faxx(pdr, clno, &value) ) /* Fat */
|
|
{
|
|
/* Fatop will set errno */
|
|
region_size = 0;
|
|
ret_val = -1;
|
|
break;
|
|
}
|
|
if (value == 0)
|
|
{
|
|
/* The cluster is free */
|
|
if (region_size)
|
|
{
|
|
/* We are inside a free extent so update counter */
|
|
region_size++;
|
|
}
|
|
else
|
|
{
|
|
/* Start tracking a new one */
|
|
region_size = 1;
|
|
if (index < listsize)
|
|
{
|
|
plist->cluster = clno;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Cluster is non-zero see if we need to terminate a region */
|
|
if (region_size >= threshhold && index < listsize)
|
|
{
|
|
plist->nclusters = region_size;
|
|
plist++;
|
|
}
|
|
if (region_size >= threshhold)
|
|
index++;
|
|
region_size = 0;
|
|
}
|
|
}
|
|
if (!ret_val)
|
|
{
|
|
/* If we left the for loop while still in a region note it */
|
|
if (region_size >= threshhold)
|
|
{
|
|
if (index < listsize)
|
|
{
|
|
plist->nclusters = region_size;
|
|
}
|
|
index++;
|
|
}
|
|
ret_val = index;
|
|
}
|
|
release_drive_mount(driveno);/* Release lock, unmount if aborted */
|
|
if (p_errno)
|
|
rtfs_set_errno(p_errno);
|
|
return(ret_val);
|
|
}
|