⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 stream.c

📁 从大量的wince源代码中剥离出的fat文件系统源代码。移植性非常高。 微软的代码
💻 C
📖 第 1 页 / 共 4 页
字号:

    
    if (pstm->s_clusFirst < DATA_CLUSTER)
        return cbNew <= pstm->s_run.r_end? ERROR_SUCCESS : ERROR_DISK_FULL;

    pvol = pstm->s_pvol;

    if (pvol->v_flags & VOLF_READONLY)
        return ERROR_WRITE_PROTECT;

    clusEnd = PositionStream(pstm, cbNew);

    // If clusEnd is UNKNOWN_CLUSTER, then the new size was past EOF, so
    // we need to add clusters until we have enough to reach the specified size.

    if (clusEnd == UNKNOWN_CLUSTER) {

        // pstm->s_run.r_clusThis should contain the first cluster of the
        // last run in a file's cluster chain.  pstm->s_run.r_end is the
        // position immediately past the last cluster (which we store in posEnd).

        posEnd = pstm->s_run.r_end;

        // Assert that the reason we're here is that the stream is not
        // large enough.

        ASSERT(posEnd < cbNew);

        // The RUN only remembers a starting cluster (r_clusThis) and a
        // length (pstm->s_run.r_end - pstm->s_run.r_start), which we must
        // convert into an ending cluster (clusEnd).  If clusEnd is
        // UNKNOWN_CLUSTER, then the stream currently contains no clusters!

        clusEnd = pstm->s_run.r_clusThis;

        if (clusEnd != UNKNOWN_CLUSTER) {
            ASSERT(pstm->s_run.r_clusThis != 0);
            ASSERT(pstm->s_run.r_end > pstm->s_run.r_start);

            clusEnd = pstm->s_run.r_clusThis +
                      ((pstm->s_run.r_end - pstm->s_run.r_start-1) >>
                       (pvol->v_log2cblkClus + BLOCK_LOG2));
        }

        LockFAT(pvol);

        cclusRun = cbRun = 0;
        clusRunEnd = UNKNOWN_CLUSTER;
        clusPrev = clusRun = NO_CLUSTER;

        // If we have the number of free clusters cached, then let's calculate
        // the minimum number of clusters we'll need, and if there are not enough
        // free clusters to satisfy the request, then force an error.

        if (pvol->v_cclusFree != UNKNOWN_CLUSTER) {
            if (pvol->v_cclusFree == 0 || pvol->v_cclusFree < (cbNew - posEnd + pvol->v_cbClus - 1) / pvol->v_cbClus)
                dwError = ERROR_DISK_FULL;
        }

        do {
            if (dwError) {
                DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!ResizeStream: ran out of free clusters\n")));
                clus = UNKNOWN_CLUSTER;
                break;
            }

            dwError = NewCluster(pvol, clusEnd, &clus);
            if (dwError == ERROR_SUCCESS && clus == UNKNOWN_CLUSTER)
                dwError = ERROR_DISK_FULL;
            if (dwError != ERROR_SUCCESS) {
                DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!ResizeStream: cannot allocate new cluster (%d)\n"), dwError));
                break;          // can't allocate any more clusters
            }

            // Link clus onto the end of the stream's cluster chain.  If
            // the previous end was UNKNOWN_CLUSTER, then record clus in the
            // stream's clusFirst field;  otherwise, link clus onto clusEnd.

            if (clusEnd == UNKNOWN_CLUSTER) {
                pstm->s_clusFirst = clus;
                pstm->s_flags |= STF_DIRTY | STF_DIRTY_CLUS;
            }
            else {

                // If a PACK fails, we're having trouble writing to the FAT

                if (PACK(pvol, clusEnd, clus, NULL) != ERROR_SUCCESS) {
                    DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!ResizeStream: trouble growing new FAT chain\n")));
                    clus = UNKNOWN_CLUSTER;
                    break;
                }
            }

            // posEnd advances, and clus becomes the new clusEnd

            posEnd += pvol->v_cbClus;

            // Remember the first new cluster we allocate, and keep
            // track of the extent to which the new cluster(s) form a run.

            if (clusRunEnd == UNKNOWN_CLUSTER) {

                if (clusRun == NO_CLUSTER)
                    clusRun = clus;

                if (clusRun + cclusRun == clus) {
                    cclusRun++;
                    cbRun += pvol->v_cbClus;
                }
                else
                    clusRunEnd = clus;          // end of the RUN
            }

            if (clusEnd != UNKNOWN_CLUSTER)
                clusPrev = clusEnd;

            clusEnd = clus;

        } while (posEnd < cbNew);

        // If the stream has any new clusters at all...

        if (clusEnd != UNKNOWN_CLUSTER) {

            // If a PACK fails, we're having trouble writing to the FAT

            if (clus != UNKNOWN_CLUSTER) {
                if (PACK(pvol, clusEnd, EOF_CLUSTER, NULL) != ERROR_SUCCESS) {
                    DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!ResizeStream: trouble terminating new FAT chain\n")));
                    clus = UNKNOWN_CLUSTER;
                }
            }

            // If we couldn't allocate all the clusters we needed,
            // then (try to) resize the stream to its original size.

            if (clus == UNKNOWN_CLUSTER) {
                DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!ResizeStream: shrinking FAT chain back down...\n")));
                ResizeStream(pstm, pstm->s_run.r_end, dwResizeFlags | RESIZESTREAM_SHRINK);
                dwError = ERROR_DISK_FULL;
            }
            else {

                // It's important that we record the cluster-granular
                // size for directories, because when CreateName grows a
                // directory, it uses this size to calculate how much data
                // has to be zeroed.  For all other streams, it's important
                // to record only the exact number of bytes requested.

                if (!(pstm->s_attr & ATTR_DIRECTORY))
                    posEnd = cbNew;

                // If there's no net change in size, don't dirty the stream.

                ASSERT(pstm->s_size != posEnd); 
                if (pstm->s_size != posEnd) {
                    pstm->s_size = posEnd;
                    pstm->s_flags |= STF_DIRTY;
                }

                // We need to update the stream's RUN info;  the easiest
                // way to do this is call RewindStream, but that can be quite
                // expensive later, in terms of unnecessary calls to UnpackRun.

                pstm->s_run.r_start = pstm->s_run.r_end;
                pstm->s_run.r_end = pstm->s_run.r_end + cbRun;
                pstm->s_run.r_blk = CLUSTERTOBLOCK(pvol, clusRun);
                pstm->s_run.r_clusPrev = pstm->s_run.r_clusThis;
                if (pstm->s_run.r_clusPrev == UNKNOWN_CLUSTER)
                    pstm->s_run.r_clusPrev = NO_CLUSTER;
                pstm->s_run.r_clusThis = clusRun;
                pstm->s_run.r_clusNext = clusRunEnd;
            }
        }

        // Commit and release FAT buffers now

        if (dwResizeFlags & RESIZESTREAM_UPDATEFAT)
            WriteAndReleaseStreamBuffers(pvol->v_pstmFAT);

        UnlockFAT(pvol);

        DEBUGMSG(ZONE_STREAMS || ZONE_ERRORS && dwError,(DBGTEXT("FATFS!ResizeStream(GROW) returned %d for '%.11hs'\n"), dwError, pstm->s_achOEM));
        return dwError;
    }

    // If shrinking was not enabled, then we're done

    if (!(dwResizeFlags & RESIZESTREAM_SHRINK))
        return ERROR_SUCCESS;

    // We enter the FAT's critical section even though we're just freeing
    // clusters, because in the process, we could alter the FAT stream's
    // current buffer, and that could mess up someone else accessing the FAT.

    LockFAT(pvol);

    // If clusEnd is NO_CLUSTER, then all the file's clusters can be freed;
    // set pstm->s_clusFirst to UNKNOWN_CLUSTER, which will get propagated
    // as ZERO to the DIRENTRY when the stream is committed (CommitStream will
    // eventually do that, because we also set STF_DIRTY, below).

    if (clusEnd == NO_CLUSTER) {

        // clusEnd becomes the first cluster to free.

        
        if (pstm->s_clusFirst != UNKNOWN_CLUSTER)
            pstm->s_flags |= STF_DIRTY_CLUS;

        clusEnd = pstm->s_clusFirst;
        pstm->s_clusFirst = UNKNOWN_CLUSTER;
    }
    else
        dwError = PACK(pvol, clusEnd, EOF_CLUSTER, &clusEnd);

    // Now free all the remaining clusters in the chain.

    while (!dwError && clusEnd >= DATA_CLUSTER && !ISEOF(pvol, clusEnd))
        dwError = PACK(pvol, clusEnd, FREE_CLUSTER, &clusEnd);

    // Commit and release FAT buffers now.

    if (dwResizeFlags & RESIZESTREAM_UPDATEFAT)
        WriteAndReleaseStreamBuffers(pvol->v_pstmFAT);

    UnlockFAT(pvol);

    pstm->s_size = cbNew;
    pstm->s_flags |= STF_DIRTY;

    // We need to update the stream's RUN info, too;  the easiest
    // way to do this is call RewindStream, but since cbNew should still
    // be within the current run, we can save work by simply shrinking the run.

    ASSERT(cbNew >= pstm->s_run.r_start && cbNew <= pstm->s_run.r_end);

    // r_start and r_clusPrev are still valid, but r_end and r_clusNext
    // need to be updated, and r_blk and r_clusThis *may* need to be updated
    // if the current run was completely eliminated.  r_clusNext simply
    // becomes UNKNOWN_CLUSTER, since we just truncated the file, and r_end
    // is the number of bytes from r_start to cbNew, converted to whole clusters.

    pstm->s_run.r_clusNext = UNKNOWN_CLUSTER;
    pstm->s_run.r_end = (cbNew + pvol->v_cbClus-1) & ~(pvol->v_cbClus-1);

    // Now, if the current run is empty, we must also set r_clusThis to
    // UNKNOWN_CLUSTER (and although no one ever pays attention to r_blk when
    // r_clusThis is invalid, we will also set r_blk to INVALID_BLOCK in DEBUG).

    if (pstm->s_run.r_start == pstm->s_run.r_end) {
        pstm->s_run.r_clusThis = UNKNOWN_CLUSTER;
        DEBUGONLY(pstm->s_run.r_blk = INVALID_BLOCK);
    }

    DEBUGMSG(ZONE_STREAMS || ZONE_ERRORS && dwError,(DBGTEXT("FATFS!ResizeStream(SHRINK) returned %d for '%.11hs'\n"), dwError, pstm->s_achOEM));
    return dwError;
}


