ewmscp  ..
outputHandlerDcap.cpp
Go to the documentation of this file.
1 #include "outputHandlerDcap.h"
2 
3 #include "ewmscp.h"
4 #include "block.h"
5 #include "copyRequest.h"
6 #include "errMsgQueue.h"
7 #include <libgen.h>
8 #include <algorithm>
9 
10 
11 namespace outputHandler {
12  decltype(dcap::factory) dcap::factory("dcap");
13  std::unique_ptr<base::writer> dcap::newWriter(const std::string& path,
14  bool mightAppend,
15  size_t sourceSize,
16  size_t readBlockSize,
18  bool noWrite,
19  std::unique_ptr<ioHandle::attrDataType>,
20  std::unique_ptr<acl::list> aclData) {
21  return std::unique_ptr<base::writer>(new writerDcap(path, mightAppend,
22  sourceSize, readBlockSize,
23  state, noWrite,
24  std::move(aclData)));
25  }
26 
30  "--", "dcap output handler",
31  "setting setAttributesAfterClose automatically");
33  }
34  }
35 
36  void dcap::ensureParentDirs(const std::string& path,
37  const std::string& srcPath,
38  inputHandler::base* InputHandler) {
39  std::vector<std::remove_reference<decltype(path)>::type::value_type> disposable_buffer(path.c_str(), path.c_str() + path.size() + 1);
40  std::string dir(dirname(disposable_buffer.data()));
41  struct dcapStat statbuf;
42  int statResult;
43  {
44  dCapLock extraOptionLock;
45  statResult = dc_stat(fixPathUrl(dir).c_str(), &statbuf);
46  }
47  if (statResult) {
48  if (errno == ENOENT) {
49  auto srcDir = srcPath.substr(0, srcPath.find_last_of('/'));
50  while (srcDir.back() == '/') {
51  srcDir.pop_back();
52  }
53  ensureParentDirs(dir, srcDir, InputHandler);
54  dCapLock extraOptionLock;
55  if (preserve.acls) {
56  auto aclData = InputHandler->getAclData(srcDir);
57  if (aclData) {
58  setAclData(*aclData, true);
59  }
60  }
61  auto result = dc_mkdir(fixPathUrl(dir).c_str(), 0777u);
62 
63  if (result != 0 && errno == EEXIST) {
64  return; // we lost a race with another copy process...
65  }
66 
67  throwcall::dcap::good0(result, "can't create directory ", dir);
68 
69  if (gid != -1 || uid != -1) {
70  throwcall::dcap::good0(dc_chown(fixPathUrl(dir).c_str(), uid, gid), "can't set owner/group (", uid, ",", gid, ") on ", dir);
71  }
72  }
73  } else if (! S_ISDIR(statbuf.st_mode)) {
75  dir, "ensure parents",
76  "is not a directory (st_mode is ", statbuf.st_mode, ") but should be");
77  }
78  }
79 
80 
81  void dcap::remove(const std::string& path, copyRequest::stateType& state) {
82  dCapLock extraOptionLock;
83  struct dcapStat dsttat;
84  {
85  auto retval = dc_lstat(fixPathUrl(path).c_str(), &dsttat);
86 
87  if (retval && errno == ENOENT) {
89  return;
90  }
91  }
92  if (S_ISDIR(dsttat.st_mode)) {
93  auto retval = dc_rmdir(fixPathUrl(path).c_str());
94  if (retval) {
95  switch (errno) {
96  case ENOENT:
98  return;
99  case ENOTEMPTY:
100  case EEXIST: // POSIX.1 allows this also
102  path, "remove", "directory not empty but should be removed");
104  return;
105  }
106  throwcall::dcap::good0(retval, "can't reemove directory ", path);
107  } else {
109  return;
110  }
111  } else {
112  auto retval = dc_unlink(fixPathUrl(path).c_str());
113 
114  if (retval && errno == ENOENT) {
116  } else {
117  throwcall::dcap::good0(retval, "can't unlink ", path);
118  }
120  }
121  }
122 
123 
124  bool dcap::renameSimple(const std::string& fromPath,
125  const std::string& toPath) {
126  dCapLock extraOptionLock;
127  auto retval = dc_rename(fromPath.c_str(), toPath.c_str());
128  //find out proper errno
129  // if (retval && (errno == ENOENT || errno == ENOTDIR)) {
130  // return false;
131  //}
132  throwcall::dcap::good0(retval, "can't rename '", fromPath, "' to '", toPath, "'");
133  return true;
134  }
135 
136  base::renameRetvalType dcap::rename(const std::string& fromPath,
137  const std::unique_ptr<const genericStat>& readInitialStat,
138  const std::string& toPath,
139  copyRequest::stateType& state) {
140  dCapLock extraOptionLock;
141  struct dcapStat fromPathStatBuf;
142  auto statRetVal = dc_stat(fixPathUrl(fromPath).c_str(), &fromPathStatBuf);
143  if (statRetVal && (errno == ENOENT || errno == ENOTDIR)) {
144  if (readInitialStat && readInitialStat->isDir()) { // no need to act, dirs are created on the fly
148  }
150  } else if (statRetVal) { // curious error
151  throwcall::dcap::good0(statRetVal, "can't stat '", fromPath, "'");
152  return base::renameRetvalType::cantHappen; // never reached
153  }
154  genericStat fromPathStat(fromPathStatBuf, std::chrono::seconds(1));
155  if (! readInitialStat || (
156  ! readInitialStat->isDir() &&
157  (fromPathStat.size != readInitialStat->size
158  || !fromPathStat.isSameMtimeAs(*readInitialStat)))) {
159  // the copy on fromPath is not up to date, create a new copy
161  fromPath, "move",
162  "file (", toPath, ") changed unexpectedly, doing fresh copy");
164  } else { // try to move the existing copy
165  auto retval = dc_rename(fixPathUrl(fromPath).c_str(), fixPathUrl(toPath).c_str());
166 
167  if (retval && (errno == ENOENT || errno == ENOTDIR)) {
168  if (readInitialStat->isDir()) { // no need to act, dirs are created on the fly
171  }
173  fromPath, "move",
174  "vanished unexpectedly, doing fresh copy");
175  // file was probably not copied yet, try to copy it from the move target
177  } else {
178  throwcall::dcap::good0(retval, "can't rename '", fromPath, "' to '", toPath, "'");
181  }
182  }
184  }
185 
186 
187 
188  void dcap::setAclData(const acl::list& aclData, bool isDir) {
189  if (aclData.entries.empty()) {
190  return;
191  }
192  bool needSpace = false;
193  std::string result(" -acl=\"");
194  for (const auto& entry : aclData.entries) {
195  if (needSpace) {
196  result += " ";
197  }
198  needSpace = true;
199  result += entry.isAllowedType() ? "A:" : "D:";
200  result += entry.isFlagSet(acl::list::entryType::flagsType::identifier_group) &&
202  ? "g:" : ":";
203  if (entry.isFlagSet(acl::list::entryType::flagsType::special_who)) {
204  switch (entry.e_id) {
205  case acl::list::entryType::specialIdType::owner_special_id:
206  result += "OWNER@:";
207  break;
208  case acl::list::entryType::specialIdType::group_special_id:
209  result += "GROUP@:";
210  break;
211  case acl::list::entryType::specialIdType::everyone_special_id:
212  result += "EVERYONE@:";
213  break;
214  default:
215  throw std::runtime_error("illegal special id");
216  }
217  } else {
218  result += std::to_string(entry.e_id);
219  result += ":";
220  }
221  for (const auto& item : acl::list::entryType::aclMaskBits) {
222  auto mask = entry._mask;
223  if (isDir) {
225  }
226  if (mask & item.first) {
227  result += item.second;
228  }
229  }
230  }
231  result += "\"";
232  dc_setExtraOption(const_cast<char*>(result.c_str())); // ugly const cast due to bad dCap interface
233 
234  };
235 
236 
237 
238 
239 
240  decltype(dcap::writerDcap::unsafeWrite) dcap::writerDcap::unsafeWrite('\0', "dCapUnsafeWrite",
241  "enable unsafe write mode, i.e. check only at close()");
242  decltype(dcap::writerDcap::noChecksum) dcap::writerDcap::noChecksum('\0', "dCapNoChecksum",
243  "disable dcap-internal adler32 checksum");
244 
245 
246  dcap::writerDcap::writerDcap(const std::string& aPath,
247  bool /* mightAppend */,
248  size_t sourceSize,
249  size_t /*readBlockSize*/,
250  copyRequest::stateType &state,
251  bool noWrite,
252  std::unique_ptr<acl::list> aclData):
253  dcapIoCommon(aPath) {
254  blockSize = 16 * 1024 * 1024;
255  if (noWrite) {
256  fd = -1;
257  } else {
258  dCapLock extraOptionLock;
259  std::string options("-alloc-size=");
260  options += std::to_string(sourceSize);
261  dc_setExtraOption(const_cast<char*>(options.c_str())); // const cast to work around broken interface
262  if (aclData) {
263  setAclData(*aclData);
264  }
265  auto openMode = O_CREAT | O_TRUNC | O_WRONLY;
266  state.clear(copyRequest::stateBitType::append); // clear any old append sets, may be left over from former attempts
267  // No appends on dcache, it's a WORM system
268  fd = throwcall::dcap::badval(dc_open(path.c_str(), openMode, S_IWUSR | S_IRUSR),
269  -1, "can't open ", path, " for writing");
270  if (unsafeWrite) {
271  dc_unsafeWrite(fd);
272  }
273  if (noChecksum) {
274  dc_noCheckSum(fd);
275  }
276  }
277  };
279  if (dc_close(fd) != 0) {
281  path, "close during unwind ",
282  dc_strerror(dc_errno));
283  }
285  path, "unlink failed copy", "due to exception");
286  if (dc_unlink(path.c_str()) != 0) {
288  path, "can't remove bad copy ",
289  dc_strerror(dc_errno));
290  }
291  };
292 
293  dcap::writerDcap::~writerDcap() noexcept(false) {
294  if (fd != -1) {
295  dCapLock extraOptionLock;
296  if (isUnwinding()) {
297  closeAndRemoveBadCopy();
298  return;
299  }
300  throwcall::dcap::good0(dc_close(fd), "can't close ", path);
301  }
302  };
303 
304 
306  size_t bytes_writen_so_far = 0;
307 
308  while (bytes_writen_so_far < b.size()) {
310  auto count = b.size() - bytes_writen_so_far;
311 
312  if (b.isHole()) { // just write zeroes
313  count = std::min({count, blockSize, block::nullBufferSize()});
314  } else {
315  count = std::min(count, blockSize);
316  }
317  auto bytes_writen = throwcall::dcap::badval(dc_write(fd, b.bufferAt(bytes_writen_so_far), count),
318  -1, "write failed on ", path);
319  if (bytes_writen == 0) {
320  throw std::runtime_error("wrote 0 bytes for " + path);
321  }
322  writeRateLimit.update(bytes_writen);
323  bytes_writen_so_far += bytes_writen;
324  }
325  }
326 
327  void dcap::writerDcap::doAttributePreservations(const genericStat& /*readInitialStat*/) {
328  throw unimplementedActionError("for dCap doAttributePreservations must be used with setAttributesAfterClose");
329  }
330 
331  void dcap::doAttributePreservations(const std::string& path,
332  const genericStat& stat) {
333  if (preserve.timestamps) { // for mtime we must use a parallel nfs mount, there is no function in the dcap lib.
334  struct timespec times[2];
335  times[0].tv_nsec = UTIME_OMIT; // leave atime unchanged
336  stat.getMtime(times[1]);
337  auto serverStart = path.find("://");
338  if (serverStart == std::string::npos) {
339  throw std::runtime_error(path + " contains no \"://\"");
340  }
341  auto realPathStart = path.find("/", serverStart + 3);
342  if (realPathStart == std::string::npos) {
343  throw std::runtime_error(path + " contains no path after server part");
344  }
345  throwcall::good0(utimensat(0, path.c_str() + realPathStart,
346  times, 0),
347  "can't set mtime on ", path.c_str() + realPathStart);
348  }
349  dCapLock extraOptionLock;
350  if (preserve.mode) {
351  throwcall::dcap::good0(dc_chmod(path.c_str(), stat.mode), "can't set mode of ", path);
352  } else {
353  auto oldmask = umask(0);
354  auto retval = dc_chmod(path.c_str(), modeBits & ~oldmask);
355  if (retval != 0) {
357  path, "chmod", "can't set mode ", dc_errno, " (", dc_strerror(dc_errno), ") ");
358  }
359  //throwcall::dcap::good0(dc_chmod(path.c_str(), modeBits & ~oldmask), "can't set mode of ", path);
360  umask(oldmask);
361  }
362 
363  if (gid != -1 || uid != -1) {
364  throwcall::dcap::good0(dc_chown(path.c_str(), uid, gid), "can't set owner/group (", uid, ",", gid, ") of ", path);
365  } else if (preserve.ownership) {
366  throwcall::dcap::good0(dc_chown(path.c_str(), stat.ownerUid, stat.ownerGid),
367  "can't set owner/group (", stat.ownerUid, ",", stat.ownerGid, ") of ",
368  path);
369  }
370  }
371 
372 
374  throwcall::dcap::good0(dc_fsync(fd), "can't sync ", path);
375  }
376 }; // end namespace outputHandler
genericStat::ownerGid
gid_t ownerGid
Definition: genericStat.h:24
block.h
genericStat::mode
mode_t mode
Definition: genericStat.h:22
inputHandler::base::getAclData
virtual std::unique_ptr< acl::list > getAclData(const std::string &)
Definition: inputHandler.h:155
throwcall::dcap::badval
T badval(T call, t badvalue, const Args &... args)
Definition: dcapCommon.h:28
errMsgQueue.h
outputHandler::dcap::rename
base::renameRetvalType rename(const std::string &fromPath, const std::unique_ptr< const genericStat > &readInitialStat, const std::string &toPath, copyRequest::stateType &state) override
Definition: outputHandlerDcap.cpp:136
dcapIoCommon
Definition: dcapCommon.h:74
outputHandler::dcap::renameSimple
bool renameSimple(const std::string &fromPath, const std::string &toPath) override
Definition: outputHandlerDcap.cpp:124
errMsg::location
class for defining the location of a error message in the source code.
Definition: errMsgQueue.h:14
dCapLock
Definition: dcapCommon.h:40
genericStat
generic stat abstraction class Used to abstract the variants of the stat structure.
Definition: genericStat.h:12
outputHandler::dcap::factory
static factoryTemplate< dcap > factory
Definition: outputHandlerDcap.h:9
outputHandler::dcap::writerDcap::noChecksum
static options::single< bool > noChecksum
Definition: outputHandlerDcap.h:15
outputHandler::dcap::writerDcap::sync
void sync() override
Definition: outputHandlerDcap.cpp:373
dcapStat
#define dcapStat
Definition: dcapCommon.h:7
copyRequest::stateBitType::ignore
@ ignore
errMsg::level::info
@ info
unimplementedActionError
class for exceptions that result from unimplemented functions Exceptions of this kind are to be throw...
Definition: copyRequestTypes.h:32
outputHandler::dcap::setAclData
static void setAclData(const acl::list &aclData, bool isDir=false)
Definition: outputHandlerDcap.cpp:188
acl::list::entryType::flagsType::special_who
@ special_who
outputHandler::base::renameRetvalType::cantHappen
@ cantHappen
copyRequest::stateBitType::done
@ done
outputHandler::base::renameRetvalType::fileChanged
@ fileChanged
copyRequest::stateBitType::failed
@ failed
outputHandler
Definition: ioHandle.h:9
genericStat::getMtime
void getMtime(struct timespec &spec) const
Definition: genericStat.cpp:65
dcapIoCommon::path
const std::string path
Definition: dcapCommon.h:76
gid
options::single< int > gid
copyRequest::stateBitType::vanished
@ vanished
options
Definition: Options.h:33
copyRequest::stateType
Definition: copyRequestTypes.h:66
block::isHole
bool isHole() const
Definition: block.h:36
genericStat::isSameMtimeAs
bool isSameMtimeAs(const genericStat &that) const
Definition: genericStat.cpp:87
errMsg::level::debug
@ debug
outputHandler::dcap::dcap
dcap()
Definition: outputHandlerDcap.cpp:27
genericStat::isDir
bool isDir() const
Definition: genericStat.cpp:92
ioHandle::blockSize
size_t blockSize
in bytes, block size to be used when reading or writing
Definition: ioHandle.h:17
outputHandler::dcap::doAttributePreservations
void doAttributePreservations(const std::string &path, const genericStat &stat) override
Definition: outputHandlerDcap.cpp:331
outputHandler::dcap::remove
void remove(const std::string &path, copyRequest::stateType &state) override
Definition: outputHandlerDcap.cpp:81
acl::list::entryType::aclMaskBits
static std::vector< std::pair< list::entryType::maskType, char > > aclMaskBits
Definition: acl.h:86
genericStat::size
size_t size
Definition: genericStat.h:16
outputHandler::dcap::writerDcap::writeBlock
void writeBlock(const block &b) override
Definition: outputHandlerDcap.cpp:305
copyRequest::base::setAttributesAfterClose
static options::single< bool > setAttributesAfterClose
Definition: copyRequest.h:124
outputHandler::base::renameRetvalType::fileVanished
@ fileVanished
block::bufferAt
void * bufferAt(size_t offset)
only way to access the data in the block
Definition: block.cpp:28
dcapCommon::fixPathUrl
static std::string fixPathUrl(const std::string &path)
Definition: dcapCommon.cpp:127
outputHandler::base::renameRetvalType::ok
@ ok
outputHandler::base::renameRetvalType
renameRetvalType
Definition: outputHandler.h:106
outputHandler::dcap::writerDcap::closeAndRemoveBadCopy
void closeAndRemoveBadCopy() override
Definition: outputHandlerDcap.cpp:278
block
data block, used to hold the data that are being copied (or checksummed).
Definition: block.h:7
outputHandler::dcap::writerDcap::unsafeWrite
static options::single< bool > unsafeWrite
Definition: outputHandlerDcap.h:14
acl::list
Definition: acl.h:8
acl::list::entryType::flagsType::identifier_group
@ identifier_group
copyRequest.h
preserve
decltype(preserve) preserve
set of properties to preserve in the copy
Definition: ewmscp.cpp:111
outputHandler::dcap::writerDcap
Definition: outputHandlerDcap.h:12
uid
options::single< int > uid
block::nullBufferSize
static size_t nullBufferSize()
Definition: block.cpp:34
inputHandler::base
class for handling input This is the (abstract) base class for handling input, both reading a file vi...
Definition: inputHandler.h:35
errMsg::level::err
@ err
throttle::watch::wait
void wait()
Definition: throttle.h:50
errMsg::emit
void emit(level aLogLevel, const location &loc, const std::string &aObject, const std::string &aAction, const Args &... args)
function to create and enqueue a message, this is the only way that messages should be created!
Definition: errMsgQueue.h:148
throwcall::dcap::good0
void good0(T call, const Args &... args)
Definition: dcapCommon.h:21
block::size
size_t size() const
Definition: block.h:16
writeRateLimit
throttle::watch writeRateLimit
acl::list::entries
std::vector< entryType > entries
Definition: acl.h:108
outputHandler::dcap::writerDcap::~writerDcap
~writerDcap() noexcept(false) override
Definition: outputHandlerDcap.cpp:293
outputHandler::dcap::writerDcap::writerDcap
writerDcap(const std::string &aPath, bool mightAppend, size_t sourceSize, size_t readBlockSize, copyRequest::stateType &state, bool noWrite, std::unique_ptr< acl::list > aAclData)
Definition: outputHandlerDcap.cpp:246
outputHandler::dcap::writerDcap::doAttributePreservations
void doAttributePreservations(const genericStat &readInitialStat) override
Definition: outputHandlerDcap.cpp:327
throwcall::good0
void good0(T call, const Args &... args)
template function to wrap system calls that return 0 on success
Definition: throwcall.h:40
outputHandler::dcap::newWriter
std::unique_ptr< writer > newWriter(const std::string &path, bool mightAppend, size_t sourceSize, size_t readBlockSize, copyRequest::stateType &state, bool noWrite, std::unique_ptr< ioHandle::attrDataType >, std::unique_ptr< acl::list > aclData) override
Definition: outputHandlerDcap.cpp:13
dcapIoCommon::fd
int fd
Definition: dcapCommon.h:77
errMsg::level::notice
@ notice
enumAsBitmask::clear
void clear(const T aBits)
Definition: enumAsBitmask.h:31
outputHandlerDcap.h
acl::list::entryType::maskType::delete_child
@ delete_child
outputHandler::dcap::ensureParentDirs
void ensureParentDirs(const std::string &path, const std::string &srcPath, inputHandler::base *InputHandler) override
Definition: outputHandlerDcap.cpp:36
throttle::watch::update
void update(double units=1.0)
Definition: throttle.h:35
ewmscp.h
copyRequest::stateBitType::append
@ append
genericStat::ownerUid
uid_t ownerUid
Definition: genericStat.h:23
modeBits
options::single< modeBitType > modeBits