1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
// fgfshelp.cc
#include "fgfshelp.h"
#ifndef _FGDLIST_H
#include "fgdlist.h"
#endif
#ifndef _FGSTRING_H
#include "fgstring.h"
#endif
#ifndef _FGEXC_H
#include "fgexc.h"
#endif
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/file.h>
#include <assert.h>
#include <string.h>
FGDirListing
FGFileSystemHelper::GetDirListing(const FGString& dir)
{
FGDirListing ret(dir);
DIR* pDir = opendir(dir);
if (pDir == NULL) {
// Oh dear
throw FGException(FGException::kLocalNoSuchDir, dir);
}
struct dirent* pDirEnt;
while ((pDirEnt = readdir(pDir)) != NULL) {
// Only report the file if it's not partially downloaded
// (this is indicated by permissions ---------T)
struct stat statBuf;
FGString fname(dir);
fname += '/';
fname += pDirEnt->d_name;
if (stat(fname, &statBuf) != -1) {
if (statBuf.st_mode != (S_ISVTX | S_IWUSR)) {
FGFileInfo fileInfo(pDirEnt->d_name, statBuf.st_size,
S_ISDIR(statBuf.st_mode),
S_ISREG(statBuf.st_mode));
ret.push_back(fileInfo);
}
}
}
closedir(pDir);
return ret;
}
void
FGFileSystemHelper::TransferRemoteToLocal(int fromFD, int toFD,
int bytes)
{
// Preconditions
assert(fromFD >= 0);
assert(toFD >= 0);
// Transfer buffer
char buf[msTransferBufSize];
// Accounting
int bytesWritten = 0;
bool hitEOF = false;
// Monitoring
fd_set remoteFDset;
FD_ZERO(&remoteFDset);
FD_SET(fromFD, &remoteFDset);
try {
while ((bytes == -1 || bytesWritten < bytes) && !hitEOF) {
// Timeout on remote read
// For now we will use 5 minutes
struct timeval tv;
tv.tv_usec = 0;
tv.tv_sec = 60 * 5;
int selectRet = select(fromFD+1, &remoteFDset, NULL, NULL, &tv);
// Did we timeout
if (selectRet == 0) {
// How irritating
throw FGException(FGException::kDownloadTimeout);
}
int numRead = read(fromFD, buf, sizeof(buf));
if (!numRead) {
hitEOF = true;
// Throw exception if expecting specific number of bytes
if (bytes != -1 && bytesWritten < bytes) {
throw FGException(FGException::kFileTooShort);
}
} else if (numRead == -1) {
throw FGException(FGException::kNetworkReadError, strerror(errno));
} else {
int writeRet = write(toFD, buf, numRead);
if (writeRet == -1) {
throw FGException(FGException::kWriteFailed, strerror(errno));
}
bytesWritten += numRead;
}
} // end: while (want more bytes)
}
catch (FGException& e) {
close(toFD);
throw e;
}
// If we completed the transfer fully, update file permissions to
// reflect this
if (bytes == -1 || bytes == bytesWritten) {
fchmod(toFD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
}
// We are no longer writing to the file - so lose the lock
flock(toFD, LOCK_UN);
// And close it
close(toFD);
}
int
FGFileSystemHelper::CreateLocalFile(const FGString& file)
{
// Create with permissions "1400" == "---w-----T"
// This flags file as "in transit"
int theFD = open(file, O_CREAT | O_WRONLY,
S_ISVTX | S_IWUSR);
if (theFD == -1) {
FGString details(file);
details += ": ";
details += strerror(errno);
throw FGException(FGException::kFileCreateFailed, details);
}
// Before truncating, lock the file - in case another thread / process
// is currently downloading the file
int lockRet = flock(theFD, LOCK_EX | LOCK_NB);
if (lockRet == -1) {
close(theFD);
throw FGException(FGException::kConcurrentGrabs);
}
int ret = ftruncate(theFD, 0);
(void) ret;
return theFD;
}
void
FGFileSystemHelper::DeleteFile(const FGString& file)
{
int delRet = unlink(file);
if (delRet == -1) {
switch (errno) {
case EACCES:
case EPERM:
case EROFS:
throw FGException(FGException::kLocalPermissionDenied);
case ENOENT:
throw FGException(FGException::kLocalNoSuchFile);
default:
throw FGException(FGException::kUnlinkFailed);
}
}
}
|