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

📄 stream.c

📁 Windows操作系统中文件系统过滤驱动和设备驱动之间的相似
💻 C
📖 第 1 页 / 共 4 页
字号:

            __try {
                memcpy(pvData, pbStart, cb);
                (PBYTE)pvData += cb;
                if (plenRead)
                    *plenRead += cb;
            }
            __except (EXCEPTION_EXECUTE_HANDLER) {
                dwError = ERROR_INVALID_PARAMETER;
            }

            ReleaseStreamBuffer(pstm, FALSE);
            if (dwError)
                break;
        }

#ifdef DEBUG
        if (ZONE_READVERIFY && ZONE_LOGIO) {
            DWORD i, j;
            for (i=0; i<cb; i++) {
                DEBUGMSG(TRUE,(DBGTEXT("%02x "), pbStart[i]));
                j = i+1;
                if (j == cb || j % 16 == 0) {
                    j = j % 16;
                    if (j == 0)
                        j = i - 15;
                    else
                        j = i - j + 1;
                    while (j <= i) {
                        DEBUGMSG(TRUE,(DBGTEXT("%c"), pbStart[j]<' ' || pbStart[j]>=0x7F? '.' : pbStart[j]));
                        j++;
                    }
                    DEBUGMSG(TRUE,(DBGTEXT("\r\n")));
                }
            }
        }
#endif
        len -= cb;
        pos += cb;
    }
    return dwError;
}


/*  WriteStreamData
 *
 *  Whereas ReadStream returns a pointer (to a buffer) and a length,
 *  and then the caller reads whatever data it wants up to that length
 *  (or modifies data up to that length and calls ModifyStreamBuffer), this
 *  function takes a pointer to data and its length, the position to write
 *  it, and simply writes it.
 *
 *  Thus, unlike ReadStream, where the caller can never get more than a
 *  buffer's worth of data back at a time and probably needs to put a loop
 *  around its ReadStream call, WriteStreamData can write any amount of data
 *  with one call (no external loop).
 *
 *  The same effect could be achieved with a ReadStream/ModifyStreamBuffer
 *  loop, but there are two important differences:  WriteStreamData can avoid
 *  the overhead of reading data that it's simply going to overwrite anyway,
 *  and it takes care of extending the stream if needed.
 *
 *  So, I've changed ReadStreamBuffer to accept an "advisory" lenMod parm,
 *  allowing us to advise it how many bytes we plan to modify.  This
 *  information percolates down to FindBuffer, allowing FindBuffer to avoid
 *  filling a buffer with disk data if the entire buffer contents are going
 *  to be modified anyway. -JTP
 *
 *  ENTRY
 *      pstm - pointer to DSTREAM
 *      pos - position within DSTREAM to write to
 *      pvData - pointer to data to write (or NULL to write zeros)
 *      len - length of data to write
 *      plenWritten - pointer to DWORD for length written (optional)
 *      fCommit - TRUE to commit buffers as they are written
 *
 *  EXIT
 *      0 if successful, error code if not (eg, disk full, or error reading
 *      the disk to fill the non-modified portion of a buffer)
 *
 *  NOTES
 *      If fCommit is FALSE, note that the last buffer modified will still
 *      be held (ie, the stream's current buffer).
 */

#define MAX_UNPACK_RETRIES 20

