XRootD
XrdOssCsiFile.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d O s s C s i F i l e . c c */
4 /* */
5 /* (C) Copyright 2021 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 "XrdOssCsi.hh"
33 #include "XrdOssCsiTrace.hh"
34 #include "XrdOssCsiTagstoreFile.hh"
35 #include "XrdOssCsiPages.hh"
36 #include "XrdOssCsiRanges.hh"
37 #include "XrdOuc/XrdOucCRC.hh"
38 #include "XrdOuc/XrdOucEnv.hh"
39 #include "XrdSfs/XrdSfsAio.hh"
40 #include "XrdSys/XrdSysPageSize.hh"
41 #include "XrdSfs/XrdSfsAio.hh"
42 
43 #include <string>
44 #include <algorithm>
45 
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50 #include <limits.h>
51 #include <assert.h>
52 
55 
56 // storage for class members
58 std::unordered_map<std::string, std::shared_ptr<XrdOssCsiFile::puMapItem_t> > XrdOssCsiFile::pumap_;
59 
60 //
61 // If no others hold a pointer to Pages object, close it and remoe the pagemap info object.
62 //
63 int XrdOssCsiFile::pageMapClose()
64 {
65  if (!pmi_) return -EBADF;
66  bool doclose = false;
67 
68  XrdSysMutexHelper lck(pmi_->mtx);
69  if (mapRelease(pmi_)) doclose = true;
70 
71  int cpret = 0;
72  if (doclose)
73  {
74  if (pmi_->pages)
75  {
76  cpret = pmi_->pages->Close();
77  pmi_->pages.reset();
78  }
79  }
80 
81  lck.UnLock();
82  pmi_.reset();
83 
84  return cpret;
85 }
86 
87 void XrdOssCsiFile::mapTake(const std::string &key, std::shared_ptr<puMapItem_t> &pmi, const bool create)
88 {
90  auto mapidx = pumap_.find(key);
91  if (mapidx == pumap_.end())
92  {
93  if (!create) return;
94  pmi.reset(new puMapItem_t());
95  pmi->tpath = key;
96  if (!key.empty())
97  {
98  pumap_.insert(std::make_pair(key, pmi));
99  }
100  }
101  else
102  {
103  pmi = mapidx->second;
104  }
105  pmi->refcount++;
106 }
107 
108 int XrdOssCsiFile::mapRelease(std::shared_ptr<puMapItem_t> &pmi, XrdSysMutexHelper *plck)
109 {
111  pmi->refcount--;
112  auto mapidx = pumap_.find(pmi->tpath);
113  if (pmi->refcount == 0 || pmi->unlinked)
114  {
115  if (mapidx != pumap_.end() && mapidx->second == pmi)
116  {
117  pumap_.erase(mapidx);
118  }
119  }
120  if (plck) plck->UnLock();
121  return (pmi->refcount == 0) ? 1 : 0;
122 }
123 
124 int XrdOssCsiFile::pageAndFileOpen(const char *fn, const int dflags, const int Oflag, const mode_t Mode, XrdOucEnv &Env)
125 {
126  if (pmi_) return -EBADF;
127 
128  {
129  std::string tpath = config_.tagParam_.makeTagFilename(fn);
130  mapTake(tpath, pmi_);
131  }
132 
133  XrdSysMutexHelper lck(pmi_->mtx);
134  pmi_->dpath = fn;
135  if (pmi_->unlinked)
136  {
137  mapRelease(pmi_, &lck);
138  // filename replaced since check, try again
139  pmi_.reset();
140  return pageAndFileOpen(fn, dflags, Oflag, Mode, Env);
141  }
142 
143  if ((dflags & O_TRUNC) && pmi_->pages)
144  {
145  // truncate of already open file at open() not supported
146  mapRelease(pmi_, &lck);
147  pmi_.reset();
148  return -EDEADLK;
149  }
150 
151  const int dataret = successor_->Open(pmi_->dpath.c_str(), dflags, Mode, Env);
152  int pageret = XrdOssOK;
153  if (dataret == XrdOssOK)
154  {
155  if (pmi_->pages)
156  {
157  return XrdOssOK;
158  }
159 
160  pageret = createPageUpdater(Oflag, Env);
161  if (pageret == XrdOssOK)
162  {
163  return XrdOssOK;
164  }
165 
166  // failed to open the datafile or create the page object.
167  // close datafile if needed
168  (void) successor_->Close();
169  }
170 
171  mapRelease(pmi_, &lck);
172  pmi_.reset();
173 
174  return (dataret != XrdOssOK) ? dataret : pageret;
175 }
176 
178 {
179  if (pmi_)
180  {
181  (void)Close();
182  }
183 }
184 
185 int XrdOssCsiFile::Close(long long *retsz)
186 {
187  if (!pmi_)
188  {
189  return -EBADF;
190  }
191 
192  // wait for any ongoing aios to finish
193  aioWait();
194 
195  const int cpret = pageMapClose();
196 
197  const int csret = successor_->Close(retsz);
198  if (cpret<0) return cpret;
199  return csret;
200 }
201 
202 int XrdOssCsiFile::createPageUpdater(const int Oflag, XrdOucEnv &Env)
203 {
204  std::unique_ptr<XrdOucEnv> tagEnv = XrdOssCsi::tagOpenEnv(config_, Env);
205 
206  // get information about data file size
207  off_t dsize = 0;
208  if (!(Oflag & O_EXCL) && !(Oflag & O_TRUNC))
209  {
210  struct stat sb;
211  const int sstat = successor_->Fstat(&sb);
212  if (sstat<0)
213  {
214  return sstat;
215  }
216  dsize = sb.st_size;
217  }
218 
219  // tag file always opened O_RDWR as the Tagstore/Pages object associated will be shared
220  // between any File instances which concurrently access the file
221  // (some of which may be RDWR, some RDONLY)
222  int tagFlags = O_RDWR;
223 
224  // data file was truncated, do same to tag file and let it be reset
225  if ((Oflag & O_TRUNC)) tagFlags |= O_TRUNC;
226 
227  // The concern with allowing creation of a new tag file is that the data file may
228  // already exist. Creating a new empty tag file would usually cause subsequent access
229  // errors, but not if the data file starts empty. In addition we may have been
230  // configured to ignore missing tag files. Approach taken is that:
231  // If the data file creation was wanted and it is currently zero length then
232  // allow creation of tag file.
233  if ((Oflag & O_CREAT) && dsize == 0)
234  {
235  tagFlags |= O_CREAT;
236  }
237 
238  // be sure the leading directories exist for the tag file
239  if ((tagFlags & O_CREAT))
240  {
241  int mkdret = XrdOssOK;
242  {
243  std::string base = pmi_->tpath;
244  const size_t idx = base.rfind("/");
245  base = base.substr(0,idx);
246  if (!base.empty())
247  {
248  const int AMode = S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH; // 775
249  mkdret = parentOss_->Mkdir(base.c_str(), AMode, 1, tagEnv.get());
250  }
251  }
252  if (mkdret != XrdOssOK && mkdret != -EEXIST)
253  {
254  return mkdret;
255  }
256  }
257 
258  std::unique_ptr<XrdOssDF> integFile(parentOss_->newFile(tident));
259  std::unique_ptr<XrdOssCsiTagstore> ts(new
260  XrdOssCsiTagstoreFile(pmi_->dpath, std::move(integFile), tident));
261  std::unique_ptr<XrdOssCsiPages> pages(new
262  XrdOssCsiPages(pmi_->dpath, std::move(ts), config_.fillFileHole(), config_.allowMissingTags(),
263  config_.disablePgExtend(), config_.disableLooseWrite(), tident));
264 
265  int puret = pages->Open(pmi_->tpath.c_str(), dsize, tagFlags, *tagEnv);
266  if (puret<0)
267  {
268  if ((puret == -EROFS || puret == -EACCES) && rdonly_)
269  {
270  // try to open tag file readonly
271  puret = pages->Open(pmi_->tpath.c_str(), dsize, O_RDONLY, *tagEnv);
272  }
273  }
274 
275  if (puret<0)
276  {
277  return puret;
278  }
279 
280  pages->BasicConsistencyCheck(successor_);
281  pmi_->pages = std::move(pages);
282  return XrdOssOK;
283 }
284 
285 int XrdOssCsiFile::Open(const char *path, const int Oflag, const mode_t Mode, XrdOucEnv &Env)
286 {
287  char cxid[4];
288 
289  if (pmi_)
290  {
291  // already open
292  return -EINVAL;
293  }
294 
295  if (!path)
296  {
297  return -EINVAL;
298  }
299  if (config_.tagParam_.isTagFile(path))
300  {
301  if ((Oflag & O_CREAT)) return -EACCES;
302  return -ENOENT;
303  }
304 
305  int dflags = Oflag;
306  if ((dflags & O_ACCMODE) == O_WRONLY)
307  {
308  // for non-aligned writes it may be needed to do read-modify-write
309  dflags &= ~O_ACCMODE;
310  dflags |= O_RDWR;
311  }
312 
313  rdonly_ = true;
314  if ((dflags & O_ACCMODE) != O_RDONLY)
315  {
316  rdonly_ = false;
317  }
318 
319  const int oret = pageAndFileOpen(path, dflags, Oflag, Mode, Env);
320  if (oret<0)
321  {
322  return oret;
323  }
324 
325  if (successor_->isCompressed(cxid)>0)
326  {
327  (void)Close();
328  return -ENOTSUP;
329  }
330 
331  if (Pages()->IsReadOnly() && !rdonly_)
332  {
333  (void)Close();
334  return -EACCES;
335  }
336  return XrdOssOK;
337 }
338 
339 ssize_t XrdOssCsiFile::Read(off_t offset, size_t blen)
340 {
341  return successor_->Read(offset, blen);
342 }
343 
344 ssize_t XrdOssCsiFile::Read(void *buff, off_t offset, size_t blen)
345 {
346  if (!pmi_) return -EBADF;
347 
349  Pages()->LockTrackinglen(rg, offset, offset+blen, true);
350 
351  const ssize_t bread = successor_->Read(buff, offset, blen);
352  if (bread<0 || blen==0) return bread;
353 
354  const ssize_t puret = Pages()->VerifyRange(successor_, buff, offset, bread, rg);
355  if (puret<0) return puret;
356  return bread;
357 }
358 
359 ssize_t XrdOssCsiFile::ReadRaw(void *buff, off_t offset, size_t blen)
360 {
361  if (!pmi_) return -EBADF;
362 
364  Pages()->LockTrackinglen(rg, offset, offset+blen, true);
365 
366  const ssize_t bread = successor_->ReadRaw(buff, offset, blen);
367  if (bread<0 || blen==0) return bread;
368 
369  const ssize_t puret = Pages()->VerifyRange(successor_, buff, offset, bread, rg);
370  if (puret<0) return puret;
371  return bread;
372 }
373 
374 ssize_t XrdOssCsiFile::ReadV(XrdOucIOVec *readV, int n)
375 {
376  if (!pmi_) return -EBADF;
377  if (n==0) return 0;
378 
380  off_t start = readV[0].offset;
381  off_t end = start + (off_t)readV[0].size;
382  for(int i=1; i<n; i++)
383  {
384  const off_t p1 = readV[i].offset;
385  const off_t p2 = p1 + (off_t)readV[i].size;
386  if (p1<start) start = p1;
387  if (p2>end) end = p2;
388  }
389  Pages()->LockTrackinglen(rg, start, end, true);
390 
391  // standard OSS gives -ESPIPE in case of partial read of an element
392  ssize_t rret = successor_->ReadV(readV, n);
393  if (rret<0) return rret;
394  for (int i=0; i<n; i++)
395  {
396  if (readV[i].size == 0) continue;
397  ssize_t puret = Pages()->VerifyRange(successor_, readV[i].data, readV[i].offset, readV[i].size, rg);
398  if (puret<0) return puret;
399  }
400  return rret;
401 }
402 
403 ssize_t XrdOssCsiFile::Write(const void *buff, off_t offset, size_t blen)
404 {
405  if (!pmi_) return -EBADF;
406  if (rdonly_) return -EBADF;
407 
409  Pages()->LockTrackinglen(rg, offset, offset+blen, false);
410 
411  int puret = Pages()->UpdateRange(successor_, buff, offset, blen, rg);
412  if (puret<0)
413  {
414  rg.ReleaseAll();
415  resyncSizes();
416  return (ssize_t)puret;
417  }
418  ssize_t towrite = blen;
419  ssize_t bwritten = 0;
420  const uint8_t *p = (uint8_t*)buff;
421  while(towrite>0)
422  {
423  ssize_t wret = successor_->Write(&p[bwritten], offset+bwritten, towrite);
424  if (wret<0)
425  {
426  rg.ReleaseAll();
427  resyncSizes();
428  return wret;
429  }
430  towrite -= wret;
431  bwritten += wret;
432  }
433  return bwritten;
434 }
435 
436 ssize_t XrdOssCsiFile::WriteV(XrdOucIOVec *writeV, int n)
437 {
438  if (!pmi_) return -EBADF;
439  if (rdonly_) return -EBADF;
440  if (n==0) return 0;
441 
443  off_t start = writeV[0].offset;
444  off_t end = start + (off_t)writeV[0].size;
445  for(int i=1; i<n; i++)
446  {
447  const off_t p1 = writeV[i].offset;
448  const off_t p2 = p1 + (off_t)writeV[i].size;
449  if (p1<start) start = p1;
450  if (p2>end) end = p2;
451  }
452  Pages()->LockTrackinglen(rg, start, end, false);
453 
454  for (int i=0; i<n; i++)
455  {
456  int ret = Pages()->UpdateRange(successor_, writeV[i].data, writeV[i].offset, writeV[i].size, rg);
457  if (ret<0)
458  {
459  rg.ReleaseAll();
460  resyncSizes();
461  return ret;
462  }
463  }
464  // standard OSS gives -ESPIPE in case of partial write of an element
465  ssize_t wret = successor_->WriteV(writeV, n);
466  if (wret<0)
467  {
468  rg.ReleaseAll();
469  resyncSizes();
470  }
471  return wret;
472 }
473 
474 ssize_t XrdOssCsiFile::pgRead(void *buffer, off_t offset, size_t rdlen, uint32_t *csvec, uint64_t opts)
475 {
476  if (!pmi_) return -EBADF;
477 
479  Pages()->LockTrackinglen(rg, offset, offset+rdlen, true);
480 
481  // if we return a short amount of data the caller will have to deal with
482  // joining csvec values from repeated reads: for simplicity try to read as
483  // such as possible up to the request read length
484  ssize_t toread = rdlen;
485  ssize_t bread = 0;
486  uint8_t *const p = (uint8_t*)buffer;
487  do
488  {
489  ssize_t rret = successor_->Read(&p[bread], offset+bread, toread);
490  if (rret<0) return rret;
491  if (rret==0) break;
492  toread -= rret;
493  bread += rret;
494  } while(toread>0);
495  if (rdlen == 0) return bread;
496 
497  ssize_t puret = Pages()->FetchRange(successor_, buffer, offset, bread, csvec, opts, rg);
498  if (puret<0) return puret;
499  return bread;
500 }
501 
502 ssize_t XrdOssCsiFile::pgWrite(void *buffer, off_t offset, size_t wrlen, uint32_t *csvec, uint64_t opts)
503 {
504  if (!pmi_) return -EBADF;
505  if (rdonly_) return -EBADF;
506  uint64_t pgopts = opts;
507 
508  const int prec = XrdOssCsiPages::pgWritePrelockCheck(buffer, offset, wrlen, csvec, opts);
509  if (prec < 0)
510  {
511  return prec;
512  }
513 
515  Pages()->LockTrackinglen(rg, offset, offset+wrlen, false);
516 
517  int puret = Pages()->StoreRange(successor_, buffer, offset, wrlen, csvec, pgopts, rg);
518  if (puret<0) {
519  rg.ReleaseAll();
520  resyncSizes();
521  return (ssize_t)puret;
522  }
523  ssize_t towrite = wrlen;
524  ssize_t bwritten = 0;
525  const uint8_t *p = (uint8_t*)buffer;
526  do
527  {
528  ssize_t wret = successor_->Write(&p[bwritten], offset+bwritten, towrite);
529  if (wret<0)
530  {
531  rg.ReleaseAll();
532  resyncSizes();
533  return wret;
534  }
535  towrite -= wret;
536  bwritten += wret;
537  } while(towrite>0);
538  return bwritten;
539 }
540 
542 {
543  if (!pmi_) return -EBADF;
544 
545  const int psret = Pages()->Fsync();
546  const int ssret = successor_->Fsync();
547  if (psret<0) return psret;
548  return ssret;
549 }
550 
551 int XrdOssCsiFile::Ftruncate(unsigned long long flen)
552 {
553  if (!pmi_) return -EBADF;
554  if (rdonly_) return -EBADF;
555 
557  Pages()->LockTrackinglen(rg, flen, LLONG_MAX, false);
558  int ret = Pages()->truncate(successor_, flen, rg);
559  if (ret<0)
560  {
561  rg.ReleaseAll();
562  resyncSizes();
563  return ret;
564  }
565  ret = successor_->Ftruncate(flen);
566  if (ret<0)
567  {
568  rg.ReleaseAll();
569  resyncSizes();
570  }
571  return ret;
572 }
573 
574 int XrdOssCsiFile::Fstat(struct stat *buff)
575 {
576  if (!pmi_) return -EBADF;
578  const int tsret = Pages()->TrackedSizesGet(sizes, false);
579  const int fsret = successor_->Fstat(buff);
580  if (fsret<0) return fsret;
581  if (tsret<0) return 0;
582  buff->st_size = std::max(sizes.first, sizes.second);
583  return 0;
584 }
585 
586 int XrdOssCsiFile::resyncSizes()
587 {
589  Pages()->LockTrackinglen(rg, 0, LLONG_MAX, false);
590  struct stat sbuff;
591  int ret = successor_->Fstat(&sbuff);
592  if (ret<0) return ret;
593  Pages()->LockResetSizes(successor_, sbuff.st_size);
594  return 0;
595 }
596 
598 {
599  if (!pmi_) return;
600 
601  Pages()->Flush();
602  successor_->Flush();
603 }
604 
606 {
607  if (!pmi_) return 0;
608  return Pages()->VerificationStatus();
609 }
#define tident
static std::string ts()
timestamp output for logging messages
Definition: XrdCephOss.cc:53
XrdOucTrace OssCsiTrace
XrdSysError OssCsiEroute
Definition: XrdOssCsi.cc:53
#define XrdOssOK
Definition: XrdOss.hh:54
#define stat(a, b)
Definition: XrdPosix.hh:105
int Mode
struct myOpts opts
std::string makeTagFilename(const char *path)
bool isTagFile(const char *path)
bool fillFileHole() const
bool disableLooseWrite() const
bool disablePgExtend() const
bool allowMissingTags() const
virtual ssize_t pgWrite(void *, off_t, size_t, uint32_t *, uint64_t)
virtual int Fsync()
virtual ssize_t Write(const void *, off_t, size_t)
virtual int Ftruncate(unsigned long long)
virtual ssize_t pgRead(void *, off_t, size_t, uint32_t *, uint64_t)
virtual ssize_t ReadV(XrdOucIOVec *readV, int n)
virtual ssize_t Read(off_t, size_t)
int VerificationStatus()
virtual int Open(const char *, int, mode_t, XrdOucEnv &)
virtual int Close(long long *retsz=0)
virtual void Flush()
Flush filesystem cached pages for this file (used for checksums).
virtual ~XrdOssCsiFile()
static XrdSysMutex pumtx_
Definition: XrdOssCsi.hh:159
virtual int Fstat(struct stat *)
static std::unordered_map< std::string, std::shared_ptr< puMapItem_t > > pumap_
Definition: XrdOssCsi.hh:160
void aioWait()
Definition: XrdOssCsi.hh:126
virtual ssize_t ReadRaw(void *, off_t, size_t)
virtual ssize_t WriteV(XrdOucIOVec *writeV, int n)
XrdOssCsiPages * Pages()
Definition: XrdOssCsi.hh:140
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)
std::pair< off_t, off_t > Sizes_t
int truncate(XrdOssDF *, off_t, XrdOssCsiRangeGuard &)
int LockResetSizes(XrdOssDF *, off_t)
int FetchRange(XrdOssDF *, const void *, off_t, size_t, uint32_t *, uint64_t, XrdOssCsiRangeGuard &)
void LockTrackinglen(XrdOssCsiRangeGuard &, off_t, off_t, bool)
int TrackedSizesGet(Sizes_t &, bool)
static int pgWritePrelockCheck(const void *, off_t, size_t, const uint32_t *, uint64_t)
int StoreRange(XrdOssDF *, const void *, off_t, size_t, uint32_t *, uint64_t, XrdOssCsiRangeGuard &)
int UpdateRange(XrdOssDF *, const void *, off_t, size_t, XrdOssCsiRangeGuard &)
bool IsReadOnly() const
int VerifyRange(XrdOssDF *, const void *, off_t, size_t, XrdOssCsiRangeGuard &)
static std::unique_ptr< XrdOucEnv > tagOpenEnv(const XrdOssCsiConfig &, XrdOucEnv &)
Definition: XrdOssCsi.cc:470
XrdOssDF * successor_
virtual int Fsync()
Definition: XrdOss.hh:172
virtual ssize_t WriteV(XrdOucIOVec *writeV, int wrvcnt)
Definition: XrdOss.cc:273
virtual int isCompressed(char *cxidp=0)
Definition: XrdOss.hh:215
virtual int Ftruncate(unsigned long long flen)
Definition: XrdOss.hh:192
virtual int Fstat(struct stat *buf)
Definition: XrdOss.hh:164
virtual ssize_t ReadRaw(void *buffer, off_t offset, size_t size)
Definition: XrdOss.hh:359
virtual int Close(long long *retsz=0)=0
virtual int Open(const char *path, int Oflag, mode_t Mode, XrdOucEnv &env)
Definition: XrdOss.hh:228
virtual ssize_t Read(off_t offset, size_t size)
Definition: XrdOss.hh:310
virtual void Flush()
Flush filesystem cached pages for this file (used for checksums).
Definition: XrdOss.hh:154
virtual ssize_t ReadV(XrdOucIOVec *readV, int rdvcnt)
Definition: XrdOss.cc:252
virtual ssize_t Write(const void *buffer, off_t offset, size_t size)
Definition: XrdOss.hh:385
virtual int Mkdir(const char *path, mode_t mode, int mkpath=0, XrdOucEnv *envP=0)=0
virtual XrdOssDF * newFile(const char *tident)=0
long long offset
Definition: XrdOucIOVec.hh:42