/** * returns 0 on success, or an errno value on failure. * errno values include ENOENT if the parent folder doesn't exist, * plus the errno values set by tr_mkdirp() and open(). */ static int TrOpenFile( int i, const char * folder, const char * torrentFile, int doWrite, int doPreallocate, uint64_t desiredFileSize ) { struct tr_openfile * file = &gFd->open[i]; int flags; char * filename; struct stat sb; int alreadyExisted; /* confirm the parent folder exists */ if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) ) return ENOENT; /* create subfolders, if any */ filename = tr_buildPath( folder, torrentFile, NULL ); if( doWrite ) { char * tmp = tr_dirname( filename ); const int err = tr_mkdirp( tmp, 0777 ) ? errno : 0; tr_free( tmp ); if( err ) { tr_free( filename ); return err; } } alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode ); if( doWrite && !alreadyExisted && doPreallocate ) if( preallocateFile( filename, desiredFileSize ) ) tr_inf( _( "Preallocated file \"%s\"" ), filename ); /* open the file */ flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY; #ifdef O_LARGEFILE flags |= O_LARGEFILE; #endif #ifdef WIN32 flags |= O_BINARY; #endif file->fd = open( filename, flags, 0666 ); if( file->fd == -1 ) { const int err = errno; tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) ); tr_free( filename ); return err; } tr_free( filename ); return 0; }
void libttest_zero_torrent_populate (tr_torrent * tor, bool complete) { tr_file_index_t i; for (i=0; i<tor->info.fileCount; ++i) { int err; uint64_t j; FILE * fp; char * path; char * dirname; const tr_file * file = &tor->info.files[i]; struct stat sb; if (!complete && (i==0)) path = tr_strdup_printf ("%s%c%s.part", tor->currentDir, TR_PATH_DELIMITER, file->name); else path = tr_strdup_printf ("%s%c%s", tor->currentDir, TR_PATH_DELIMITER, file->name); dirname = tr_dirname (path); tr_mkdirp (dirname, 0700); fp = fopen (path, "wb+"); for (j=0; j<file->length; ++j) fputc (((!complete) && (i==0) && (j<tor->info.pieceSize)) ? '\1' : '\0', fp); fclose (fp); tr_free (dirname); tr_free (path); path = tr_torrentFindFile (tor, i); assert (path != NULL); err = errno; errno = 0; stat (path, &sb); assert (errno == 0); errno = err; tr_free (path); } sync (); libttest_blockingTorrentVerify (tor); if (complete) assert (tr_torrentStat(tor)->leftUntilDone == 0); else assert (tr_torrentStat(tor)->leftUntilDone == tor->info.pieceSize); }
static void create_text_file (const char * path, const char * contents) { FILE * fp; char * dir; dir = tr_dirname (path); tr_mkdirp (dir, 0700); tr_free (dir); tr_remove (path); fp = fopen (path, "w+"); fprintf (fp, "%s", contents); fclose (fp); sync (); }
/** * returns 0 on success, or an errno value on failure. * errno values include ENOENT if the parent folder doesn't exist, * plus the errno values set by tr_mkdirp () and open (). */ static int cached_file_open (struct tr_cached_file * o, const char * filename, bool writable, tr_preallocation_mode allocation, uint64_t file_size) { int flags; struct stat sb; bool alreadyExisted; /* create subfolders, if any */ if (writable) { char * dir = tr_dirname (filename); const int err = tr_mkdirp (dir, 0777) ? errno : 0; if (err) { tr_err (_("Couldn't create \"%1$s\": %2$s"), dir, tr_strerror (err)); tr_free (dir); return err; } tr_free (dir); } alreadyExisted = !stat (filename, &sb) && S_ISREG (sb.st_mode); if (writable && !alreadyExisted && (allocation == TR_PREALLOCATE_FULL)) if (preallocate_file_full (filename, file_size)) tr_dbg ("Preallocated file \"%s\"", filename); /* open the file */ flags = writable ? (O_RDWR | O_CREAT) : O_RDONLY; flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL; o->fd = open (filename, flags, 0666); if (o->fd == -1) { const int err = errno; tr_err (_("Couldn't open \"%1$s\": %2$s"), filename, tr_strerror (err)); return err; } /* If the file already exists and it's too large, truncate it. * This is a fringe case that happens if a torrent's been updated * and one of the updated torrent's files is smaller. * http://trac.transmissionbt.com/ticket/2228 * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249 */ if (alreadyExisted && (file_size < (uint64_t)sb.st_size)) { if (ftruncate (o->fd, file_size) == -1) { const int err = errno; tr_err (_("Couldn't truncate \"%1$s\": %2$s"), filename, tr_strerror (err)); return err; } } if (writable && !alreadyExisted && (allocation == TR_PREALLOCATE_SPARSE)) preallocate_file_sparse (o->fd, file_size); /* Many (most?) clients request blocks in ascending order, * so increase the readahead buffer. * Also, disable OS-level caching because "inactive memory" angers users. */ tr_set_file_for_single_pass (o->fd); return 0; }
/** * returns 0 on success, or an errno value on failure. * errno values include ENOENT if the parent folder doesn't exist, * plus the errno values set by tr_mkdirp() and open(). */ static int TrOpenFile( tr_session * session, int i, const char * filename, tr_bool doWrite, tr_preallocation_mode preallocationMode, uint64_t desiredFileSize ) { int flags; struct stat sb; tr_bool alreadyExisted; struct tr_openfile * file; assert( tr_isSession( session ) ); assert( session->fdInfo != NULL ); file = &session->fdInfo->openFiles[i]; /* create subfolders, if any */ if( doWrite ) { char * dir = tr_dirname( filename ); const int err = tr_mkdirp( dir, 0777 ) ? errno : 0; if( err ) { tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir, tr_strerror( err ) ); tr_free( dir ); return err; } tr_free( dir ); } alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode ); if( doWrite && !alreadyExisted && ( preallocationMode == TR_PREALLOCATE_FULL ) ) if( preallocateFileFull( filename, desiredFileSize ) ) tr_dbg( _( "Preallocated file \"%s\"" ), filename ); /* open the file */ flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY; #ifdef O_SEQUENTIAL flags |= O_SEQUENTIAL; #endif #ifdef O_LARGEFILE flags |= O_LARGEFILE; #endif #ifdef WIN32 flags |= O_BINARY; #endif file->fd = open( filename, flags, 0666 ); if( file->fd == -1 ) { const int err = errno; tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) ); return err; } /* If the file already exists and it's too large, truncate it. * This is a fringe case that happens if a torrent's been updated * and one of the updated torrent's files is smaller. * http://trac.transmissionbt.com/ticket/2228 * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249 */ if( alreadyExisted && ( desiredFileSize < (uint64_t)sb.st_size ) ) ftruncate( file->fd, desiredFileSize ); if( doWrite && !alreadyExisted && ( preallocationMode == TR_PREALLOCATE_SPARSE ) ) preallocateFileSparse( file->fd, desiredFileSize ); #ifdef HAVE_POSIX_FADVISE /* this doubles the OS level readahead buffer, which in practice * turns out to be a good thing, because many (most?) clients request * chunks of blocks in order. * It's okay for this to fail silently, so don't let it affect errno */ { const int err = errno; posix_fadvise( file->fd, 0, 0, POSIX_FADV_SEQUENTIAL ); errno = err; } #endif #if defined( SYS_DARWIN ) /** * 1. Enable readahead for reasons described above w/POSIX_FADV_SEQUENTIAL. * * 2. Disable OS-level caching due to user reports of adverse effects of * excessive inactive memory. However this is experimental because * previous attempts at this have *also* had adverse effects (see r8198) * * It's okay for this to fail silently, so don't let it affect errno */ { const int err = errno; fcntl( file->fd, F_NOCACHE, 1 ); fcntl( file->fd, F_RDAHEAD, 1 ); errno = err; } #endif return 0; }