/*  CheckStreamHandles - Check for stream with open handles
 *
 *  ENTRY
 *      pvol - pointer to VOLUME
 *      psid - pointer to SID, or NULL to check all streams for open handles
 *
 *  EXIT
 *      TRUE if stream exists and has open handles, FALSE if not
 *
 *  NOTES
 *      This check excludes VOLUME-based handles, because it excludes VOLUME-
 *      based streams (ie, the FAT and root directory).
 */

BOOL CheckStreamHandles(PVOLUME pvol, PDSID psid)
{
    PDSTREAM pstm;
    BOOL fOpen = FALSE;

    EnterCriticalSection(&pvol->v_csStms);

    for (pstm = pvol->v_dlOpenStreams.pstmNext; pstm != (PDSTREAM)&pvol->v_dlOpenStreams; pstm = pstm->s_dlOpenStreams.pstmNext) {

        if (pstm->s_flags & STF_VOLUME)
            continue;

        if (psid == NULL || pstm->s_sid.sid_clusDir == psid->sid_clusDir && pstm->s_sid.sid_ordDir == psid->sid_ordDir) {
            fOpen = (pstm->s_dlOpenHandles.pfhNext != (PFHANDLE)&pstm->s_dlOpenHandles);
            if (psid || fOpen)
                break;
        }
    }
    LeaveCriticalSection(&pvol->v_csStms);
    return fOpen;
}


/*  CheckStreamSharing - Check requested mode against stream
 *
 *  ENTRY
 *      pstm - pointer to DSTREAM
 *      mode - requested mode
 *
 *  EXIT
 *      TRUE if all handles for stream permit the specified mode, FALSE if not
 */

BOOL CheckStreamSharing(PDSTREAM pstm, int mode)
{
    BYTE bRequired = 0;
    PFHANDLE pfh, pfhEnd;

    ASSERT(pstm);
    ASSERT(OWNCRITICALSECTION(&pstm->s_cs));

    if (mode & FH_MODE_READ)
        bRequired |= FH_MODE_SHARE_READ;

    if (mode & FH_MODE_WRITE)
        bRequired |= FH_MODE_SHARE_WRITE;

    pfh = pstm->s_dlOpenHandles.pfhNext;
    pfhEnd = (PFHANDLE)&pstm->s_dlOpenHandles;

    while (pfh != pfhEnd) {
        if ((pfh->fh_mode & bRequired) != bRequired)
            return FALSE;
        pfh = pfh->fh_dlOpenHandles.pfhNext;
    }
    return TRUE;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -