XRootD
Loading...
Searching...
No Matches
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"
35#include "XrdOssCsiPages.hh"
36#include "XrdOssCsiRanges.hh"
37#include "XrdOuc/XrdOucCRC.hh"
38#include "XrdOuc/XrdOucEnv.hh"
39#include "XrdSfs/XrdSfsAio.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
58std::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//
63int 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
87void 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
108int 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
124int 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
185int 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
202int 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
285int 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
339ssize_t XrdOssCsiFile::Read(off_t offset, size_t blen)
340{
341 return successor_->Read(offset, blen);
342}
343
344ssize_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
359ssize_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
374ssize_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
403ssize_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
436ssize_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
474ssize_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
502ssize_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
551int 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
574int 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
586int 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}
XrdOucTrace OssCsiTrace
XrdOucTrace OssCsiTrace & 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)
virtual ssize_t pgWrite(void *, off_t, size_t, uint32_t *, uint64_t)
XrdOssCsiPages * Pages()
Definition XrdOssCsi.hh:140
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)
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
virtual ssize_t ReadRaw(void *, off_t, size_t)
virtual ssize_t WriteV(XrdOucIOVec *writeV, int n)
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)
XrdOssCsiPages(const std::string &fn, std::unique_ptr< XrdOssCsiTagstore > ts, bool wh, bool am, bool dpe, bool dlw, const char *)
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 &)
int VerifyRange(XrdOssDF *, const void *, off_t, size_t, XrdOssCsiRangeGuard &)
XrdOssCsiTagstoreFile(const std::string &fn, std::unique_ptr< XrdOssDF > fd, const char *tid)
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
long long offset