DWORD WriteStreamData(PDSTREAM pstm, DWORD pos, PCVOID pvData, DWORD len, PDWORD plenWritten, BOOL fCommit)
{
    DWORD dwError, dwUnpackError;
    PBYTE pbStart, pbEnd;
    DWORD dwRetries;
    BOOL fSizeChanged = FALSE;
    RETAILMSG(1,(L"[FSD] WriteStreamData\r\n"));
    dwUnpackError = ERROR_SUCCESS;
    dwRetries = 0;
    ASSERT(OWNCRITICALSECTION(&pstm->s_cs));

    if (pstm->s_pvol->v_flags & VOLF_READONLY)
        return ERROR_WRITE_PROTECT;

    dwError = ERROR_SUCCESS;

#ifdef TFAT
     // If CloneStream fails (most likely because no room to copy), then fail the write
    if (pstm->s_pvol->v_fTfat && (pstm->s_flags & STF_TRANSDATA)) {
        LockFAT (pstm->s_pvol);        
        dwError = CloneStream(pstm, pos, len );
        if (dwError != ERROR_SUCCESS)
            goto exit;
    }
#endif

    // A write of 0 bytes always succeeds, and is not supposed to do
    // anything other than cause the "last write" timestamp to eventually
    // get updated.

    if (len == 0) {
        pstm->s_flags |= STF_DIRTY_DATA;
        pstm->s_flags &= ~STF_WRITE_TIME;    //set write time
        goto exit;
    }


    do {

        while (len) {
            
            DWORD cb, blk, cblk;

            // Locate the data run which contains the bytes requested.
            if (dwError = SeekStream(pstm, pos)) {
                break;
            }
               
            if (pvData && LockBlocks(pstm, pos, len, &blk, &cblk, TRUE)) {
            RETAILMSG(1,(L"[FSD] WriteVolume\r\n"));
                __try {                        
                    dwError = WriteVolume(pstm->s_pvol, blk, cblk, (PVOID)pvData, pstm->s_flags & STF_WRITETHRU);
                    if (!dwError) {
                        cb = cblk << pstm->s_pvol->v_log2cbBlk;
                        (PBYTE)pvData += cb;
                        if (plenWritten)
                            *plenWritten += cb;
                    }
                    else
                        DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!WriteStreamData: WriteVolume(block %d, %d blocks) failed (%d)\r\n"), blk, cblk, dwError));
                }
                __except (EXCEPTION_EXECUTE_HANDLER) {
                    dwError = ERROR_INVALID_PARAMETER;
                }

                UnlockBlocks(pstm, blk, cblk, TRUE);
                if (dwError)
                    goto exit;
            }
            else {

                dwError = ReadStreamBuffer(pstm,
                                           pos,
                                           pos+len < pstm->s_size? len : pstm->s_runList.r_EndPosition - pos,
                                           &pbStart,
                                           &pbEnd);

                if (dwError) {
                    DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!WriteStreamData: ReadStreamBuffer(pos %d) failed (%d)\r\n"), pos, dwError));
                    goto exit;
                }

                cb = pbEnd - pbStart;
                if (cb > len)
                    cb = len;
                RETAILMSG(1,(L"[FSD] ModifyStreamBuffer\r\n"));
                dwError = ModifyStreamBuffer(pstm, pbStart, cb);
                if (dwError)
                    goto exit;

                if (pvData) {
                    __try {
                        memcpy(pbStart, pvData, cb);
                        (PBYTE)pvData += cb;
                        if (plenWritten)
                            *plenWritten += cb;
                    }
                    __except (EXCEPTION_EXECUTE_HANDLER) {
                        dwError = ERROR_INVALID_PARAMETER;
                        goto exit;
                    }
                }
                else
                    memset(pbStart, 0, cb);
            }

            len -= cb;
            pos += cb;

            // Once we get this far at least once through the loop,
            // we need to make sure the stream data is flagged as dirty.

            pstm->s_flags |= STF_DIRTY;
            pstm->s_flags &= ~STF_WRITE_TIME;

            // Furthermore, if we just wrote beyond the "official"
            // size of the stream, then update the "official" size

            if (pos > pstm->s_size) {
                pstm->s_size = pos;
                fSizeChanged = TRUE;
            }

            // This is where all successful WriteStreamData calls
            // end up (except when len is ZERO to begin with, of course).

            if (len == 0)
                goto exit;
        }

        // We've run out of cluster runs, and we still have len bytes
        // to write at pos.  Resize the stream to encompass that boundary
        // and try to continue.
        
    } while ((dwError = ResizeStream(pstm, pos + len, RESIZESTREAM_SHRINK|RESIZESTREAM_UPDATEFAT)) == ERROR_SUCCESS);

    DEBUGMSG(ZONE_ERRORS,(DBGTEXT("FATFS!WriteStreamData failed (%d)\r\n"), dwError));

  exit:
    if (fCommit) {

        DWORD dw;

        if (pstm->s_flags & STF_DIRTY_CLUS) {
            dw = CommitStream(pstm, FALSE);
            if (!dwError)
                dwError = dw;
        }
#ifdef TFAT
        else if (pstm->s_pvol->v_fTfat && (pstm->s_flags & STF_TRANSDATA) && !(pstm->s_attr & ATTR_DIRECTORY) && fSizeChanged) {
            // Update the directory entry with the new size if we changed the file size
            // without calling ResizeStream (didn't need a new cluster).
            UpdateDirEntryCluster(pstm);
        }
#endif

        dw = ReleaseStreamBuffer(pstm, FALSE);
        if (!dwError)
            dwError = dw;
    }

#ifdef TFAT
    if (pstm->s_pvol->v_fTfat && (pstm->s_flags & STF_TRANSDATA)) {
        UnlockFAT (pstm->s_pvol);
    }
#endif

    return dwError;
}


