BOOL CDownload::OpenDownload() { if ( m_sName.IsEmpty() ) return TRUE; // Download has no name yet, postponing if ( IsFileOpen() ) return TRUE; // Already opened SetModified(); if ( ( IsTorrent() && ! ( m_oSHA1 || m_oTiger || m_oED2K || m_oMD5 ) ) ? Open( m_pTorrent ) : Open( this ) ) return TRUE; if ( m_nSize != SIZE_UNKNOWN && ! Downloads.IsSpaceAvailable( m_nSize, Downloads.dlPathIncomplete ) ) { CString strFileError; strFileError.Format( LoadString( IDS_DOWNLOAD_DISK_SPACE ), (LPCTSTR)m_sName, (LPCTSTR)Settings.SmartVolume( m_nSize ) ); SetFileError( ERROR_DISK_FULL, strFileError ); theApp.Message( MSG_ERROR, L"%s", (LPCTSTR)strFileError ); } return FALSE; }
/* * Write data in buffer to disk cache file. * * This thread is a consumer for the double buffer thread. * A conumser must wait until the buffer is not empty, retrieve its * data, and then notify the producer that the buffer is not full. */ int DiskCacheWrite( FileInfo_t *file, int fd) { CircularBuffer_t *writer; boolean_t checksum; /* use checksum enabled on file */ boolean_t multivolume; /* staging a multivolume file */ boolean_t verify; /* staging a file for data verification */ sam_ioctl_swrite_t swrite; int position; /* block position for debugging */ char *out; /* output buffer pointer */ int nbytes; int nwritten; /* FIXME comments */ int copy; boolean_t closeDiskcache; /* LINTED variable unused in function */ time_t endTime; /* LINTED variable unused in function */ int secs; /* Wait for file to stage. */ ThreadStateWait(&IoThread->io_writeReady); writer = IoThread->io_writer; copy = file->copy; memset(&swrite, 0, sizeof (sam_ioctl_swrite_t)); if (GET_FLAG(file->flags, FI_DCACHE)) { swrite.offset += file->write_off; } checksum = ifChecksum(file); verify = ifVerify(file); multivolume = ifMultiVolume(file); dataToWrite = IoThread->io_size; cancel = B_FALSE; readErrno = 0; position = 0; /* block written to disk for file */ Trace(TR_FILES, "Write disk inode: %d.%d offset: %lld len: %lld", file->id.ino, file->id.gen, swrite.offset, dataToWrite); while (DATA_TO_WRITE()) { /* * Wait until the write buffer is not empty. * Retrieve data at 'out' position. * If archive read thread found an error during a read * from media, positioning failure or tar header validation * the error flag will be set in io thead control structure. */ out = CircularIoAvail(writer, &nbytes, &readErrno); if (readErrno != 0) { SetErrno = readErrno; Trace(TR_ERR, "Error in writer buffer, slot:%d readErrno:%d", CircularIoSlot(IoThread->io_writer, out), readErrno); break; } /* If not a full block of data left to write. */ if (dataToWrite < nbytes) { nbytes = dataToWrite; } Trace(TR_DEBUG, "Write block: %d buf: %d [0x%x] offset: %lld len: %d", position, CircularIoSlot(IoThread->io_writer, out), (int)out, swrite.offset, nbytes); swrite.buf.ptr = out; swrite.nbyte = nbytes; /* Accumulate checksum value. */ if (checksum == B_TRUE) { Checksum(out, nbytes); } /* Write data block to disk cache. */ if (verify == B_FALSE) { nwritten = ioctl(fd, F_SWRITE, &swrite); if (nwritten != nbytes) { Trace(TR_ERR, "Write error: %d fd: %d nbyte: %d " "offset: %lld", errno, fd, swrite.nbyte, swrite.offset); /* * Cancel stage request, this will be picked * up in the reader and doublebuffer threads. */ Trace(TR_MISC, "Cancelled(write error) inode: %d.%d", file->id.ino, file->id.gen); SET_FLAG(IoThread->io_flags, IO_cancel); if (errno == ECANCELED) { cancel = B_TRUE; SET_FLAG(file->flags, FI_CANCEL); } else { readErrno = errno; SET_FLAG(file->flags, FI_WRITE_ERROR); } } } /* * Wait for checksum thread to complete on the data block * before allowing the double buffer thread to reuse * the 'out' buffer. */ if (checksum == B_TRUE && nbytes > 0) { ChecksumWait(); } /* * Write complete. Advance write buffer's 'out' * pointer and notify double buffer thread that the buffer * is not empty. */ CircularIoAdvanceOut(writer); file->stage_size += nbytes; swrite.offset += nbytes; dataToWrite -= nbytes; ASSERT_WAIT_FOR_DBX(dataToWrite >= 0); position++; Trace(TR_DEBUG, "Wrote %d bytes left: %lld (%d/%d)", nbytes, dataToWrite, readErrno, cancel); } Trace(TR_FILES, "Write disk complete inode: %d.%d", file->id.ino, file->id.gen); /* * If no error AND cancel request, close disk cache file. * If no error AND no more VSNs, close disk cache file. * If error OR more VSNs to stage, save disk cache information. */ /* * There are a number of scenarios under which to determine * whether to close the disk cache file or leave it open for * a future request. * * If no error AND cancel, close disk cache file. * If no error AND not multivolume, close disk cache file. * If no error AND multivolume AND stage_n, close disk cache file. * If error OR more VSNs to stage, save disk cache information. */ /* If no error AND cancel, close disk cache file. */ if (readErrno == 0 && cancel == B_TRUE) { closeDiskcache = B_TRUE; /* If no error AND not multivolume, close disk cache file. */ } else if (readErrno == 0 && multivolume == 0) { closeDiskcache = B_TRUE; /* If no error AND multivolume AND stage_n, close disk cache file. */ } else if (readErrno == 0 && multivolume != 0 && GET_FLAG(file->flags, FI_STAGE_NEVER) && file->stage_size == file->len) { closeDiskcache = B_TRUE; /* Else, an error OR more VSNs to stage, save disk cache information. */ } else { /* * If no device available or interrupted system call, * close disk cache. */ if (readErrno == ENODEV || readErrno == EINTR) { closeDiskcache = B_TRUE; } else { closeDiskcache = B_FALSE; } } /* * Checksumming may have found an error. */ if (closeDiskcache == B_TRUE && checksum == B_TRUE) { /* * Do not check for checksum error if request was * cancelled or an I/O error occurred. */ if (cancel == B_FALSE && readErrno == 0) { readErrno = ChecksumCompare(fd, &file->id); if (readErrno != 0) { closeDiskcache = B_FALSE; swrite.offset = 0; if (verify == B_TRUE) { SetErrno = 0; /* set for trace */ Trace(TR_ERR, "Unable to verify " "inode: %d.%d copy: %d errno: %d", file->id.ino, file->id.gen, copy + 1, readErrno); closeDiskcache = B_TRUE; } } else { if (verify == B_TRUE) { setVerify(file); } } } else { if (verify == B_TRUE) { SetErrno = 0; /* set for trace */ Trace(TR_ERR, "Unable to verify " "inode: %d.%d copy: %d errno: %d", file->id.ino, file->id.gen, copy + 1, errno); } } checksum = B_FALSE; } if (closeDiskcache == B_TRUE) { (void) close(fd); CLEAR_FLAG(file->flags, FI_DCACHE); NumOpenFiles--; } else { SetFileError(file, fd, swrite.offset, readErrno); if (checksum == B_TRUE && multivolume != 0) { file->csum_val = ChecksumGetVal(); } } /* Done writing staged file. */ ThreadStatePost(&IoThread->io_writeDone); return (file->error); }
BOOL CDownload::SeedTorrent() { if ( IsMoving() || IsCompleted() ) return FALSE; ASSERT( IsFileOpen() == FALSE ); if ( IsFileOpen() ) return FALSE; ASSERT( m_pTorrent.GetCount() ); augment::auto_ptr< CFragmentedFile > pFragmentedFile( new CFragmentedFile ); if ( ! pFragmentedFile.get() ) return FALSE; // Out of memory if ( ! pFragmentedFile->Open( m_pTorrent, FALSE ) ) { SetFileError( pFragmentedFile->GetFileError(), pFragmentedFile->GetFileErrorString() ); return FALSE; } AttachFile( pFragmentedFile.release() ); if ( IsSingleFileTorrent() ) { // Refill missing hashes for single-file torrent const CBTInfo::CBTFile* pBTFile = m_pTorrent.m_pFiles.GetHead(); if ( ! m_pTorrent.m_oSHA1 && pBTFile->m_oSHA1 ) m_pTorrent.m_oSHA1 = pBTFile->m_oSHA1; if ( ! m_pTorrent.m_oTiger && pBTFile->m_oTiger ) m_pTorrent.m_oTiger = pBTFile->m_oTiger; if ( ! m_pTorrent.m_oED2K && pBTFile->m_oED2K ) m_pTorrent.m_oED2K = pBTFile->m_oED2K; if ( ! m_pTorrent.m_oMD5 && pBTFile->m_oMD5 ) m_pTorrent.m_oMD5 = pBTFile->m_oMD5; // Refill missed hash for library file CQuickLock oLock( Library.m_pSection ); if ( CLibraryFile* pLibraryFile = LibraryMaps.LookupFileByPath( pBTFile->FindFile() ) ) { if ( ! pLibraryFile->m_oBTH && m_oBTH ) { Library.RemoveFile( pLibraryFile ); pLibraryFile->m_oBTH = m_oBTH; Library.AddFile( pLibraryFile ); } } } // Refill missing hashes if ( ! m_oSHA1 && m_pTorrent.m_oSHA1 ) m_oSHA1 = m_pTorrent.m_oSHA1; if ( ! m_oTiger && m_pTorrent.m_oTiger ) m_oTiger = m_pTorrent.m_oTiger; if ( ! m_oED2K && m_pTorrent.m_oED2K ) m_oED2K = m_pTorrent.m_oED2K; if ( ! m_oMD5 && m_pTorrent.m_oMD5 ) m_oMD5 = m_pTorrent.m_oMD5; GenerateTorrentDownloadID(); m_bSeeding = TRUE; m_bComplete = true; m_tCompleted = GetTickCount(); m_bVerify = TRI_TRUE; memset( m_pTorrentBlock, TRI_TRUE, m_nTorrentBlock ); m_nTorrentSuccess = m_nTorrentBlock; MakeComplete(); ResetVerification(); return TRUE; }
/* * Stage all files in the stream. */ static void copyStream() { int rval; FileInfo_t *file; int dcache; boolean_t reject; StageInit(Stream->vsn); /* Set loading flag for this stream. */ PthreadMutexLock(&Stream->mutex); SET_FLAG(Stream->flags, SR_LOADING); PthreadMutexUnlock(&Stream->mutex); rval = LoadVolume(); /* Reject if mount/open failed. */ if (rval != 0) { PthreadMutexLock(&Stream->mutex); removeDcachedFile(Stream, rval); if (rval == ENODEV) { Stream->context = 0; PthreadMutexUnlock(&Stream->mutex); rejectRequest(0, B_TRUE); SET_FLAG(Instance->ci_flags, CI_shutdown); } else { PthreadMutexUnlock(&Stream->mutex); SendCustMsg(HERE, 19017, Stream->vsn); rejectRequest(rval, B_TRUE); } StageEnd(); return; } /* VSN load has completed. */ checkBuffers(Stream->vsn); PthreadMutexLock(&Stream->mutex); CLEAR_FLAG(Stream->flags, SR_LOADING); Instance->ci_seqnum = Stream->seqnum; reject = B_FALSE; /* * Copy all files in stage stream request. The files have * been ordered to eliminate backward media positioning. */ while (STREAM_IS_VALID() && reject == B_FALSE) { /* Stop staging if parent died. */ if (getppid() == 1) { SetErrno = 0; /* set for trace */ Trace(TR_ERR, "Detected stager daemon exit"); Stream->first = EOS; SET_FLAG(Instance->ci_flags, CI_shutdown); break; } file = GetFile(Stream->first); PthreadMutexLock(&file->mutex); PthreadMutexUnlock(&Stream->mutex); /* * If the first vsn, clear bytes read count. * And if multivolume and stage -n set, initialize * residual length. */ if (file->vsn_cnt == 0) { file->read = 0; if (GET_FLAG(file->flags, FI_MULTIVOL) && GET_FLAG(file->flags, FI_STAGE_NEVER)) { file->residlen = file->len; } else { file->residlen = 0; } } SET_FLAG(file->flags, FI_ACTIVE); PthreadMutexUnlock(&file->mutex); /* Set file in io control structure for archive read thread. */ setIoThread(file); /* Log stage start. */ file->eq = IoThread->io_drive; LogIt(LOG_STAGE_START, file); /* * Check if last request was canceled. If the last request * was canceled, invalidate i/o buffers and clear cancel * flag in the control structure. */ if (GET_FLAG(IoThread->io_flags, IO_cancel)) { ResetBuffers(); CLEAR_FLAG(IoThread->io_flags, IO_cancel); } /* * Next archive file. If disk archive, we may be opening * a disk archive tarball. */ if ((rval = NextArchiveFile()) == 0) { /* Prepare filesystem to receive staged file. */ dcache = DiskCacheOpen(file); } else { /* Unable to open disk archive. Error request. */ Trace(TR_ERR, "Unable to open disk archive " "copy: %d inode: %d.%d errno: %d", file->copy + 1, file->id.ino, file->id.gen, errno); dcache = -1; file->error = errno; SendErrorResponse(file); } if (dcache >= 0 && rval == 0) { /* * Notify reader thread that next file in stream * is ready to be staged. */ ThreadStatePost(&IoThread->io_readReady); /* Write data to disk cache. */ rval = DiskCacheWrite(file, dcache); if (rval != 0) { SendErrorResponse(file); /* Check if number of stream errors exceeded. */ reject = ifMaxStreamErrors(file); } ThreadStateWait(&IoThread->io_readDone); } else if (rval != 0 && dcache >= 0) { /* Setup for error handling. */ SetFileError(file, dcache, 0, EIO); SendErrorResponse(file); } EndArchiveFile(); /* Remove file from stream before marking it as done. */ PthreadMutexLock(&Stream->mutex); Stream->first = file->next; /* Device not available. */ if (file->error == ENODEV) { SetErrno = 0; /* set for trace */ Trace(TR_ERR, "No device available"); reject = B_TRUE; if (NumOpenFiles <= 0 && Instance->ci_first == NULL) { SET_FLAG(Instance->ci_flags, CI_shutdown); Instance->ci_busy = B_TRUE; } } /* Mark file staging as done. */ SetStageDone(file); Stream->count--; if (Stream->first == EOS) { Stream->last = EOS; } } /* Reject rest of stages in this stream. */ if (reject == B_TRUE) { if (Stream->first > EOS) { removeDcachedFile(Stream, ENODEV); } PthreadMutexUnlock(&Stream->mutex); rejectRequest(ENODEV, B_FALSE); PthreadMutexLock(&Stream->mutex); } /* Remove copy request, no one is waiting on it. */ RemoveMapFile(copyRequestPath, Request, sizeof (CopyRequestInfo_t)); Request = NULL; /* Ready to unload. Mark stream as done. */ SET_FLAG(Stream->flags, SR_DONE); PthreadMutexUnlock(&Stream->mutex); UnloadVolume(); /* * Unmap pages of memory. Stream's memory * mapped file is removed in parent. */ UnMapFile(Stream, sizeof (StreamInfo_t)); Stream = NULL; StageEnd(); }