XRootD
XrdOssCsi.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d O s s C s i . c c */
4 /* */
5 /* (C) Copyright 2020 CERN. */
6 /* */
7 /* This file is part of the XRootD software suite. */
8 /* */
9 /* XRootD is free software: you can redistribute it and/or modify it under */
10 /* the terms of the GNU Lesser General Public License as published by the */
11 /* Free Software Foundation, either version 3 of the License, or (at your */
12 /* option) any later version. */
13 /* */
14 /* In applying this licence, CERN does not waive the privileges and */
15 /* immunities granted to it by virtue of its status as an Intergovernmental */
16 /* Organization or submit itself to any jurisdiction. */
17 /* */
18 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
19 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
20 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
21 /* License for more details. */
22 /* */
23 /* You should have received a copy of the GNU Lesser General Public License */
24 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
25 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
26 /* */
27 /* The copyright holder's institutional names and contributor's names may not */
28 /* be used to endorse or promote products derived from this software without */
29 /* specific prior written permission of the institution or contributor. */
30 /******************************************************************************/
31 
32 #include "XrdOssCsiTrace.hh"
33 #include "XrdOssCsi.hh"
34 #include "XrdOssCsiConfig.hh"
35 #include "XrdOuc/XrdOucEnv.hh"
36 #include "XrdSys/XrdSysPageSize.hh"
37 #include "XrdOuc/XrdOuca2x.hh"
38 #include "XrdVersion.hh"
39 
40 #include <string>
41 #include <memory>
42 #include <functional>
43 
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <fcntl.h>
47 #include <limits.h>
48 #include <assert.h>
49 
51 
54 
55 XrdScheduler *XrdOssCsi::Sched_;
56 
57 int XrdOssCsiDir::Opendir(const char *path, XrdOucEnv &env)
58 {
59  if (config_.tagParam_.isTagFile(path)) return -ENOENT;
60 
61  skipsuffix_ = !config_.tagParam_.hasPrefix();
62  if (!skipsuffix_)
63  {
64  skipprefix_ = config_.tagParam_.matchPrefixDir(path);
65  if (skipprefix_)
66  {
67  skipprefixname_ = config_.tagParam_.getPrefixName();
68  }
69  }
70  return successor_->Opendir(path, env);
71 }
72 
73 // skip tag files in directory listing
74 int XrdOssCsiDir::Readdir(char *buff, int blen)
75 {
76  int ret;
77  do
78  {
79  ret = successor_->Readdir(buff, blen);
80  if (ret<0) return ret;
81  if (skipsuffix_)
82  {
83  if (config_.tagParam_.isTagFile(buff)) continue;
84  }
85  else if (skipprefix_)
86  {
87  if (skipprefixname_ == buff) continue;
88  }
89  break;
90  } while(1);
91  return ret;
92 }
93 
95 {
96  // tident starting with '*' is a special case to bypass OssCsi
97  if (tident && *tident == '*')
98  {
99  return successor_->newDir(tident);
100  }
101 
102  return (XrdOssDF *)new XrdOssCsiDir(successor_, tident, config_);
103 }
104 
106 {
107  // tident starting with '*' is a special case to bypass OssCsi
108  if (tident && *tident == '*')
109  {
110  return successor_->newFile(tident);
111  }
112 
113  return (XrdOssDF *)new XrdOssCsiFile(successor_, tident, config_);
114 }
115 
116 int XrdOssCsi::Init(XrdSysLogger *lP, const char *cP, const char *params, XrdOucEnv *env)
117 {
118  OssCsiEroute.logger(lP);
119 
120  int cret = config_.Init(OssCsiEroute, cP, params, env);
121  if (cret != XrdOssOK)
122  {
123  return cret;
124  }
125 
126  if ( ! env ||
127  ! (Sched_ = (XrdScheduler*) env->GetPtr("XrdScheduler*")))
128  {
129  Sched_ = new XrdScheduler;
130  Sched_->Start();
131  }
132 
133  return XrdOssOK;
134 }
135 
136 int XrdOssCsi::Unlink(const char *path, int Opts, XrdOucEnv *eP)
137 {
138  if (config_.tagParam_.isTagFile(path)) return -ENOENT;
139 
140  // get mapinfo entries for file
141  std::shared_ptr<XrdOssCsiFile::puMapItem_t> pmi;
142  {
143  const std::string tpath = config_.tagParam_.makeTagFilename(path);
144  XrdOssCsiFile::mapTake(tpath, pmi);
145  }
146 
147  int utret = 0;
148 
149  XrdSysMutexHelper lck(pmi->mtx);
150  pmi->dpath = path;
151  if (!pmi->unlinked)
152  {
153  const int uret = successor_->Unlink(path, Opts, eP);
154  if (uret != XrdOssOK)
155  {
156  XrdOssCsiFile::mapRelease(pmi,&lck);
157  return uret;
158  }
159 
160  utret = successor_->Unlink(pmi->tpath.c_str(), Opts, eP);
161  }
162 
163  pmi->unlinked = true;
164  XrdOssCsiFile::mapRelease(pmi,&lck);
165 
166  return (utret == -ENOENT) ? 0 : utret;
167 }
168 
169 int XrdOssCsi::Rename(const char *oldname, const char *newname,
170  XrdOucEnv *old_env, XrdOucEnv *new_env)
171 {
172  if (config_.tagParam_.isTagFile(oldname) || config_.tagParam_.isTagFile(newname)) return -ENOENT;
173 
174  const std::string inew = config_.tagParam_.makeTagFilename(newname);
175  const std::string iold = config_.tagParam_.makeTagFilename(oldname);
176 
177  // get mapinfo entries for both old and possibly existing newfile
178  std::shared_ptr<XrdOssCsiFile::puMapItem_t> newpmi,pmi;
179  XrdOssCsiFile::mapTake(inew, newpmi);
180  XrdOssCsiFile::mapTake(iold , pmi);
181 
182  // rename to self, do nothing
183  if (newpmi == pmi)
184  {
187  return 0;
188  }
189 
190  // take in consistent order
191  XrdSysMutexHelper lck(NULL), lck2(NULL);
192  // using the pointer here to get a total order, which is not
193  // guaranteed for operator<() so use std::less
194  if (std::less{}(pmi,newpmi))
195  {
196  lck.Lock(&newpmi->mtx);
197  lck2.Lock(&pmi->mtx);
198  }
199  else
200  {
201  lck2.Lock(&pmi->mtx);
202  lck.Lock(&newpmi->mtx);
203  }
204 
205  if (pmi->unlinked || newpmi->unlinked)
206  {
207  // something overwrote the source or target file since we checked
208  XrdOssCsiFile::mapRelease(pmi,&lck2);
209  XrdOssCsiFile::mapRelease(newpmi,&lck);
210  return Rename(oldname, newname, old_env, new_env);
211  }
212 
213  const int sret = successor_->Rename(oldname, newname, old_env, new_env);
214  if (sret<0)
215  {
216  XrdOssCsiFile::mapRelease(pmi,&lck2);
217  XrdOssCsiFile::mapRelease(newpmi,&lck);
218  return sret;
219  }
220 
221  int mkdret = XrdOssOK;
222  {
223  std::string base = inew;
224  const size_t idx = base.rfind("/");
225  base = base.substr(0,idx);
226  if (!base.empty())
227  {
228  const int AMode = S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH; // 775
229  mkdret = successor_->Mkdir(base.c_str(), AMode, 1, new_env);
230  }
231  }
232 
233  if (mkdret != XrdOssOK && mkdret != -EEXIST)
234  {
235  (void) successor_->Rename(newname, oldname, new_env, old_env);
236  XrdOssCsiFile::mapRelease(pmi,&lck2);
237  XrdOssCsiFile::mapRelease(newpmi,&lck);
238  return mkdret;
239  }
240 
241  const int iret = successor_->Rename(iold.c_str(), inew.c_str(), old_env, new_env);
242  if (iret<0)
243  {
244  if (iret == -ENOENT)
245  {
246  // old tag did not exist, make sure there is no new tag
247  (void) successor_->Unlink(inew.c_str(), 0, new_env);
248  }
249  else
250  {
251  (void) successor_->Rename(newname, oldname, new_env, old_env);
252  XrdOssCsiFile::mapRelease(pmi,&lck2);
253  XrdOssCsiFile::mapRelease(newpmi,&lck);
254  return iret;
255  }
256  }
257 
258  if (newpmi)
259  {
260  newpmi->unlinked = true;
261  }
262 
263  {
265  auto mapidx_new = XrdOssCsiFile::pumap_.find(inew);
266  if (mapidx_new != XrdOssCsiFile::pumap_.end()) XrdOssCsiFile::pumap_.erase(mapidx_new);
267 
268  auto mapidx = XrdOssCsiFile::pumap_.find(iold);
269  assert(mapidx != XrdOssCsiFile::pumap_.end());
270 
271  XrdOssCsiFile::pumap_.erase(mapidx);
272  XrdOssCsiFile::pumap_.insert(std::make_pair(inew, pmi));
273  pmi->dpath = newname;
274  pmi->tpath = inew;
275  }
276 
277  XrdOssCsiFile::mapRelease(pmi,&lck2);
278  XrdOssCsiFile::mapRelease(newpmi,&lck);
279 
280  return XrdOssOK;
281 }
282 
283 int XrdOssCsi::Truncate(const char *path, unsigned long long size, XrdOucEnv *envP)
284 {
285  if (config_.tagParam_.isTagFile(path)) return -ENOENT;
286 
287  std::unique_ptr<XrdOssDF> fp(newFile("xrdt"));
288  XrdOucEnv myEnv;
289  int ret = fp->Open(path, O_RDWR, 0, envP ? *envP : myEnv);
290  if (ret != XrdOssOK)
291  {
292  return ret;
293  }
294  ret = fp->Ftruncate(size);
295  if (ret != XrdOssOK)
296  {
297  return ret;
298  }
299  long long retsz=0;
300  fp->Close(&retsz);
301  return XrdOssOK;
302 }
303 
304 int XrdOssCsi::Reloc(const char *tident, const char *path,
305  const char *cgName, const char *anchor)
306 {
307  if (config_.tagParam_.isTagFile(path)) return -ENOENT;
308  return successor_->Reloc(tident, path, cgName, anchor);
309 }
310 
311 int XrdOssCsi::Mkdir(const char *path, mode_t mode, int mkpath, XrdOucEnv *envP)
312 {
313  if (config_.tagParam_.isTagFile(path)) return -EACCES;
314  return successor_->Mkdir(path, mode, mkpath, envP);
315 }
316 
317 int XrdOssCsi::Create(const char *tident, const char *path, mode_t access_mode,
318  XrdOucEnv &env, int Opts)
319 {
320  // tident starting with '*' is a special case to bypass OssCsi
321  if (tident && *tident == '*')
322  {
323  return successor_->Create(tident, path, access_mode, env, Opts);
324  }
325 
326  if (config_.tagParam_.isTagFile(path)) return -EACCES;
327 
328  // get mapinfo entries for file
329  std::shared_ptr<XrdOssCsiFile::puMapItem_t> pmi;
330  {
331  const std::string tpath = config_.tagParam_.makeTagFilename(path);
332  XrdOssCsiFile::mapTake(tpath, pmi);
333  }
334 
335  XrdSysMutexHelper lck(pmi->mtx);
336  if (pmi->unlinked)
337  {
338  XrdOssCsiFile::mapRelease(pmi,&lck);
339  return Create(tident, path, access_mode, env, Opts);
340  }
341 
342  const bool isTrunc = ((Opts>>8)&O_TRUNC) ? true : false;
343  const bool isExcl = ((Opts&XRDOSS_new) || ((Opts>>8)&O_EXCL)) ? true : false;
344 
345  if (isTrunc && pmi->pages)
346  {
347  // truncate of already open file at open() not supported
348  XrdOssCsiFile::mapRelease(pmi, &lck);
349  return -EDEADLK;
350  }
351 
352  // create file: require it not to exist (unless we're truncating) so that
353  // we can tell if we have a zero length file without stat in more cases
354 
355  const int exflags = isTrunc ? 0 : ((O_EXCL<<8)|XRDOSS_new);
356 
357  int ret = successor_->Create(tident, path, access_mode, env, Opts | exflags);
358  if (ret == XrdOssOK || ret == -EEXIST)
359  {
360  // success from trunc/exclusive create means the file must now be zero length
361  bool zlen = (ret == XrdOssOK) ? true : false;
362  struct stat sbuf;
363  if (!zlen && successor_->Stat(path, &sbuf, 0, &env) == XrdOssOK)
364  {
365  // had to check file size
366  if (sbuf.st_size == 0)
367  {
368  zlen = true;
369  }
370  }
371 
372  // If datafile is zero length try to make empty tag file
373  if (zlen)
374  {
375  const std::string tpath = config_.tagParam_.makeTagFilename(path);
376  const int flags = O_RDWR|O_CREAT|O_TRUNC;
377  const int cropts = XRDOSS_mkpath;
378 
379  std::unique_ptr<XrdOucEnv> tagEnv = tagOpenEnv(config_, env);
380 
381  ret = successor_->Create(tident, tpath.c_str(), 0666, *tagEnv, (flags<<8)|cropts);
382  }
383  }
384 
385  XrdOssCsiFile::mapRelease(pmi, &lck);
386 
387  // may not need to return EEXIST
388  return (ret==-EEXIST && !isExcl) ? XrdOssOK : ret;
389 }
390 
391 int XrdOssCsi::Chmod(const char *path, mode_t mode, XrdOucEnv *envP)
392 {
393  if (config_.tagParam_.isTagFile(path)) return -ENOENT;
394  return successor_->Chmod(path, mode, envP);
395 }
396 
397 int XrdOssCsi::Remdir(const char *path, int Opts, XrdOucEnv *eP)
398 {
399  if (config_.tagParam_.isTagFile(path)) return -ENOENT;
400  const int ret = successor_->Remdir(path, Opts, eP);
401  if (ret != XrdOssOK || !config_.tagParam_.hasPrefix()) return ret;
402 
403  // try to remove the corresponding directory under the tagfile directory.
404  // ignore errors
405 
406  const std::string tpath = config_.tagParam_.makeBaseDirname(path);
407  (void) successor_->Remdir(tpath.c_str(), Opts, eP);
408  return XrdOssOK;
409 }
410 
411 int XrdOssCsi::Stat(const char *path, struct stat *buff, int opts,
412  XrdOucEnv *EnvP)
413 {
414  if (config_.tagParam_.isTagFile(path)) return -ENOENT;
415  return successor_->Stat(path, buff, opts, EnvP);
416 }
417 
418 int XrdOssCsi::StatPF(const char *path, struct stat *buff, int opts)
419 {
420  if (config_.tagParam_.isTagFile(path)) return -ENOENT;
421  if (!(opts & XrdOss::PF_dStat)) return successor_->StatPF(path, buff, opts);
422 
423  buff->st_rdev = 0;
424  const int pfret = successor_->StatPF(path, buff, opts);
425  if (pfret != XrdOssOK)
426  {
427  return pfret;
428  }
429 
430  std::unique_ptr<XrdOssCsiFile> fp((XrdOssCsiFile*)newFile("xrdt"));
431  XrdOucEnv myEnv;
432  const int oret = fp->Open(path, O_RDONLY, 0, myEnv);
433  if (oret != XrdOssOK)
434  {
435  return oret;
436  }
437  const int vs = fp->VerificationStatus();
438 
439  long long retsz=0;
440  fp->Close(&retsz);
441 
442  buff->st_rdev &= ~(XrdOss::PF_csVer | XrdOss::PF_csVun);
443  buff->st_rdev |= static_cast<dev_t>(vs);
444  return XrdOssOK;
445 }
446 
447 int XrdOssCsi::StatXA(const char *path, char *buff, int &blen,
448  XrdOucEnv *envP)
449 {
450  if (config_.tagParam_.isTagFile(path)) return -ENOENT;
451  return successor_->StatXA(path, buff, blen, envP);
452 }
453 
454 
457  const char *config_fn,
458  const char *parms,
459  XrdOucEnv *envP)
460 {
461  XrdOssCsi *myOss = new XrdOssCsi(curr_oss);
462  if (myOss->Init(Logger, config_fn, parms, envP) != XrdOssOK)
463  {
464  delete myOss;
465  return NULL;
466  }
467  return (XrdOss*)myOss;
468 }
469 
470 std::unique_ptr<XrdOucEnv> XrdOssCsi::tagOpenEnv(const XrdOssCsiConfig &config, XrdOucEnv &env)
471 {
472  // for tagfile open, start with copy of datafile environment
473  int infolen;
474  const char *info = env.Env(infolen);
475  std::unique_ptr<XrdOucEnv> newEnv(new XrdOucEnv(info, infolen, env.secEnv()));
476 
477  // give space name for tag files
478  newEnv->Put("oss.cgroup", config.xrdtSpaceName().c_str());
479 
480  char *tmp;
481  long long cgSize=0;
482  if ((tmp = env.Get("oss.asize")) && XrdOuca2x::a2sz(OssCsiEroute,"invalid asize",tmp,&cgSize,0))
483  {
484  cgSize=0;
485  }
486 
487  if (cgSize>0)
488  {
489  char size_str[32];
490  sprintf(size_str, "%lld", 20+4*((cgSize+XrdSys::PageSize-1)/XrdSys::PageSize));
491  newEnv->Put("oss.asize", size_str);
492  }
493  else
494  {
495  newEnv->Put("oss.asize", "0");
496  }
497 
498  return newEnv;
499 }
#define tident
XrdOucTrace OssCsiTrace
XrdVERSIONINFO(XrdOssAddStorageSystem2, XrdOssCsi) XrdSysError OssCsiEroute(0
XrdOucTrace OssCsiTrace & OssCsiEroute
Definition: XrdOssCsi.cc:53
osscsi_
Definition: XrdOssCsi.cc:52
XrdOss * XrdOssAddStorageSystem2(XrdOss *curr_oss, XrdSysLogger *Logger, const char *config_fn, const char *parms, XrdOucEnv *envP)
Definition: XrdOssCsi.cc:455
#define XrdOssOK
Definition: XrdOss.hh:50
#define XRDOSS_new
Definition: XrdOss.hh:467
#define XRDOSS_mkpath
Definition: XrdOss.hh:466
#define stat(a, b)
Definition: XrdPosix.hh:101
struct myOpts opts
std::string makeBaseDirname(const char *path)
std::string makeTagFilename(const char *path)
bool hasPrefix()
bool isTagFile(const char *path)
std::string xrdtSpaceName() const
int Init(XrdSysError &, const char *, const char *, XrdOucEnv *)
virtual int Opendir(const char *path, XrdOucEnv &env)
Definition: XrdOssCsi.cc:57
virtual int Readdir(char *buff, int blen)
Definition: XrdOssCsi.cc:74
static XrdSysMutex pumtx_
Definition: XrdOssCsi.hh:159
static std::unordered_map< std::string, std::shared_ptr< puMapItem_t > > pumap_
Definition: XrdOssCsi.hh:160
static void mapTake(const std::string &, std::shared_ptr< puMapItem_t > &, bool create=true)
static int mapRelease(std::shared_ptr< puMapItem_t > &, XrdSysMutexHelper *plck=NULL)
virtual int Remdir(const char *path, int Opts=0, XrdOucEnv *eP=0)
Definition: XrdOssCsi.cc:397
virtual int Stat(const char *path, struct stat *buff, int opts=0, XrdOucEnv *EnvP=0)
Definition: XrdOssCsi.cc:411
static std::unique_ptr< XrdOucEnv > tagOpenEnv(const XrdOssCsiConfig &, XrdOucEnv &)
Definition: XrdOssCsi.cc:470
virtual int Unlink(const char *path, int Opts=0, XrdOucEnv *eP=0)
Definition: XrdOssCsi.cc:136
virtual int Reloc(const char *tident, const char *path, const char *cgName, const char *anchor=0)
Definition: XrdOssCsi.cc:304
virtual int Init(XrdSysLogger *lp, const char *cfn)
Definition: XrdOssCsi.hh:186
virtual XrdOssDF * newFile(const char *tident)
Definition: XrdOssCsi.cc:105
virtual int Mkdir(const char *path, mode_t mode, int mkpath=0, XrdOucEnv *envP=0)
Definition: XrdOssCsi.cc:311
virtual int Chmod(const char *path, mode_t mode, XrdOucEnv *envP=0)
Definition: XrdOssCsi.cc:391
virtual int Create(const char *tident, const char *path, mode_t access_mode, XrdOucEnv &env, int Opts=0)
Definition: XrdOssCsi.cc:317
virtual int Truncate(const char *path, unsigned long long size, XrdOucEnv *envP=0)
Definition: XrdOssCsi.cc:283
virtual int StatXA(const char *path, char *buff, int &blen, XrdOucEnv *envP=0)
Definition: XrdOssCsi.cc:447
static XrdScheduler * Sched_
Definition: XrdOssCsi.hh:216
virtual int StatPF(const char *path, struct stat *buff, int opts)
Definition: XrdOssCsi.cc:418
virtual int Rename(const char *oldname, const char *newname, XrdOucEnv *old_env=0, XrdOucEnv *new_env=0)
Definition: XrdOssCsi.cc:169
virtual XrdOssDF * newDir(const char *tident)
Definition: XrdOssCsi.cc:94
XrdOssDF * successor_
virtual int Readdir(char *buff, int blen)
Definition: XrdOss.hh:92
XrdOss * successor_
virtual int Mkdir(const char *path, mode_t mode, int mkpath=0, XrdOucEnv *envP=0)=0
static const int PF_dStat
Definition: XrdOss.hh:773
virtual int StatXA(const char *path, char *buff, int &blen, XrdOucEnv *envP=0)
Definition: XrdOss.cc:127
static const int PF_csVer
verified file checksums present
Definition: XrdOss.hh:778
virtual int Create(const char *tid, const char *path, mode_t mode, XrdOucEnv &env, int opts=0)=0
virtual XrdOssDF * newDir(const char *tident)=0
virtual int Reloc(const char *tident, const char *path, const char *cgName, const char *anchor=0)
Definition: XrdOss.cc:76
virtual int Chmod(const char *path, mode_t mode, XrdOucEnv *envP=0)=0
virtual int StatPF(const char *path, struct stat *buff, int opts)
Definition: XrdOss.cc:107
static const int PF_csVun
unverified file checksums present
Definition: XrdOss.hh:779
virtual int Remdir(const char *path, int Opts=0, XrdOucEnv *envP=0)=0
virtual int Rename(const char *oPath, const char *nPath, XrdOucEnv *oEnvP=0, XrdOucEnv *nEnvP=0)=0
virtual XrdOssDF * newFile(const char *tident)=0
virtual int Stat(const char *path, struct stat *buff, int opts=0, XrdOucEnv *envP=0)=0
virtual int Unlink(const char *path, int Opts=0, XrdOucEnv *envP=0)=0
void * GetPtr(const char *varname)
Definition: XrdOucEnv.cc:281
char * Get(const char *varname)
Definition: XrdOucEnv.hh:69
const XrdSecEntity * secEnv() const
Definition: XrdOucEnv.hh:107
char * Env(int &envlen)
Definition: XrdOucEnv.hh:48
static int a2sz(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1)
Definition: XrdOuca2x.cc:257
void Lock(XrdSysMutex *Mutex)
XrdSysLogger Logger
Definition: XrdGlobals.cc:47
int Opts
Definition: XrdMpxStats.cc:58
XrdOucEnv * envP
Definition: XrdPss.cc:109
static const int PageSize