XRootD
Loading...
Searching...
No Matches
XrdOssArcCompose.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d O s s A r c C o m p o s e . c c */
4/* */
5/* (c) 2025 by the Board of Trustees of the Leland Stanford, Jr., University */
6/* All Rights Reserved */
7/* Produced by Andrew Hanushevsky for Stanford University under contract */
8/* DE-AC02-76-SFO0515 with the Department of Energy */
9/* */
10/* This file is part of the XRootD software suite. */
11/* */
12/* XRootD is free software: you can redistribute it and/or modify it under */
13/* the terms of the GNU Lesser General Public License as published by the */
14/* Free Software Foundation, either version 3 of the License, or (at your */
15/* option) any later version. */
16/* */
17/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20/* License for more details. */
21/* */
22/* You should have received a copy of the GNU Lesser General Public License */
23/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25/* */
26/* The copyright holder's institutional names and contributor's names may not */
27/* be used to endorse or promote products derived from this software without */
28/* specific prior written permission of the institution or contributor. */
29/******************************************************************************/
30
31#include <errno.h>
32#include <stdlib.h>
33#include <string.h>
34
35#include <sys/stat.h>
36
40
41#include "XrdOuc/XrdOucECMsg.hh"
42#include "XrdOuc/XrdOucEnv.hh"
43#include "XrdOuc/XrdOucProg.hh"
45
46#include "XrdSys/XrdSysE2T.hh"
47
48#ifndef ENOANO
49#define ENOANO 555
50#endif
51
52/******************************************************************************/
53/* S t a t i c O b j e c t s */
54/******************************************************************************/
55
56namespace XrdOssArcGlobals
57{
59extern XrdSysError Elog;
60extern thread_local XrdOucECMsg ecMsg;
61}
62using namespace XrdOssArcGlobals;
63
64/******************************************************************************/
65
66int XrdOssArcCompose::minLenDSN = 4;
67int XrdOssArcCompose::minLenFN = 4;
68
69/******************************************************************************/
70/* C o n s t r u c t o r */
71/******************************************************************************/
72
74 int& rc, bool isW, bool optfn)
75{
76 TraceInfo("Compose",0);
77
78// By conventioon, the env["ossarc.fn"] holds the file did and the path is
79// the dataset did both represented as paths. We only support two path
80// prefixes: 1) the archive prefix, and 2) the backup prefix. These are
81// removed but we track which one it was as they have different semantics.
82//
83
84// Determine the path type being referenced
85//
86 if (!strncmp(Config.arcvPathLFN, path, Config.arcvPathLEN))
87 {didType = isARC;
88 path += Config.arcvPathLEN;
89 }
90 else if (!strncmp(Config.arcvPathLFN, path, Config.arcvPathLEN-1)
91 && strlen(path) < (size_t)Config.arcvPathLEN)
92 {didType = isARC;
93 path += Config.arcvPathLEN-1;
94 }
95 else if (!strncmp(Config.bkupPathLFN, path, Config.bkupPathLEN))
96 {didType = isBKP;
97 path += Config.bkupPathLEN;
98 optfn = false; // ossarc.fn is required for /backup
99 }
100 else {rc = EDOM; return;} // This will forward the request
101
102// Get the dataset name and if there is no ENV, we can return as this is
103// only a construction for datasets.
104//
105 if ((rc = getDSN(path))) return;
106 if (!env) {rc = 0; return;}
107
108// Prepare for full construction (note: we know we have an env pointer)
109//
110 const char* theFN = 0;
111 bool fl2arc = false;
112 bool fscpDS = false;
113
114// This is our domain and that domain is strictly read/only
115//
116 if (isW) {rc = EROFS; return;}
117
118// Make sure we have an env file name to work with. This must come from the CGI
119// if /backup and from either the CGI path for /archive. We support optional
120// path specification to allow recursive copies of all dataset archive files.
121// Noe that we ignore the CGI if specified via the path.
122//
123 if (didType == isARC && isArcFile(path))
124 {const char* fn = rindex(path, '/');
125 if (fn && fn != path)
126 {theFN = fn + 1;
127 dsName.erase(dsName.find_last_of("/"));
128 }
129 }
130
131 if (!theFN && !(theFN = env->Get("ossarc.fn")))
132 {if (optfn) rc = 0;
133 else {rc = EINVAL;
134 ecMsg.Msg("Compose","CGI ossarc.fn=<target_fname> not specified");
135 }
136 return;
137 }
138
139// If the archive if the target then env filename may refer to a specific
140// archive or be a file that is to be used to determine the target archive.
141// Otherwise, it should refer to a single file backups so an arc file suffix
142// is disallowed as it makes to sense.
143//
144 if (isArcFile(theFN))
145 {if (didType == isBKP)
146 {rc = EINVAL;
147 ecMsg.Msg("Compose","Backup filename cannot refer to an archive");
148 return;
149 }
150 arName = theFN;
151 } else fl2arc = true;
152
153
154// Pick apart the filename if this is a backup reference or an indirect
155// archive reference as we will need the scope and file.
156//
157 if ((didType == isBKP) || fl2arc)
158 {const char* colon = index(theFN, ':');
159 if (!colon)
160 {flScope = dsScope;
161 flName = theFN;
162 fscpDS = true; // For tracing purposes
163 } else {
164 if (*theFN == ':')
165 {rc = EINVAL;
166 ecMsg.Msg("Compose","File scope not specified though implied");
167 return;
168 }
169 flScope.assign(theFN, colon - theFN);
170 flName = colon+1;
171 }
172 if ((int)flName.length() < minLenDSN)
173 {rc = EINVAL;
174 ecMsg.Msg("Compose", "Dataset name is too short");
175 return;
176 }
177 }
178
179// Do some debugging
180//
181 const char* atName[] = {"Archive", "Backup"};
182
183 DEBUG("Type="<<atName[didType]<<" f2a="<<fl2arc<<" fscpDF="<<fscpDS
184 <<" dsScope="<<dsScope<<" dsName="<<dsName
185 <<" flScope="<<flScope<<" flName="<<flName<<" arName="<<arName);
186
187// Set the archive name if we need to
188//
189 rc = (fl2arc ? SetarName() : 0);
190}
191
192/******************************************************************************/
193/* A r c M e m b e r */
194/******************************************************************************/
195
196int XrdOssArcCompose::ArcMember(char* buff, int blen)
197{
198 if (snprintf(buff, blen, "%s:%s", flScope.c_str(), flName.c_str()) >= blen)
199 {std::string fn = flScope + ":" + flName;
200 Elog.Emsg("Compose", ENAMETOOLONG, "generate archive member name "
201 "in dataset", dsName.c_str());
202 ecMsg.Msg("Compose", "Archive member name", fn.c_str(), "is too long");
203 return ENAMETOOLONG;
204 }
205 return 0;
206}
207
208/******************************************************************************/
209/* A r c P a t h */
210/******************************************************************************/
211
212int XrdOssArcCompose::ArcPath(char* buff, int blen, bool addafn)
213{
214 int n;
215
216// The path to the archive is <tape_path>/<scope>/<dataset_did>/<arc_fname>
217//
218 if (addafn) n = snprintf(buff, blen, "%s/%s/%s/%s", Config.tapePath,
219 dsScope.c_str(), dsName.c_str(), arName.c_str());
220 else n = snprintf(buff, blen, "%s/%s/%s", Config.tapePath,
221 dsScope.c_str(), dsName.c_str());
222
223// Verify that we did not truncate the path
224//
225 if (n >= blen)
226 {std::string dsn = dsScope + ":" + dsName;
227 Elog.Emsg("Compose", ENAMETOOLONG, "generate archive path for dataset",
228 dsn.c_str());
229 return ENAMETOOLONG;
230 }
231
232// All done
233//
234 return 0;
235}
236
237/******************************************************************************/
238/* D S N 2 D i r */
239/******************************************************************************/
240
241std::string XrdOssArcCompose::DSN2Dir(const char* dsn)
242{
243 std::string retdir(dsn);
244 int n = retdir.length();
245
246 for (int i = 0; i < n; i++) if (retdir[i] == '/') retdir[i] = '%';
247
248 return retdir;
249}
250
251/******************************************************************************/
252/* D i r 2 D S N */
253/******************************************************************************/
254
255std::string XrdOssArcCompose::Dir2DSN(const char* dir)
256{
257 std::string retdsn(dir);
258 int n = retdsn.length();
259
260 for (int i = 0; i < n; i++) if (retdsn[i] == '%') retdsn[i] = '/';
261
262 return retdsn;
263}
264
265/******************************************************************************/
266/* Private: g e t D S N */
267/******************************************************************************/
268
269int XrdOssArcCompose::getDSN(const char *path)
270{
271
272// At this point we must have a dataset name containing a scope name
273//
274 const char* colon = index(path, ':');
275 if (!colon || *path == ':')
276 {ecMsg.Msg("Compose", "Dataset scope not specified");
277 return EINVAL;
278 }
279 dsScope.assign(path, colon - path);
280
281// Verify that the dataset name is long enough
282//
283 if ((int)strlen(colon+1) < minLenDSN)
284 {ecMsg.Msg("Compose", "dataset name is too short");
285 return EINVAL;
286 }
287
288// The dataset name must not end with the arc file suffix
289//
290// if (isArcFile(colon+1))
291// {ecMsg.Msg("Compose", "Dataset name cannot refer to an archive");
292// return EINVAL;
293// }
294
295// Assign the dataset name
296//
297 dsName = colon+1;
298 return 0;
299}
300
301/******************************************************************************/
302/* i s A r c F i l e */
303/******************************************************************************/
304
305bool XrdOssArcCompose::isArcFile(const char *path)
306{
307 int n = strlen(path);
308
309// Is it's too short, it cannot end with ".zip" (or what it the suffix is)
310//
311 if (n <= Config.arfSfxLen) return false;
312
313// Verify the ending
314//
315 return !strcmp(Config.arfSfx, path+n-Config.arfSfxLen);
316}
317
318/******************************************************************************/
319/* i s A r c P a t h */
320/******************************************************************************/
321
322bool XrdOssArcCompose::isArcPath(const char *path)
323{
324 return !strncmp(Config.arcvPathLFN, path, Config.arcvPathLEN);
325}
326
327/******************************************************************************/
328/* i s B k p P a t h */
329/******************************************************************************/
330
331bool XrdOssArcCompose::isBkpPath(const char *path)
332{
333 return !strncmp(Config.bkupPathLFN, path, Config.bkupPathLEN);
334}
335
336/******************************************************************************/
337/* i s M i n e */
338/******************************************************************************/
339
340bool XrdOssArcCompose::isMine(const char *path)
341{
342 return !strncmp(Config.arcvPathLFN, path, Config.arcvPathLEN) ||
343 !strncmp(Config.bkupPathLFN, path, Config.bkupPathLEN);
344}
345
346/******************************************************************************/
347/* Private: S e t a r N a m e */
348/******************************************************************************/
349
350int XrdOssArcCompose::SetarName()
351{
352 TraceInfo("SetarName", 0);
353
354// Which is applicable to any type of object as it only wants to know
355// which archive file contains a particular dataset file. So, construct
356// the argument list to ask.
357//
358 std::string fName = flScope + (std::string)":" + flName;
359 const char* argV[] = {"which", Config.arFName, fName.c_str(),
360 dsScope.c_str(), dsName.c_str()};
361 int argC = sizeof(argV)/sizeof(char*);
362 int rc;
363
364// Do some tracing
365//
366 DEBUG("Running "<<Config.BkpUtilName<<' '<<argV[0]<<' '
367 <<argV[1]<<' '<<argV[2]<<' '<<argV[3]<<' '<<argV[4]);
368
369// Execute command, it should return a single line of output
370//
371 XrdOucStream cmdSup;
372 bool aOK = false;
373
374 if (!(rc = Config.BkpUtilProg->Run(&cmdSup, argV, argC)))
375 {char* resp;
376 if (cmdSup.GetLine() && (resp = cmdSup.GetToken()) && *resp)
377 {arName = resp;
378 aOK = *resp != '!';
379 DEBUG(resp<<" holds "<<dsScope<<':'<<dsName<<'['<<fName<<"]")
380 }
381 rc = Config.BkpUtilProg->RunDone(cmdSup);
382 }
383
384// Check if we have a result, that implies all went well enough
385//
386 if (aOK) return 0;
387
388// Diagnose the error
389//
390 if (arName == "!ENOENT") rc = ENOENT;
391 else if (arName == "!ENOANO") rc = ENOANO;
392 else if (!rc) rc = EPROTO;
393
394 fName += ';';
395 std::string dName = dsScope +':'+ dsName;
396
397 ecMsg.Msg("Compose", "Unable to determine name of the dataset",
398 dName.c_str(), "archive for", fName.c_str(),
399 (rc == ENOANO ? "dataset not backed up" : XrdSysE2T(rc)));
400 return rc;
401}
402
403/******************************************************************************/
404/* S t a t */
405/******************************************************************************/
406
407int XrdOssArcCompose::Stat(const char* Scope, const char* Name,
408 struct stat* Stat)
409{
410 TraceInfo("Stat", 0);
411
412// Setup argument list for a stat call
413//
414 const char* argV[] = {"stat", "cgi", Scope, Name};
415 int argC = sizeof(argV)/sizeof(char*);
416 int rc, rc2 = 0;
417
418// Do some tracing
419//
420 DEBUG("Running "<<Config.BkpUtilName<<' '<<argV[0]<<' '
421 <<argV[1]<<' '<<argV[2]<<' '<<argV[3]);
422
423// Execute command, it should return a single line of output
424//
425 XrdOucStream cmdSup;
426 bool aOK = false;
427
428 if (!(rc = Config.BkpUtilProg->Run(&cmdSup, argV, argC)))
429 {char* resp;
430 if (cmdSup.GetLine() && (resp = cmdSup.GetToken()) && *resp)
431 {if (*resp != '!')
432 {if (!(rc = StatDecode(*Stat, resp))) aOK = true;
433 else if (!strcmp("!ENOENT", resp)) rc = ENOENT;
434 else rc = EINVAL;
435 }
436 }
437 rc2 = Config.BkpUtilProg->RunDone(cmdSup);
438 }
439
440// Check if we have a result, that implies all went well enough
441//
442 if (aOK) return 0;
443 if (rc) return rc;
444 if (rc2) return rc2;
445 return EPROTO;
446}
447
448/******************************************************************************/
449/* Private: S t a t D e c o d e */
450/******************************************************************************/
451
452int XrdOssArcCompose::StatDecode(struct stat& Stat, const char* resp)
453{
454 TraceInfo("StatDecode", 0);
455 XrdOucEnv env(resp);
456 char* infoP;
457 long long val;
458 int n;
459
460 memset(&Stat, 0, sizeof(struct stat));
461
462 if (StatGet("uid", env, val)) Stat.st_uid = int(val);
463 else return EINVAL;
464
465 if (StatGet("gid", env, val)) Stat.st_gid = int(val);
466 else return EINVAL;
467
468 if (StatGet("size", env, val)) Stat.st_size = val;
469 else return EINVAL;
470
471 if (StatGet("atime", env, val)) Stat.st_atime = time_t(val);
472 else return EINVAL;
473 if (StatGet("mtime", env, val)) Stat.st_mtime = time_t(val);
474 else return EINVAL;
475 if (StatGet("ctime", env, val)) Stat.st_ctime = time_t(val);
476 else return EINVAL;
477
478// Set the mode
479//
480 if (!(infoP = env.Get("mode")))
481 {DEBUG("Missing 'mode' in stat resp '"<<env.Env(n)<<"'");
482 return EINVAL;
483 }
484
485// Set the correct mode
486//
487 Stat.st_mode = S_IRUSR | S_IRGRP;
488 if (!strcmp(infoP, "FILE")) Stat.st_mode |= S_IFREG;
489 else Stat.st_mode = S_IFDIR | S_IXUSR | S_IXGRP;
490
491// All done
492//
493 return 0;
494}
495
496/******************************************************************************/
497/* Private: S t a t G e t */
498/******************************************************************************/
499
500bool XrdOssArcCompose::StatGet(const char* var, XrdOucEnv& env, long long& val)
501{
502 TraceInfo("StatGet", 0);
503 const char* varval;
504 char* endval;
505 int n;
506
507// Fetch the value
508//
509 if (!(varval = env.Get(var)))
510 {DEBUG("Missing '"<<var<<"' in stat resp '"<<env.Env(n)<<"'");
511 return false;
512 }
513
514// Convert it to a long long
515//
516 val = strtoll(varval, &endval, 10);
517 if (*endval == 0) return true;
518
519// Document failure
520//
521 DEBUG("Invalid '"<<var<<'='<<varval<<"' in stat resp '"<<env.Env(n)<<"'");
522 return false;
523}
#define DEBUG(x)
struct stat Stat
Definition XrdCks.cc:49
#define ENOANO
#define TraceInfo(x, y)
#define stat(a, b)
Definition XrdPosix.hh:101
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:104
static bool isArcPath(const char *path)
static int Stat(const char *Scope, const char *Name, struct stat *Stat)
static bool isArcFile(const char *path)
XrdOssArcCompose(const char *path, XrdOucEnv *env, int &retc, bool isW=true, bool optfn=false)
static std::string Dir2DSN(const char *dir)
static std::string DSN2Dir(const char *dsn)
static bool isMine(const char *path)
static bool isBkpPath(const char *path)
int ArcMember(char *buff, int blen)
int ArcPath(char *buff, int blen, bool addafn=false)
const char * BkpUtilName
XrdOucProg * BkpUtilProg
std::string Msg()
char * Env(int &envlen)
Definition XrdOucEnv.hh:48
char * Get(const char *varname)
Definition XrdOucEnv.hh:69
int RunDone(XrdOucStream &cmd) const
int Run(XrdOucStream *Sp, const char *argV[], int argc=0, const char *envV[]=0) const
char * GetLine()
char * GetToken(int lowcase=0)
XrdOssArcConfig Config
Definition XrdOssArc.cc:68
XrdSysError Elog(0, "OssArc_")