/*  ResizeStream - truncate or extend a stream
 *
 *  ENTRY
 *      pstm - pointer to DSTREAM
 *      cbNew - new size of stream
 *      dwResizeFlags - zero or more RESIZESTREAM flags;  RESIZESTREAM_SHRINK
 *      forces shrinking (instead of simply insuring that the stream is at least
 *      as large as cbNew), and RESIZESTREAM_UPDATEFAT requests that changes to 
 *      the FAT be written.
 *
 *  EXIT
 *      ERROR_SUCCESS (0) if successful, non-zero if not
 *
 *      Truncating streams should only fail due to "hard" error conditions
 *      (eg, write-protected media), whereas extending a stream can also fail
 *      due to "soft" error conditions (eg, disk is full, or non-cluster-mapped
 *      stream -- like the root directory -- is full).
 */

DWORD ResizeStream (PDSTREAM pstm, DWORD cbNew, DWORD dwResizeFlags)
{
    PVOLUME pvol;
    DWORD dwError = ERROR_SUCCESS;
    DWORD posEnd, clus, clusEnd, clusPrev;
    DWORD oldSize;
    BOOL bFirstClusChanged = FALSE;

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

    DEBUGMSG(ZONE_STREAMS,(DBGTEXT("FATFS!ResizeStream: changing size from 0x%x to 0x%x for '%.11hs'\r\n"), pstm->s_size, cbNew, pstm->s_achOEM));

    // Early exit if stream size already matches desired size.

    if (pstm->s_size == cbNew)
        return ERROR_SUCCESS;

    oldSize = pstm->s_size;

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

    pvol = pstm->s_pvol;

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

    // Subtract 1 from the position, because in the case of a cluster boundary,
    // we want the cluster preceding the position.
    if (cbNew) {
        dwError = PositionStream(pstm, cbNew - 1, &clusEnd);
    }
    else {
        clusEnd = NO_CLUSTER;
    }
    
    if (dwError == ERROR_HANDLE_EOF) {
        // This isn't an error in this case, just extending the stream
        dwError = ERROR_SUCCESS;
    }
    if (dwError) {
        ASSERT (0);  // Something wrong with cluster chain or FAT
        return dwError;
    }

    // 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) {

        posEnd = pstm->s_runList.r_EndPosition;

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

        ASSERT(posEnd < cbNew);

        // Get the ending cluster of the current stream.

        if (posEnd > 0) {
            dwError = GetClusterAtPosition (&pstm->s_runList, posEnd - 1, &clusEnd);
            if (dwError != ERROR_SUCCESS) {
                return dwError;
            }
        }
        
        LockFAT(pvol);

        clusPrev = 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\r\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)\r\n"), dwError));
                break;          // can't allocate any more clusters
            }

            DEBUGMSG(ZONE_STREAMS,(DBGTEXT("FATFS!ResizeStream: new cluster for %.11hs returned %d'\r\n"), pstm->s_achOEM, clus));        

            // 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;
                bFirstClusChanged = TRUE;
            }
            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\r\n")));
                    clus = UNKNOWN_CLUSTER;
                    break;
                }
            }

            dwError = AppendRunList(&pstm->s_runList, posEnd, clus);
            if (dwError != ERROR_SUCCESS) {

⌨️ 快捷键说明

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