XRootD
Loading...
Searching...
No Matches
XrdXrootdXeq.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d X r o o t d X e q . c c */
4/* */
5/* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */
6/* Produced by Andrew Hanushevsky for Stanford University under contract */
7/* DE-AC02-76-SFO0515 with the Department of Energy */
8/* */
9/* This file is part of the XRootD software suite. */
10/* */
11/* XRootD is free software: you can redistribute it and/or modify it under */
12/* the terms of the GNU Lesser General Public License as published by the */
13/* Free Software Foundation, either version 3 of the License, or (at your */
14/* option) any later version. */
15/* */
16/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
17/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
18/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
19/* License for more details. */
20/* */
21/* You should have received a copy of the GNU Lesser General Public License */
22/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
23/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
24/* */
25/* The copyright holder's institutional names and contributor's names may not */
26/* be used to endorse or promote products derived from this software without */
27/* specific prior written permission of the institution or contributor. */
28/******************************************************************************/
29
30#include <cctype>
31#include <cstdio>
32#include <map>
33#include <memory>
34#include <string>
35#include <sys/time.h>
36#include <vector>
37
39#include "XrdSfs/XrdSfsFlags.hh"
40#include "XrdSys/XrdSysError.hh"
42#include "XrdSys/XrdSysTimer.hh"
43#include "XrdCks/XrdCksData.hh"
45#include "XrdOuc/XrdOucEnv.hh"
46#include "XrdOuc/XrdOucReqID.hh"
47#include "XrdOuc/XrdOucTList.hh"
51#include "XrdOuc/XrdOucUtils.hh"
55#include "XrdSys/XrdSysE2T.hh"
56#include "Xrd/XrdBuffer.hh"
57#include "Xrd/XrdInet.hh"
58#include "Xrd/XrdLinkCtl.hh"
76
77#include "XrdVersion.hh"
78
79#ifndef ENODATA
80#define ENODATA ENOATTR
81#endif
82
83#ifndef ETIME
84#define ETIME ETIMEDOUT
85#endif
86
87/******************************************************************************/
88/* G l o b a l s */
89/******************************************************************************/
90
92
93/******************************************************************************/
94/* L o c a l S t r u c t u r e s */
95/******************************************************************************/
96
98 {unsigned int Sid;
99 int Pid;
100 int FD;
101 unsigned int Inst;
102
105 };
106
107/******************************************************************************/
108/* L o c a l D e f i n e s */
109/******************************************************************************/
110
111namespace
112{
113
114const char *getTime()
115{
116static char buff[16];
117char tuff[8];
118struct timeval tv;
119struct tm *tmp;
120
121 if (gettimeofday(&tv, 0))
122 {perror("gettimeofday");
123 exit(255);
124 }
125 tmp = localtime(&tv.tv_sec);
126 if (!tmp)
127 {perror("localtime");
128 exit(255);
129 }
130 //012345678901234
131 if (strftime(buff, sizeof(buff), "%y%m%d:%H%M%S. ", tmp) <= 0)
132 {errno = EINVAL;
133 perror("strftime");
134 exit(255);
135 }
136
137 snprintf(tuff, sizeof(tuff), "%d", static_cast<int>(tv.tv_usec/100000));
138 buff[14] = tuff[0];
139 return buff;
140}
141
142// comment out genUEID as it is not used
143//
144
145//int genUEID()
146//{
147// static XrdSysMutex ueidMutex;
148// static int ueidVal = 1;
149// AtomicBeg(ueidMutex);
150// int n = AtomicInc(ueidVal);
151// AtomicEnd(ueidMutex);
152// return n;
153//}
154
155// Startup time
156// 012345670123456
157// yymmdd:hhmmss.t
158static const char *startUP = getTime();
159}
160
161/******************************************************************************/
162/* d o _ A u t h */
163/******************************************************************************/
164
165int XrdXrootdProtocol::do_Auth()
166{
168 XrdSecParameters *parm = 0;
169 XrdOucErrInfo eMsg;
170 const char *eText;
171 int rc, n;
172
173// Ignore authenticate requests if security turned off
174//
175 if (!CIA) return Response.Send();
176 cred.size = Request.header.dlen;
177 cred.buffer = argp->buff;
178
179// If we have no auth protocol or the current protocol is being changed by the
180// client (the client can do so at any time), try to get it. Track number of
181// times we got a protocol object as the read count (we will zero it out later).
182// The credtype change check is always done. While the credtype is consistent,
183// not all protocols provided this information in the past. So, old clients will
184// not necessarily be able to switch protocols mid-stream.
185//
186 if (!AuthProt
187 || strncmp(Entity.prot, (const char *)Request.auth.credtype,
188 sizeof(Request.auth.credtype)))
189 {if (AuthProt) AuthProt->Delete();
190 size_t size = sizeof(Request.auth.credtype);
191 strncpy(Entity.prot, (const char *)Request.auth.credtype, size);
192 if (!(AuthProt = CIA->getProtocol(Link->Host(), *(Link->AddrInfo()),
193 &cred, eMsg)))
194 {eText = eMsg.getErrText(rc);
195 eDest.Emsg("Xeq", "User authentication failed;", eText);
196 return Response.Send(kXR_AuthFailed, eText);
197 }
198 AuthProt->Entity.tident = AuthProt->Entity.pident = Link->ID;
199 numReads++;
200 }
201
202// Now try to authenticate the client using the current protocol
203//
204 if (!(rc = AuthProt->Authenticate(&cred, &parm, &eMsg))
205 && CIA->PostProcess(AuthProt->Entity, eMsg))
206 {rc = Response.Send(); Status &= ~XRD_NEED_AUTH; SI->Bump(SI->LoginAU);
207 AuthProt->Entity.ueid = mySID;
208 Client = &AuthProt->Entity; numReads = 0; strcpy(Entity.prot, "host");
209 if (TRACING(TRACE_AUTH)) Client->Display(eDest);
210 if (DHS) Protect = DHS->New4Server(*AuthProt,clientPV&XrdOucEI::uVMask);
211 if (Monitor.Logins() && Monitor.Auths()) MonAuth();
212 if (!logLogin(true)) return -1;
213 return rc;
214 }
215
216// If we need to continue authentication, tell the client as much
217//
218 if (rc > 0)
219 {TRACEP(LOGIN, "more auth requested; sz=" <<(parm ? parm->size : 0));
220 if (parm) {rc = Response.Send(kXR_authmore, parm->buffer, parm->size);
221 delete parm;
222 return rc;
223 }
224 eDest.Emsg("Xeq", "Security requested additional auth w/o parms!");
225 return Response.Send(kXR_ServerError,"invalid authentication exchange");
226 }
227
228// Authentication failed. We will delete the authentication object and zero
229// out the pointer. We can do this without any locks because this section is
230// single threaded relative to a connection. To prevent guessing attacks, we
231// wait a variable amount of time if there have been 3 or more tries.
232//
233 if (AuthProt) {AuthProt->Delete(); AuthProt = 0;}
234 if ((n = numReads - 2) > 0) XrdSysTimer::Snooze(n > 5 ? 5 : n);
235
236// We got an error, bail out.
237//
238 SI->Bump(SI->AuthBad);
239 eText = eMsg.getErrText(rc);
240 eDest.Emsg("Xeq", "User authentication failed;", eText);
241 return Response.Send(kXR_AuthFailed, eText);
242}
243
244/******************************************************************************/
245/* d o _ B i n d */
246/******************************************************************************/
247
248int XrdXrootdProtocol::do_Bind()
249{
250 XrdXrootdSessID *sp = (XrdXrootdSessID *)Request.bind.sessid;
252 XrdLink *lp;
253 int i, pPid, rc;
254 char buff[64], *cp, *dp;
255
256// Update misc stats count
257//
258 SI->Bump(SI->miscCnt);
259
260// Check if binds need to occur on a TLS connection.
261//
262 if ((doTLS & Req_TLSData) && !isTLS && !Link->hasBridge())
263 return Response.Send(kXR_TLSRequired, "bind requires TLS");
264
265// Find the link we are to bind to
266//
267 if (sp->FD <= 0 || !(lp = XrdLinkCtl::fd2link(sp->FD, sp->Inst)))
268 return Response.Send(kXR_NotFound, "session not found");
269
270// The link may have escaped so we need to hold this link and try again
271//
272 lp->Hold(1);
273 if (lp != XrdLinkCtl::fd2link(sp->FD, sp->Inst))
274 {lp->Hold(0);
275 return Response.Send(kXR_NotFound, "session just closed");
276 }
277
278// Get the protocol associated with the link
279//
280 if (!(pp=dynamic_cast<XrdXrootdProtocol *>(lp->getProtocol()))||lp != pp->Link)
281 {lp->Hold(0);
282 return Response.Send(kXR_ArgInvalid, "session protocol not xroot");
283 }
284
285// Verify that the parent protocol is fully logged in
286//
287 if (!(pp->Status & XRD_LOGGEDIN) || (pp->Status & XRD_NEED_AUTH))
288 {lp->Hold(0);
289 return Response.Send(kXR_ArgInvalid, "session not logged in");
290 }
291
292// Verify that the bind is valid for the requestor
293//
294 if (sp->Pid != myPID || sp->Sid != pp->mySID)
295 {lp->Hold(0);
296 return Response.Send(kXR_ArgInvalid, "invalid session ID");
297 }
298
299// For now, verify that the request is comming from the same host
300//
301 if (strcmp(Link->Host(), lp->Host()))
302 {lp->Hold(0);
303 return Response.Send(kXR_NotAuthorized, "cross-host bind not allowed");
304 }
305
306// We need to hold the parent's stream mutex to prevent inspection or
307// modification of other parallel binds that may occur
308//
309 XrdSysMutexHelper smHelper(pp->streamMutex);
310
311// Find a slot for this path in parent protocol
312//
313 for (i = 1; i < maxStreams && pp->Stream[i]; i++) {}
314 if (i >= maxStreams)
315 {lp->Hold(0);
316 return Response.Send(kXR_NoMemory, "bind limit exceeded");
317 }
318
319// Link this protocol to the parent
320//
321 pp->Stream[i] = this;
322 Stream[0] = pp;
323 PathID = i;
324
325// Construct a login name for this bind session
326//
327 cp = strdup(lp->ID);
328 if ( (dp = rindex(cp, '@'))) *dp = '\0';
329 if (!(dp = rindex(cp, '.'))) pPid = 0;
330 else {*dp++ = '\0'; pPid = strtol(dp, (char **)NULL, 10);}
331 Link->setID(cp, pPid);
332 free(cp);
333 CapVer = pp->CapVer;
335 boundRecycle = new XrdSysSemaphore(0);
336 clientPV = pp->clientPV;
338
339// Check if we need to enable packet marking for this stream
340//
341 if (pp->pmDone)
342 {pmDone = true;
343 if (pp->pmHandle) pmHandle = PMark->Begin(*(Link->AddrInfo()),
344 *(pp->pmHandle), Link->ID);
345 }
346
347// Document the bind
348//
349 smHelper.UnLock();
350 sprintf(buff, "FD %d#%d bound", Link->FDnum(), i);
351 eDest.Log(SYS_LOG_01, "Xeq", buff, lp->ID);
352
353// Get the required number of parallel I/O objects
354//
356
357// There are no errors possible at this point unless the response fails
358//
359 buff[0] = static_cast<char>(i);
360 if (!(rc = Response.Send(kXR_ok, buff, 1))) rc = -EINPROGRESS;
361
362// Return but keep the link disabled
363//
364 lp->Hold(0);
365 return rc;
366}
367
368/******************************************************************************/
369/* d o _ C h k P n t */
370/* */
371/* Resides in XrdXrootdXeqChkPnt.cc */
372/******************************************************************************/
373
374/******************************************************************************/
375/* d o _ c h m o d */
376/******************************************************************************/
377
378int XrdXrootdProtocol::do_Chmod()
379{
380 int mode, rc;
381 char *opaque;
382 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
383
384// Check for static routing
385//
386 STATIC_REDIRECT(RD_chmod);
387
388// Unmarshall the data
389//
390 mode = mapMode((int)ntohs(Request.chmod.mode));
391 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Modifying", argp->buff);
392 if (!Squash(argp->buff)) return vpEmsg("Modifying", argp->buff);
393
394// Preform the actual function
395//
396 rc = osFS->chmod(argp->buff, (XrdSfsMode)mode, myError, CRED, opaque);
397 TRACEP(FS, "chmod rc=" <<rc <<" mode=" <<Xrd::oct1 <<mode <<' ' <<argp->buff);
398 if (SFS_OK == rc) return Response.Send();
399
400// An error occurred
401//
402 return fsError(rc, XROOTD_MON_CHMOD, myError, argp->buff, opaque);
403}
404
405/******************************************************************************/
406/* d o _ C K s u m */
407/******************************************************************************/
408
409int XrdXrootdProtocol::do_CKsum(int canit)
410{
411 char *opaque;
412 char *algT = JobCKT, *args[6];
413 int rc;
414
415// Check for static routing
416//
417 STATIC_REDIRECT(RD_chksum);
418
419// Check if we support this operation
420//
421 if (!JobCKT || (!JobLCL && !JobCKS))
422 return Response.Send(kXR_Unsupported, "query chksum is not supported");
423
424// Prescreen the path
425//
426 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Check summing", argp->buff);
427 if (!Squash(argp->buff)) return vpEmsg("Check summing", argp->buff);
428
429// If this is a cancel request, do it now
430//
431 if (canit)
432 {if (JobCKS) JobCKS->Cancel(argp->buff, &Response);
433 return Response.Send();
434 }
435
436// Check if multiple checksums are supported and if so, pre-process
437//
438 if (JobCKCGI && opaque && *opaque)
439 {char cksT[64];
440 algT = getCksType(opaque, cksT, sizeof(cksT));
441 if (!algT)
442 {char ebuf[1024];
443 snprintf(ebuf, sizeof(ebuf), "%s checksum not supported.", cksT);
444 return Response.Send(kXR_ServerError, ebuf);
445 }
446 }
447
448// If we are allowed to locally query the checksum to avoid computation, do it
449//
450 if (JobLCL && (rc = do_CKsum(algT, argp->buff, opaque)) <= 0) return rc;
451
452// Just make absolutely sure we can continue with a calculation
453//
454 if (!JobCKS)
455 return Response.Send(kXR_ServerError, "Logic error computing checksum.");
456
457// Check if multiple checksums are supported and construct right argument list
458// We make a concession to a wrongly placed setfsuid/gid plugin. Fortunately,
459// it only needs to know user's name but that can come from another plugin.
460//
461 std::string keyval; // Contents will be copied prior to return!
462 if (JobCKCGI > 1 || JobLCL)
463 {args[0] = algT;
464 args[1] = algT;
465 args[2] = argp->buff;
466 args[3] = const_cast<char *>(Client->tident);
467 if (Client->eaAPI->Get(std::string("request.name"), keyval) && !keyval.empty())
468 args[4] = const_cast<char *>(keyval.c_str());
469 else if (Client->name) args[4] = Client->name;
470 else args[4] = 0;
471 args[5] = 0;
472 } else {
473 args[0] = algT;
474 args[1] = argp->buff;
475 args[2] = 0;
476 }
477
478// Preform the actual function
479//
480 return JobCKS->Schedule(argp->buff, (const char **)args, &Response,
481 ((CapVer & kXR_vermask) >= kXR_ver002 ? 0 : JOB_Sync));
482}
483
484/******************************************************************************/
485
486int XrdXrootdProtocol::do_CKsum(char *algT, const char *Path, char *Opaque)
487{
488 static char Space = ' ';
489 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
490 int CKTLen = strlen(algT);
491 int ec, rc = osFS->chksum(XrdSfsFileSystem::csGet, algT, Path,
492 myError, CRED, Opaque);
493 const char *csData = myError.getErrText(ec);
494
495// Diagnose any hard errors
496//
497 if (rc) return fsError(rc, 0, myError, Path, Opaque);
498
499// Return result if it is actually available
500//
501 if (*csData)
502 {if (*csData == '!') return Response.Send(csData+1);
503 struct iovec iov[4] = {{0,0}, {algT, (size_t)CKTLen}, {&Space, 1},
504 {(char *)csData, strlen(csData)+1}};
505 return Response.Send(iov, 4);
506 }
507
508// Diagnose soft errors
509//
510 if (!JobCKS)
511 {const char *eTxt[2] = {JobCKT, " checksum not available."};
512 myError.setErrInfo(0, eTxt, 2);
513 return Response.Send(kXR_ChkSumErr, myError.getErrText());
514 }
515
516// Return indicating that we should try calculating the checksum
517//
518 return 1;
519}
520
521/******************************************************************************/
522/* d o _ C l o n e */
523/******************************************************************************/
524
525int XrdXrootdProtocol::do_Clone()
526{
527 XrdXrootdFHandle fh(Request.clone.fhandle);
528 XrdXrootdFile* fP;
529 XrdSfsFile *dstFile, *srcFile = 0;
530 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
531 int clVecNum, clVecLen = Request.header.dlen;
532 int currFH =- -1;
533
534// Make sure we can do this operation
535//
537 return Response.Send(kXR_Unsupported, "file cloning is not supported");
538
539// Make sure target file is actually open
540//
541 if (!FTab || !(fP = FTab->Get(fh.handle)))
542 return Response.Send(kXR_FileNotOpen,
543 "clone does not refer to an open dest file");
544 dstFile = fP->XrdSfsp;
545
546// Compute number of elements in the clone vector and make sure we have no
547// partial elements.
548//
549 clVecNum = clVecLen / sizeof(XrdProto::clone_list);
550 if ( (clVecNum <= 0) ||
551 (clVecNum*(int)sizeof(XrdProto::clone_list) != clVecLen) )
552 return Response.Send(kXR_ArgInvalid, "Clone vector is invalid");
553
554// Make sure that we can copy the clone vector to our local stack. We must impose
555// a limit on it's size. We do this to be able to reuse the data buffer to
556// prevent cross-cpu memory cache synchronization.
557//
558 if (clVecNum > XrdProto::maxClonesz)
559 return Response.Send(kXR_ArgTooLong, "Clone vector is too long");
560
561// Allocate a new clone vector
562//
563 std::vector<XrdOucCloneSeg> clVec(clVecNum);
564
565// Setup for clone vector initialisation
566//
567 XrdProto::clone_list* clList = (XrdProto::clone_list *)argp->buff;
568
569// Create new clone vector
570//
571 for (int i = 0; i < clVecNum; i++)
572 {fh.Set(clList[i].srcFH);
573 if (!srcFile || currFH != fh.handle)
574 {currFH = fh.handle;
575 if (!(fP = FTab->Get(currFH)))
576 return Response.Send(kXR_FileNotOpen,
577 "clone does not refer to an open src file");
578 srcFile = fP->XrdSfsp;
579 }
580
581 int fdNum;
582 if (srcFile->fctl(SFS_FCTL_GETFD, 0, myError) != SFS_OK)
583 {int ecode;
584 const char *eMsg = myError.getErrText(ecode);
585 const int rc = XProtocol::mapError(ecode);
586 return Response.Send((XErrorCode)rc, eMsg);
587 }
588 else fdNum = myError.getErrInfo();
589
590 if (fdNum<0)
591 return Response.Send(kXR_FileNotOpen,
592 "clone does not refer to an open src file");
593
594 clVec[i].srcFD = fdNum;
595 n2hll(clList[i].srcOffs, clVec[i].srcOffs);
596 n2hll(clList[i].srcLen, clVec[i].srcLen);
597 n2hll(clList[i].dstOffs, clVec[i].dstOffs);
598 }
599
600// Now execute the clone request
601//
602 int rc = dstFile->Clone(clVec);
603 if (SFS_OK != rc) return fsError(rc, 0, dstFile->error, 0, 0);
604
605 return Response.Send();
606}
607
608/******************************************************************************/
609/* d o _ C l o s e */
610/******************************************************************************/
611
612int XrdXrootdProtocol::do_Close()
613{
614 static XrdXrootdCallBack closeCB("close", XROOTD_MON_CLOSE);
615 XrdXrootdFile *fp;
616 XrdXrootdFHandle fh(Request.close.fhandle);
617 int rc;
618 bool doDel = true;
619
620// Keep statistics
621//
622 SI->Bump(SI->miscCnt);
623
624// Find the file object
625//
626 if (!FTab || !(fp = FTab->Get(fh.handle)))
627 return Response.Send(kXR_FileNotOpen,
628 "close does not refer to an open file");
629
630// Serialize the file to make sure all references due to async I/O and parallel
631// stream operations have completed.
632//
633 fp->Serialize();
634
635// If the file has a fob then it was subject to pgwrite and if uncorrected
636// checksum errors exist do a forced close. This will trigger POSC or a restore.
637//
638 if (fp->pgwFob && !do_PgClose(fp, rc))
639 {FTab->Del((Monitor.Files() ? Monitor.Agent : 0), fh.handle, true);
640 numFiles--;
641 return rc;
642 }
643
644// Setup the callback to allow close() to return SFS_STARTED so we can defer
645// the response to the close request as it may be a lengthy operation. In
646// this case the argument is the actual file pointer and the link reference
647// is recorded in the file object.
648//
649 fp->cbArg = ReqID.getID();
650 fp->XrdSfsp->error.setErrCB(&closeCB, (unsigned long long)fp);
651
652// Add a reference count to the file in case the close will be deferred. In
653// the deferred case the reference is used to prevent the callback from
654// deleting the file until we have done necessary processing of the object
655// during its removal from the open table.
656//
657 fp->Ref(1);
658
659// Do an explicit close of the file here; check for exceptions. Stall requests
660// leave the file open as there will be a retry. Otherwise, we remove the
661// file from our open table but a "started" return defers the the delete.
662//
663 rc = fp->XrdSfsp->close();
664 TRACEP(FS, " fh=" <<fh.handle <<" close rc=" <<rc);
665 if (rc == SFS_STARTED) doDel = false;
666 else {fp->Ref(-1);
667 if (rc >= SFS_STALL)
668 return fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
669 }
670
671// Before we potentially delete the file handle in FTab->Del, generate the
672// appropriate error code (if necessary). Note that we delay the call
673// to Response.Send() in the successful case to avoid holding on to the lock
674// while the response is sent.
675//
676 int retval = 0;
677 if (SFS_OK != rc) retval = fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
678
679// Delete the file from the file table. If the file object is deleted then it
680// will unlock the file In all cases, final monitoring records will be produced.
681//
682 FTab->Del((Monitor.Files() ? Monitor.Agent : 0), fh.handle, doDel);
683 numFiles--;
684 if (!doDel) fp->Ref(-1);
685
686// Send back the right response
687//
688 if (SFS_OK == rc) return Response.Send();
689 return retval;
690}
691
692/******************************************************************************/
693/* d o _ D i r l i s t */
694/******************************************************************************/
695
696int XrdXrootdProtocol::do_Dirlist()
697{
698 int bleft, rc = 0, dlen, cnt = 0;
699 char *opaque, *buff, ebuff[4096];
700 const char *dname;
701 XrdSfsDirectory *dp;
702 bool doDig;
703
704// Check if we are digging for data
705//
706 doDig = (digFS && SFS_LCLROOT(argp->buff));
707
708// Check for static routing
709//
710 if (!doDig) {STATIC_REDIRECT(RD_dirlist);}
711
712// Prescreen the path
713//
714 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Listing", argp->buff);
715 if (!doDig && !Squash(argp->buff))return vpEmsg("Listing", argp->buff);
716
717// Get a directory object
718//
719 if (doDig) dp = digFS->newDir(Link->ID, Monitor.Did);
720 else dp = osFS->newDir(Link->ID, Monitor.Did);
721
722// Make sure we have the object
723//
724 if (!dp)
725 {snprintf(ebuff,sizeof(ebuff)-1,"Insufficient memory to open %s",argp->buff);
726 eDest.Emsg("Xeq", ebuff);
727 return Response.Send(kXR_NoMemory, ebuff);
728 }
729
730// First open the directory
731//
733 if ((rc = dp->open(argp->buff, CRED, opaque)))
734 {rc = fsError(rc, XROOTD_MON_OPENDIR, dp->error, argp->buff, opaque);
735 delete dp;
736 return rc;
737 }
738
739// Check if the caller wants stat information as well
740//
741 if (Request.dirlist.options[0] & (kXR_dstat | kXR_dcksm))
742 return do_DirStat(dp, ebuff, opaque);
743
744// Start retreiving each entry and place in a local buffer with a trailing new
745// line character (the last entry will have a null byte). If we cannot fit a
746// full entry in the buffer, send what we have with an OKSOFAR and continue.
747// This code depends on the fact that a directory entry will never be longer
748// than sizeof( ebuff)-1; otherwise, an infinite loop will result. No errors
749// are allowed to be reflected at this point.
750//
751 dname = 0;
752 do {buff = ebuff; bleft = sizeof(ebuff);
753 while(dname || (dname = dp->nextEntry()))
754 {dlen = strlen(dname);
755 if (dlen > 2 || dname[0] != '.' || (dlen == 2 && dname[1] != '.'))
756 {if ((bleft -= (dlen+1)) < 0) break;
757 strcpy(buff, dname); buff += dlen; *buff = '\n'; buff++; cnt++;
758 }
759 dname = 0;
760 }
761 if (dname) rc = Response.Send(kXR_oksofar, ebuff, buff-ebuff);
762 } while(!rc && dname);
763
764// Send the ending packet if we actually have one to send
765//
766 if (!rc)
767 {if (ebuff == buff) rc = Response.Send();
768 else {*(buff-1) = '\0';
769 rc = Response.Send((void *)ebuff, buff-ebuff);
770 }
771 }
772
773// Close the directory
774//
775 dp->close();
776 delete dp;
777 if (!rc) {TRACEP(FS, "dirlist entries=" <<cnt <<" path=" <<argp->buff);}
778 return rc;
779}
780
781/******************************************************************************/
782/* d o _ D i r S t a t */
783/******************************************************************************/
784
785int XrdXrootdProtocol::do_DirStat(XrdSfsDirectory *dp, char *pbuff,
786 char *opaque)
787{
788 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
789 struct stat Stat;
790 char *buff, *dLoc, *algT = 0;
791 const char *csData, *dname;
792 int bleft, rc = 0, dlen, cnt = 0, statSz = 160;
793 bool manStat;
794 struct {char ebuff[8192]; char epad[512];} XB;
795
796// Preprocess checksum request. If we don't support checksums or if the
797// requested checksum type is not supported, ignore it.
798//
799 if ((Request.dirlist.options[0] & kXR_dcksm) && JobLCL)
800 {char cksT[64];
801 algT = getCksType(opaque, cksT, sizeof(cksT));
802 if (!algT)
803 {char ebuf[1024];
804 snprintf(ebuf, sizeof(ebuf), "%s checksum not supported.", cksT);
805 return Response.Send(kXR_ServerError, ebuf);
806 }
807 statSz += XrdCksData::NameSize + (XrdCksData::ValuSize*2) + 8;
808 }
809
810// We always return stat information, see if we can use autostat
811//
812 manStat = (dp->autoStat(&Stat) != SFS_OK);
813
814// Construct the path to the directory as we will be asking for stat calls
815// if the interface does not support autostat or returning checksums.
816//
817 if (manStat || algT)
818 {strcpy(pbuff, argp->buff);
819 dlen = strlen(pbuff);
820 if (pbuff[dlen-1] != '/') {pbuff[dlen] = '/'; dlen++;}
821 dLoc = pbuff+dlen;
822 } else dLoc = 0;
823
824// The initial leadin is a "dot" entry to indicate to the client that we
825// support the dstat option (older servers will not do that). It's up to the
826// client to issue individual stat requests in that case.
827//
828 memset(&Stat, 0, sizeof(Stat));
829 strcpy(XB.ebuff, ".\n0 0 0 0\n");
830 buff = XB.ebuff+10; bleft = sizeof(XB.ebuff)-10;
831
832// Start retreiving each entry and place in a local buffer with a trailing new
833// line character (the last entry will have a null byte). If we cannot fit a
834// full entry in the buffer, send what we have with an OKSOFAR and continue.
835// This code depends on the fact that a directory entry will never be longer
836// than sizeof( ebuff)-1; otherwise, an infinite loop will result. No errors
837// are allowed to be reflected at this point.
838//
839 dname = 0;
840 do {while(dname || (dname = dp->nextEntry()))
841 {dlen = strlen(dname);
842 if (dlen > 2 || dname[0] != '.' || (dlen == 2 && dname[1] != '.'))
843 {if ((bleft -= (dlen+1)) < 0 || bleft < statSz) break;
844 if (dLoc) strcpy(dLoc, dname);
845 if (manStat)
846 {rc = osFS->stat(pbuff, &Stat, myError, CRED, opaque);
847 if (rc == SFS_ERROR && myError.getErrInfo() == ENOENT)
848 {dname = 0; continue;}
849 if (rc != SFS_OK)
850 return fsError(rc, XROOTD_MON_STAT, myError,
851 argp->buff, opaque);
852 }
853 strcpy(buff, dname); buff += dlen; *buff = '\n'; buff++; cnt++;
854 dlen = StatGen(Stat, buff, sizeof(XB.epad));
855 bleft -= dlen; buff += (dlen-1);
856 if (algT)
857 {int ec = osFS->chksum(XrdSfsFileSystem::csGet, algT,
858 pbuff, myError, CRED, opaque);
859 csData = myError.getErrText();
860 if (ec != SFS_OK || !(*csData) || *csData == '!')
861 csData = "none";
862 int n = snprintf(buff,sizeof(XB.epad)," [ %s:%s ]",
863 algT, csData);
864 buff += n; bleft -= n;
865 }
866 *buff = '\n'; buff++;
867 }
868 dname = 0;
869 }
870 if (dname)
871 {rc = Response.Send(kXR_oksofar, XB.ebuff, buff-XB.ebuff);
872 buff = XB.ebuff; bleft = sizeof(XB.ebuff);
873 TRACEP(FS, "dirstat sofar n=" <<cnt <<" path=" <<argp->buff);
874 }
875 } while(!rc && dname);
876
877// Send the ending packet if we actually have one to send
878//
879 if (!rc)
880 {if (XB.ebuff == buff) rc = Response.Send();
881 else {*(buff-1) = '\0';
882 rc = Response.Send((void *)XB.ebuff, buff-XB.ebuff);
883 }
884 }
885
886// Close the directory
887//
888 dp->close();
889 delete dp;
890 if (!rc) {TRACEP(FS, "dirstat entries=" <<cnt <<" path=" <<argp->buff);}
891 return rc;
892}
893
894/******************************************************************************/
895/* d o _ E n d s e s s */
896/******************************************************************************/
897
898int XrdXrootdProtocol::do_Endsess()
899{
900 XrdXrootdSessID *sp, sessID;
901 int rc;
902
903// Update misc stats count
904//
905 SI->Bump(SI->miscCnt);
906
907// Extract out the FD and Instance from the session ID
908//
909 sp = (XrdXrootdSessID *)Request.endsess.sessid;
910 memcpy((void *)&sessID.Pid, &sp->Pid, sizeof(sessID.Pid));
911 memcpy((void *)&sessID.FD, &sp->FD, sizeof(sessID.FD));
912 memcpy((void *)&sessID.Inst, &sp->Inst, sizeof(sessID.Inst));
913
914// Trace this request
915//
916 TRACEP(LOGIN, "endsess " <<sessID.Pid <<':' <<sessID.FD <<'.' <<sessID.Inst);
917
918// If this session id does not refer to us, ignore the request
919//
920 if (sessID.Pid != myPID) return Response.Send();
921
922// Terminate the indicated session, if possible. This could also be a self-termination.
923//
924 if ((sessID.FD == 0 && sessID.Inst == 0)
925 || !(rc = Link->Terminate(0, sessID.FD, sessID.Inst))) return -1;
926
927// Trace this request
928//
929 TRACEP(LOGIN, "endsess " <<sessID.Pid <<':' <<sessID.FD <<'.' <<sessID.Inst
930 <<" rc=" <<rc <<" (" <<XrdSysE2T(rc < 0 ? -rc : EAGAIN) <<")");
931
932// Return result. We only return obvious problems (exclude ESRCH and EPIPE).
933//
934 if (rc > 0)
935 return (rc = Response.Send(kXR_wait, rc, "session still active")) ? rc:1;
936
937 if (rc == -EACCES)return Response.Send(kXR_NotAuthorized, "not session owner");
938 if (rc == -ETIME) return Response.Send(kXR_Cancelled,"session not ended");
939
940 return Response.Send();
941}
942
943/******************************************************************************/
944/* d o _ F A t t r */
945/* */
946/* Resides in XrdXrootdXeqFAttr.cc */
947/******************************************************************************/
948
949/******************************************************************************/
950/* d o _ g p F i l e */
951/******************************************************************************/
952
953int XrdXrootdProtocol::do_gpFile()
954{
955// int gopts, buffsz;
956
957// Keep Statistics (TO DO: differentiate get vs put)
958//
959 SI->Bump(SI->getfCnt);
960// SI->Bump(SI->putfCnt);
961
962// Check if gpfile need to occur on a TLS connection
963//
964 if ((doTLS & Req_TLSGPFile) && !isTLS && !Link->hasBridge())
965 return Response.Send(kXR_TLSRequired, "gpfile requires TLS");
966
967 return Response.Send(kXR_Unsupported, "gpfile request is not supported");
968}
969
970/******************************************************************************/
971/* d o _ L o c a t e */
972/******************************************************************************/
973
974int XrdXrootdProtocol::do_Locate()
975{
976 static XrdXrootdCallBack locCB("locate", XROOTD_MON_LOCATE);
977 int rc, opts, fsctl_cmd = SFS_FSCTL_LOCATE;
978 char *opaque = 0, *Path, *fn = argp->buff, opt[8], *op=opt;
979 XrdOucErrInfo myError(Link->ID,&locCB,ReqID.getID(),Monitor.Did,clientPV);
980 bool doDig = false;
981
982// Unmarshall the data
983//
984 opts = (int)ntohs(Request.locate.options);
985
986// Map the options
987//
988 if (opts & kXR_nowait) {fsctl_cmd |= SFS_O_NOWAIT; *op++ = 'i';}
989 if (opts & kXR_refresh) {fsctl_cmd |= SFS_O_RESET; *op++ = 's';}
990 if (opts & kXR_force ) {fsctl_cmd |= SFS_O_FORCE; *op++ = 'f';}
991 if (opts & kXR_prefname){fsctl_cmd |= SFS_O_HNAME; *op++ = 'n';}
992 if (opts & kXR_compress){fsctl_cmd |= SFS_O_RAWIO; *op++ = 'u';}
993 if (opts & kXR_4dirlist){fsctl_cmd |= SFS_O_DIRLIST;*op++ = 'D';}
994 *op = '\0';
995 TRACEP(FS, "locate " <<opt <<' ' <<fn);
996
997// Check if this is a non-specific locate
998//
999 if (*fn != '*'){Path = fn;
1000 doDig = (digFS && SFS_LCLROOT(Path));
1001 }
1002 else if (*(fn+1)) {Path = fn+1;
1003 doDig = (digFS && SFS_LCLROOT(Path));
1004 }
1005 else {Path = 0;
1006 fn = XPList.Next()->Path();
1007 fsctl_cmd |= SFS_O_TRUNC;
1008 }
1009
1010// Check for static routing
1011//
1012 if (!doDig) {STATIC_REDIRECT(RD_locate);}
1013
1014// Prescreen the path
1015//
1016 if (Path)
1017 {if (rpCheck(Path, &opaque)) return rpEmsg("Locating", Path);
1018 if (!doDig && !Squash(Path))return vpEmsg("Locating", Path);
1019 }
1020
1021// Preform the actual function. For regular Fs add back any opaque info
1022//
1023 if (doDig) rc = digFS->fsctl(fsctl_cmd, fn, myError, CRED);
1024 else {if (opaque)
1025 {int n = strlen(argp->buff); argp->buff[n] = '?';
1026 if ((argp->buff)+n != opaque-1)
1027 memmove(&argp->buff[n+1], opaque, strlen(opaque)+1);
1028 }
1029 rc = osFS->fsctl(fsctl_cmd, fn, myError, CRED);
1030 }
1031 TRACEP(FS, "rc=" <<rc <<" locate " <<fn);
1032 return fsError(rc, (doDig ? 0 : XROOTD_MON_LOCATE), myError, Path, opaque);
1033}
1034
1035/******************************************************************************/
1036/* d o _ L o g i n */
1037/*.x***************************************************************************/
1038
1039int XrdXrootdProtocol::do_Login()
1040{
1041 XrdXrootdSessID sessID;
1042 XrdNetAddrInfo *addrP;
1043 int i, pid, rc, sendSID = 0;
1044 char uname[sizeof(Request.login.username)+1];
1045
1046// Keep Statistics
1047//
1048 SI->Bump(SI->LoginAT);
1049
1050// Check if login need to occur on a TLS connection
1051//
1052 if ((doTLS & Req_TLSLogin) && !isTLS && !Link->hasBridge())
1053 {const char *emsg = "login requires TLS be enabled";
1054 if (!ableTLS)
1055 {emsg = "login requires TLS support";
1056 eDest.Emsg("Xeq","login requires TLS but",Link->ID,"is incapable.");
1057 }
1058 return Response.Send(kXR_TLSRequired, emsg);
1059 }
1060
1061// Unmarshall the pid and construct username using the POSIX.1-2008 standard
1062//
1063 pid = (int)ntohl(Request.login.pid);
1064 strncpy(uname, (const char *)Request.login.username, sizeof(uname)-1);
1065 uname[sizeof(uname)-1] = 0;
1066 XrdOucUtils::Sanitize(uname);
1067
1068// Make sure the user is not already logged in
1069//
1070 if (Status) return Response.Send(kXR_InvalidRequest,
1071 "duplicate login; already logged in");
1072
1073// Establish the ID for this link
1074//
1075 Link->setID(uname, pid);
1076 CapVer = Request.login.capver[0];
1077
1078// Establish the session ID if the client can handle it (protocol version > 0)
1079//
1080 if ((i = (CapVer & kXR_vermask)))
1081 {sessID.FD = Link->FDnum();
1082 sessID.Inst = Link->Inst();
1083 sessID.Pid = myPID;
1084 mySID = getSID();
1085 sessID.Sid = mySID;
1086 sendSID = 1;
1087 if (!clientPV)
1088 { if (i >= kXR_ver004) clientPV = (int)0x0310;
1089 else if (i == kXR_ver003) clientPV = (int)0x0300;
1090 else if (i == kXR_ver002) clientPV = (int)0x0290;
1091 else if (i == kXR_ver001) clientPV = (int)0x0200;
1092 else clientPV = (int)0x0100;
1093 }
1095 if (Request.login.ability & kXR_fullurl)
1097 if (Request.login.ability & kXR_lclfile)
1099 if (Request.login.ability & kXR_multipr)
1101 if (Request.login.ability & kXR_readrdok)
1103 if (Request.login.ability & kXR_hasipv64)
1105 if (Request.login.ability & kXR_redirflags)
1107 if (Request.login.ability2 & kXR_ecredir )
1109 }
1110
1111// Mark the client as IPv4 if they came in as IPv4 or mapped IPv4 we can only
1112// return IPv4 addresses. Of course, if the client is dual-stacked then we
1113// simply indicate the client can accept either (the client better be honest).
1114//
1115 addrP = Link->AddrInfo();
1116 if (addrP->isIPType(XrdNetAddrInfo::IPv4) || addrP->isMapped())
1118// WORKAROUND: XrdCl 4.0.x often identifies worker nodes as being IPv6-only.
1119// Rather than breaking a significant number of our dual-stack workers, we
1120// automatically denote IPv6 connections as also supporting IPv4 - regardless
1121// of what the remote client claims. This was fixed in 4.3.x but we can't
1122// tell release differences until 4.5 when we can safely ignore this as we
1123// also don't want to misidentify IPv6-only clients either.
1124 else if (i < kXR_ver004 && XrdInet::GetAssumeV4())
1126
1127// Mark the client as being on a private net if the address is private
1128//
1129 if (addrP->isPrivate()) {clientPV |= XrdOucEI::uPrip; rdType = 1;}
1130 else rdType = 0;
1131
1132// Get the security token for this link. We will either get a token, a null
1133// string indicating host-only authentication, or a null indicating no
1134// authentication. We can then optimize of each case.
1135//
1136 if (CIA)
1137 {const char *pp=CIA->getParms(i, Link->AddrInfo());
1138 if (pp && i ) {if (!sendSID) rc = Response.Send((void *)pp, i);
1139 else {struct iovec iov[3];
1140 iov[1].iov_base = (char *)&sessID;
1141 iov[1].iov_len = sizeof(sessID);
1142 iov[2].iov_base = (char *)pp;
1143 iov[2].iov_len = i;
1144 rc = Response.Send(iov,3,int(i+sizeof(sessID)));
1145 }
1147 }
1148 else {rc = (sendSID ? Response.Send((void *)&sessID, sizeof(sessID))
1149 : Response.Send());
1150 Status = XRD_LOGGEDIN; SI->Bump(SI->LoginUA);
1151 }
1152 }
1153 else {rc = (sendSID ? Response.Send((void *)&sessID, sizeof(sessID))
1154 : Response.Send());
1155 Status = XRD_LOGGEDIN; SI->Bump(SI->LoginUA);
1156 }
1157
1158// We always allow at least host-based authentication. This may be over-ridden
1159// should strong authentication be enabled. Allocation of the protocol object
1160// already supplied the protocol name and the host name. We supply the tident
1161// and the connection details in addrInfo.
1162//
1163 Entity.tident = Entity.pident = Link->ID;
1164 Entity.addrInfo = Link->AddrInfo();
1165 Client = &Entity;
1166
1167// Check if we need to process a login environment
1168//
1169 if (Request.login.dlen > 8)
1170 {XrdOucEnv loginEnv(argp->buff+1, Request.login.dlen-1);
1171 char *rnumb = loginEnv.Get("xrd.rn");
1172 char *cCode = loginEnv.Get("xrd.cc");
1173 char *tzVal = loginEnv.Get("xrd.tz");
1174 char *appXQ = loginEnv.Get("xrd.appname");
1175 char *aInfo = loginEnv.Get("xrd.info");
1176 int tzNum = (tzVal ? atoi(tzVal) : 0);
1177 if (cCode && *cCode && tzNum >= -12 && tzNum <= 14)
1178 {XrdNetAddrInfo::LocInfo locInfo;
1179 locInfo.Country[0] = cCode[0]; locInfo.Country[1] = cCode[1];
1180 locInfo.TimeZone = tzNum & 0xff;
1181 Link->setLocation(locInfo);
1182 }
1183 if (Monitor.Ready() && (appXQ || aInfo))
1184 {char apBuff[1024];
1185 snprintf(apBuff, sizeof(apBuff), "&R=%s&x=%s&y=%s&I=%c",
1186 (rnumb ? rnumb : ""),
1187 (appXQ ? appXQ : ""), (aInfo ? aInfo : ""),
1188 (clientPV & XrdOucEI::uIPv4 ? '4' : '6'));
1189 Entity.moninfo = strdup(apBuff);
1190 }
1191
1192 if (rnumb)
1193 {int majr, minr, pchr;
1194 if (sscanf(rnumb, "v%d.%d.%d", &majr, &minr, &pchr) == 3)
1195 clientRN = (majr<<16) | ((minr<<8) | pchr);
1196 else if (sscanf(rnumb, "v%d-%*x", &majr) == 1) clientRN = -1;
1197 }
1198 if (appXQ) AppName = strdup(appXQ);
1199 }
1200
1201// Allocate a monitoring object, if needed for this connection
1202//
1203 if (Monitor.Ready())
1204 {Monitor.Register(Link->ID, Link->Host(), "xroot", mySID);
1205 if (Monitor.Logins() && (!Monitor.Auths() || !(Status & XRD_NEED_AUTH)))
1206 {Monitor.Report(Entity.moninfo);
1207 if (Entity.moninfo) {free(Entity.moninfo); Entity.moninfo = 0;}
1208 Entity.secMon = &Monitor;
1209 }
1210 }
1211
1212// Complete the rquestID object
1213//
1214 ReqID.setID(Request.header.streamid, Link->FDnum(), Link->Inst());
1215
1216// Document this login
1217//
1218 if (!(Status & XRD_NEED_AUTH) && !logLogin()) return -1;
1219 return rc;
1220}
1221
1222/******************************************************************************/
1223/* d o _ M k d i r */
1224/******************************************************************************/
1225
1226int XrdXrootdProtocol::do_Mkdir()
1227{
1228 int mode, rc;
1229 char *opaque;
1230 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
1231
1232// Check for static routing
1233//
1234 STATIC_REDIRECT(RD_mkdir);
1235
1236// Unmarshall the data
1237//
1238 mode = mapMode((int)ntohs(Request.mkdir.mode)) | S_IRWXU;
1239 if (Request.mkdir.options[0] & static_cast<unsigned char>(kXR_mkdirpath))
1240 mode |= SFS_O_MKPTH;
1241 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Creating", argp->buff);
1242 if (!Squash(argp->buff)) return vpEmsg("Creating", argp->buff);
1243
1244// Preform the actual function
1245//
1246 rc = osFS->mkdir(argp->buff, (XrdSfsMode)mode, myError, CRED, opaque);
1247 TRACEP(FS, "rc=" <<rc <<" mkdir " <<Xrd::oct1 <<mode <<' ' <<argp->buff);
1248 if (SFS_OK == rc) return Response.Send();
1249
1250// An error occurred
1251//
1252 return fsError(rc, XROOTD_MON_MKDIR, myError, argp->buff, opaque);
1253}
1254
1255/******************************************************************************/
1256/* d o _ M v */
1257/******************************************************************************/
1258
1259int XrdXrootdProtocol::do_Mv()
1260{
1261 int rc;
1262 char *oldp, *newp, *Opaque, *Npaque;
1263 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
1264
1265// Check for static routing
1266//
1267 STATIC_REDIRECT(RD_mv);
1268
1269// Find the space separator between the old and new paths
1270//
1271 oldp = newp = argp->buff;
1272 if (Request.mv.arg1len)
1273 {int n = ntohs(Request.mv.arg1len);
1274 if (n < 0 || n >= Request.mv.dlen || *(argp->buff+n) != ' ')
1275 return Response.Send(kXR_ArgInvalid, "invalid path specification");
1276 *(oldp+n) = 0;
1277 newp += n+1;
1278 } else {
1279 while(*newp && *newp != ' ') newp++;
1280 if (*newp) {*newp = '\0'; newp++;
1281 while(*newp && *newp == ' ') newp++;
1282 }
1283 }
1284
1285// Get rid of relative paths and multiple slashes
1286//
1287 if (rpCheck(oldp, &Opaque)) return rpEmsg("Renaming", oldp);
1288 if (rpCheck(newp, &Npaque)) return rpEmsg("Renaming to", newp);
1289 if (!Squash(oldp)) return vpEmsg("Renaming", oldp);
1290 if (!Squash(newp)) return vpEmsg("Renaming to", newp);
1291
1292// Check if new path actually specified here
1293//
1294 if (*newp == '\0')
1295 Response.Send(kXR_ArgMissing, "new path specified for mv");
1296
1297// Preform the actual function
1298//
1299 rc = osFS->rename(oldp, newp, myError, CRED, Opaque, Npaque);
1300 TRACEP(FS, "rc=" <<rc <<" mv " <<oldp <<' ' <<newp);
1301 if (SFS_OK == rc) return Response.Send();
1302
1303// An error occurred
1304//
1305 return fsError(rc, XROOTD_MON_MV, myError, oldp, Opaque);
1306}
1307
1308/******************************************************************************/
1309/* d o _ O f f l o a d */
1310/******************************************************************************/
1311
1312int XrdXrootdProtocol::do_Offload(int (XrdXrootdProtocol::*Invoke)(),int pathID)
1313{
1314 XrdSysSemaphore isAvail(0);
1316 XrdXrootdPio *pioP;
1317 int rc;
1318 kXR_char streamID[2];
1319
1320// Verify that the path actually exists (note we will have the stream lock)
1321//
1322 if (!(pp = VerifyStream(rc, pathID))) return rc;
1323
1324// Grab the stream ID
1325//
1326 Response.StreamID(streamID);
1327
1328// Try to schedule this operation. In order to maximize the I/O overlap, we
1329// will wait until the stream gets control and will have a chance to start
1330// reading from the network. We handle refs for consistency.
1331//
1332 do{if (!pp->isActive)
1333 {pp->IO = IO;
1334 pp->myBlen = 0;
1335 pp->Resume = &XrdXrootdProtocol::do_OffloadIO;
1336 pp->ResumePio= Invoke;
1337 pp->isActive = true;
1338 pp->newPio = true;
1339 pp->reTry = &isAvail;
1340 pp->Response.Set(streamID);
1341 pp->streamMutex.UnLock();
1342 Link->setRef(1);
1343 IO.File->Ref(1);
1344 Sched->Schedule((XrdJob *)(pp->Link));
1345 isAvail.Wait();
1346 return 0;
1347 }
1348
1349 if ((pioP = pp->pioFree)) break;
1350 pp->reTry = &isAvail;
1351 pp->streamMutex.UnLock();
1352 TRACEP(FSZIO, "busy path " <<pathID <<" offs=" <<IO.Offset);
1353 isAvail.Wait();
1354 TRACEP(FSZIO, "retry path " <<pathID <<" offs=" <<IO.Offset);
1355 pp->streamMutex.Lock();
1356 if (pp->isNOP)
1357 {pp->streamMutex.UnLock();
1358 return Response.Send(kXR_ArgInvalid, "path ID is not connected");
1359 }
1360 } while(1);
1361
1362// Fill out the queue entry and add it to the queue
1363//
1364 pp->pioFree = pioP->Next; pioP->Next = 0;
1365 pioP->Set(Invoke, IO, streamID);
1366 IO.File->Ref(1);
1367 if (pp->pioLast) pp->pioLast->Next = pioP;
1368 else pp->pioFirst = pioP;
1369 pp->pioLast = pioP;
1370 pp->streamMutex.UnLock();
1371 return 0;
1372}
1373
1374/******************************************************************************/
1375/* d o _ O f f l o a d I O */
1376/******************************************************************************/
1377
1378int XrdXrootdProtocol::do_OffloadIO()
1379{
1380 XrdXrootdPio *pioP;
1381 int rc;
1382
1383// Entry implies that we just got scheduled and are marked as active. Hence
1384// we need to post the session thread so that it can pick up the next request.
1385//
1386 streamMutex.Lock();
1387 isLinkWT = false;
1388 if (newPio)
1389 {newPio = false;
1390 if (reTry) {reTry->Post(); reTry = 0;}
1391 TRACEP(FSZIO, "dispatch new I/O path " <<PathID <<" offs=" <<IO.Offset);
1392 }
1393
1394// Perform all I/O operations on a parallel stream
1395//
1396 if (!isNOP)
1397 do {streamMutex.UnLock();
1398 rc = (*this.*ResumePio)();
1399 streamMutex.Lock();
1400
1401 if (rc > 0 && !isNOP)
1402 {ResumePio = Resume;
1403 Resume = &XrdXrootdProtocol::do_OffloadIO;
1404 isLinkWT = true;
1405 streamMutex.UnLock();
1406 return rc;
1407 }
1408
1409 IO.File->Ref(-1); // Note: File was ref'd when request was queued
1410 if (rc || isNOP || !(pioP = pioFirst)) break;
1411 if (!(pioFirst = pioP->Next)) pioLast = 0;
1412
1413 IO = pioP->IO;
1414 ResumePio = pioP->ResumePio;
1415 Response.Set(pioP->StreamID);
1416 pioP->Next = pioFree; pioFree = pioP;
1417 if (reTry) {reTry->Post(); reTry = 0;}
1418 } while(1);
1419 else {rc = -1; IO.File->Ref(-1);}
1420
1421// There are no pending operations or the link died
1422//
1423 if (rc) isNOP = true;
1424 isActive = false;
1425 Stream[0]->Link->setRef(-1);
1426 if (reTry) {reTry->Post(); reTry = 0;}
1427 if (endNote) endNote->Signal();
1428 streamMutex.UnLock();
1429 TRACEP(FSZIO, "offload complete path "<<PathID<<" virt rc=" <<rc);
1430 return (rc ? rc : -EINPROGRESS);
1431}
1432
1433/******************************************************************************/
1434/* d o _ O p e n */
1435/******************************************************************************/
1436
1437namespace
1438{
1439struct OpenHelper
1440 {XrdSfsFile *fp;
1441 XrdXrootdFile *xp;
1442 XrdXrootdFileLock *Locker;
1443 const char *path;
1444 char mode;
1445 bool isOK;
1446
1447 OpenHelper(XrdXrootdFileLock *lkP, const char *fn)
1448 : fp(0), xp(0), Locker(lkP), path(fn), mode(0),
1449 isOK(false) {}
1450
1451 ~OpenHelper()
1452 {if (!isOK)
1453 {if (xp) delete xp; // Deletes fp & unlocks
1454 else {if (fp) delete fp;
1455 if (mode) Locker->Unlock(path,mode);
1456 }
1457 }
1458 }
1459 };
1460}
1461
1462int XrdXrootdProtocol::do_Open()
1463{
1464 static XrdXrootdCallBack openCB("open file", XROOTD_MON_OPENR);
1465 int fhandle;
1466 int rc, mode, opts, optt, openopts, compchk = 0;
1467 int popt;
1468 char *opaque, usage, ebuff[2048], opC;
1469 bool doDig, doforce = false, isAsync = false, doClone = false;
1470 char *fn = argp->buff, opt[24], *op=opt;
1471 XrdSfsFile *fp;
1472 XrdXrootdFile *xp, *sameFS = 0;
1473 struct stat statbuf;
1474 struct ServerResponseBody_Open myResp;
1475 int resplen = sizeof(myResp.fhandle);
1476 struct iovec IOResp[3]; // Note that IOResp[0] is completed by Response
1477 int retStat = 0;
1478
1479// Keep Statistics
1480//
1481 SI->Bump(SI->openCnt);
1482
1483// Unmarshall the data
1484//
1485 mode = (int)ntohs(Request.open.mode);
1486 opts = (int)ntohs(Request.open.options);
1487 optt = (int)ntohs(Request.open.optiont);
1488
1489// Make sutre that retstat and retstatx are processed correctly
1490//
1491 if (optt & kXR_retstatx) opts |= kXR_retstat;
1492
1493// Map the mode and options
1494//
1495 mode = mapMode(mode) | S_IRUSR | S_IWUSR; usage = 'r';
1496 if (opts & kXR_open_read)
1497 {openopts = SFS_O_RDONLY; *op++ = 'r'; opC = XROOTD_MON_OPENR;}
1498 else if (opts & kXR_open_updt)
1499 {openopts = SFS_O_RDWR; *op++ = 'u'; usage = 'w';
1500 opC = XROOTD_MON_OPENW;}
1501 else if (opts & kXR_open_wrto)
1502 {openopts = SFS_O_WRONLY; *op++ = 'o'; usage = 'w';
1503 opC = XROOTD_MON_OPENW;}
1504 else {openopts = SFS_O_RDONLY; *op++ = 'r'; opC = XROOTD_MON_OPENR;}
1505
1506 if (opts & kXR_new)
1507 {openopts |= SFS_O_CREAT; *op++ = 'n'; opC = XROOTD_MON_OPENC;
1508 if (opts & kXR_replica) {*op++ = '+';
1509 openopts |= SFS_O_REPLICA;
1510 }
1511 // Up until 3/28/19 we mistakenly used kXR_mkdir instead of
1512 // kXR_mkpath to allow path creation. That meant, path creation was
1513 // allowed if _mkpath|_async|_refresh|_open_apnd|_replica were set.
1514 // Since the client has always turned on _async that meant that
1515 // path creation was always enabled. We continue this boondogle
1516 // using the correct flag for backward compatibility reasons, sigh.
1517 //
1518 if (opts & (kXR_mkpath | kXR_async))
1519 {*op++ = 'm';
1520 mode |= SFS_O_MKPTH;
1521 }
1522 }
1523 else if (opts & kXR_delete)
1524 {openopts = SFS_O_TRUNC; *op++ = 'd'; opC = XROOTD_MON_OPENW;
1525 if (opts & (kXR_mkpath | kXR_async))
1526 {*op++ = 'm';
1527 mode |= SFS_O_MKPTH;
1528 }
1529 }
1530 if (opts & kXR_compress)
1531 {openopts |= SFS_O_RAWIO; *op++ = 'c'; compchk = 1;}
1532 if (opts & kXR_force) {*op++ = 'f'; doforce = true;}
1533 if ((opts & kXR_async || as_force) && as_aioOK)
1534 {*op++ = 'a'; isAsync = true;}
1535 if (opts & kXR_refresh) {*op++ = 's'; openopts |= SFS_O_RESET;
1536 SI->Bump(SI->Refresh);
1537 }
1538 if (opts & kXR_retstat) {*op++ = 't'; retStat = 1;}
1539 if (opts & kXR_posc) {*op++ = 'p'; openopts |= SFS_O_POSC;}
1540 if (opts & kXR_seqio) {*op++ = 'S'; openopts |= SFS_O_SEQIO;}
1541 if (optt & kXR_samefs || optt & kXR_dup)
1542 {XrdXrootdFHandle fh(Request.open.fhtemplt);
1543 if (!(fsFeatures & XrdSfs::hasFICL))
1544 return Response.Send(kXR_Unsupported,(optt & kXR_dup) ?
1545 "file cloning is not supported" :
1546 "colocating with a specified file is not supported");
1547 if (optt & kXR_dup)
1548 {if (usage != 'w') return Response.Send(kXR_ArgInvalid,
1549 "cloned file is not being opened R/W");
1550 {*op++ = 'K'; doClone = true;}
1551 }
1552 if (!(opts & kXR_new)) return Response.Send(kXR_ArgInvalid,
1553 "file must be opened as a new file in order to colocate");
1554 if (openopts &= SFS_O_CREAT) {*op++ = 'L'; openopts |= SFS_O_CREATAT;}
1555
1556 if (!FTab || !(sameFS = FTab->Get(fh.handle)))
1557 return Response.Send(kXR_FileNotOpen,
1558 "file template does not refer to an open file");
1559 }
1560
1561 *op = '\0';
1562
1563// Do some tracing, avoid exposing any security token in the URL
1564//
1565 if (TRACING(TRACE_FS))
1566 {char* cgiP = index(fn, '?');
1567 if (cgiP) *cgiP = 0;
1568 TRACEP(FS, "open " <<opt <<' ' <<fn);
1569 if (cgiP) *cgiP = '?';
1570 }
1571
1572// Check if opaque data has been provided
1573//
1574 if (rpCheck(fn, &opaque)) return rpEmsg("Opening", fn);
1575
1576// Check if this is a local dig type file
1577//
1578 doDig = (digFS && SFS_LCLPATH(fn));
1579
1580// Validate the path/req type and then check if static redirection applies
1581//
1582 if (doDig) {popt = XROOTDXP_NOLK; opC = 0;}
1583 else {int ropt = -1;
1584 if (!(popt = Squash(fn))) return vpEmsg("Opening", fn);
1585 if (Route[RD_open1].Host[rdType])
1586 ropt = RPList.Validate(fn);
1587 else
1588 if (Route[RD_openw].Host[rdType] && ('w' == usage || strchr(op, 'd')))
1589 ropt = RD_openw;
1590 if (ropt > 0)
1591 return Response.Send(
1592 kXR_redirect, Route[ropt].Port[rdType],
1593 Route[ropt].Host[rdType]
1594 );
1595 }
1596
1597// Add the multi-write option if this path supports it
1598//
1599 if (popt & XROOTDXP_NOMWCHK) openopts |= SFS_O_MULTIW;
1600
1601// Construct an open helper to release resources should we exit due to an error.
1602//
1603 OpenHelper oHelp(Locker, fn);
1604
1605// Lock this file
1606//
1607 if (!(popt & XROOTDXP_NOLK))
1608 {if ((rc = Locker->Lock(fn, usage, doforce)))
1609 {const char *who;
1610 if (rc > 0) who = (rc > 1 ? "readers" : "reader");
1611 else { rc = -rc;
1612 who = (rc > 1 ? "writers" : "writer");
1613 }
1614 snprintf(ebuff, sizeof(ebuff)-1,
1615 "%s file %s is already opened by %d %s; open denied.",
1616 ('r' == usage ? "Input" : "Output"), fn, rc, who);
1617 eDest.Emsg("Xeq", ebuff);
1618 return Response.Send(kXR_FileLocked, ebuff);
1619 } else oHelp.mode = usage;
1620 }
1621
1622// Get a file object
1623//
1624 if (doDig) fp = digFS->newFile(Link->ID, Monitor.Did);
1625 else fp = osFS->newFile(Link->ID, Monitor.Did);
1626
1627// Make sure we got one
1628//
1629 if (!fp)
1630 {snprintf(ebuff, sizeof(ebuff)-1,"Insufficient memory to open %s",fn);
1631 eDest.Emsg("Xeq", ebuff);
1632 return Response.Send(kXR_NoMemory, ebuff);
1633 }
1634 oHelp.fp = fp;
1635
1636// The open is elegible for a deferred response, indicate we're ok with that
1637// unless a clone is required. Then this needs to be done synchrnously.
1638//
1639 if (!doClone)
1640 {fp->error.setErrCB(&openCB, ReqID.getID());
1641 fp->error.setUCap(clientPV);
1642 }
1643
1644// If TPC opens require TLS but this is not a TLS connection, prohibit TPC
1645//
1646 if ((doTLS & Req_TLSTPC) && !isTLS && !Link->hasBridge())
1647 openopts|= SFS_O_NOTPC;
1648
1649// If needed add the colocation information. This is the filesystem in
1650// which the new file should be created.
1651//
1652 std::string oinfo(opaque ? opaque : "");
1653 if ((openopts & SFS_O_CREATAT) == SFS_O_CREATAT)
1654 {std::string coloc = sameFS->XrdSfsp->FName();
1655 coloc = "oss.coloc=" + XrdOucUtils::UrlEncode(coloc);
1656 oinfo += (!oinfo.empty() ? "&" : "") + coloc;
1657 }
1658
1659// Open the file
1660//
1661 if ((rc = fp->open(fn, (XrdSfsFileOpenMode)openopts,
1662 (mode_t)mode, CRED, oinfo.c_str())))
1663 return fsError(rc, opC, fp->error, fn, opaque);
1664
1665// If file needs to be cloned, do so now
1666//
1667 if (doClone && (rc = fp->Clone(*(sameFS->XrdSfsp))))
1668 return fsError(rc, opC, fp->error, fn, opaque);
1669
1670// Obtain a hyper file object
1671//
1672 xp = new XrdXrootdFile(Link->ID, fn, fp, usage, isAsync, &statbuf);
1673 if (!xp)
1674 {snprintf(ebuff, sizeof(ebuff)-1, "Insufficient memory to open %s", fn);
1675 eDest.Emsg("Xeq", ebuff);
1676 return Response.Send(kXR_NoMemory, ebuff);
1677 }
1678 oHelp.xp = xp;
1679
1680// Serialize the link
1681//
1682 Link->Serialize();
1683 *ebuff = '\0';
1684
1685// Create a file table for this link if it does not have one
1686//
1687 if (!FTab) FTab = new XrdXrootdFileTable(Monitor.Did);
1688
1689// Insert this file into the link's file table
1690//
1691 if (!FTab || (fhandle = FTab->Add(xp)) < 0)
1692 {snprintf(ebuff, sizeof(ebuff)-1, "Insufficient memory to open %s", fn);
1693 eDest.Emsg("Xeq", ebuff);
1694 return Response.Send(kXR_NoMemory, ebuff);
1695 }
1696
1697// If the file supports exchange buffering, supply it with the object
1698//
1699 if (fsFeatures & XrdSfs::hasSXIO) fp->setXio(this);
1700
1701// Document forced opens
1702//
1703 if (doforce)
1704 {int rdrs, wrtrs;
1705 Locker->numLocks(fn, rdrs, wrtrs);
1706 if (('r' == usage && wrtrs) || ('w' == usage && rdrs) || wrtrs > 1)
1707 {snprintf(ebuff, sizeof(ebuff)-1,
1708 "%s file %s forced opened with %d reader(s) and %d writer(s).",
1709 ('r' == usage ? "Input" : "Output"), fn, rdrs, wrtrs);
1710 eDest.Emsg("Xeq", ebuff);
1711 }
1712 }
1713
1714// Determine if file is compressed
1715//
1716 memset(&myResp, 0, sizeof(myResp));
1717 if (!compchk) resplen = sizeof(myResp.fhandle);
1718 else {int cpsize;
1719 fp->getCXinfo((char *)myResp.cptype, cpsize);
1720 myResp.cpsize = static_cast<kXR_int32>(htonl(cpsize));
1721 resplen = sizeof(myResp);
1722 }
1723
1724// If client wants a stat in open, return the stat information
1725//
1726 if (retStat)
1727 {retStat = StatGen(statbuf, ebuff, sizeof(ebuff));
1728 IOResp[1].iov_base = (char *)&myResp; IOResp[1].iov_len = sizeof(myResp);
1729 IOResp[2].iov_base = ebuff; IOResp[2].iov_len = retStat;
1730 resplen = sizeof(myResp) + retStat;
1731 }
1732
1733// If we are monitoring, send off a path to dictionary mapping (must try 1st!)
1734//
1735 if (Monitor.Files())
1736 {xp->Stats.FileID = Monitor.MapPath(fn);
1738 Monitor.Agent->Open(xp->Stats.FileID, statbuf.st_size);
1739 }
1740
1741// Since file monitoring is deprecated, a dictid may not have been assigned.
1742// But if fstream monitoring is enabled it will assign the dictid.
1743//
1744 if (Monitor.Fstat())
1745 XrdXrootdMonFile::Open(&(xp->Stats), fn, Monitor.Did, usage == 'w');
1746
1747// Insert the file handle
1748//
1749 memcpy((void *)myResp.fhandle,(const void *)&fhandle,sizeof(myResp.fhandle));
1750 numFiles++;
1751
1752// If packet marking is enabled, notify that we have potentially started data.
1753// We also need to extend the marking to any associated streams.
1754//
1755 int eCode, aCode;
1756 if (PMark && !pmDone)
1757 {streamMutex.Lock();
1758 pmDone = true;
1759 if ((pmHandle = PMark->Begin(*Client, fn, opaque, AppName)))
1760 for (int i = 1; i < maxStreams; i++)
1761 {if (Stream[i] && !(Stream[i]->pmDone))
1762 {Stream[i]->pmDone = true;
1763 Stream[i]->pmHandle =
1764 PMark->Begin(*(Stream[i]->Link->AddrInfo()),
1765 *pmHandle, Stream[i]->Link->ID);
1766 }
1767 }
1768 streamMutex.UnLock();
1769
1770 if (pmHandle && Monitor.Logins() && pmHandle->getEA(eCode, aCode))
1771 Monitor.Report(eCode, aCode);
1772 } else {
1773 if (!pmDone && Monitor.Logins()
1774 && XrdNetPMark::getEA(opaque, eCode, aCode))
1775 {Monitor.Report(eCode, aCode); pmDone = true;}
1776 }
1777
1778// Respond (failure is not an option now)
1779//
1780 oHelp.isOK = true;
1781 if (retStat) return Response.Send(IOResp, 3, resplen);
1782 else return Response.Send((void *)&myResp, resplen);
1783}
1784
1785/******************************************************************************/
1786/* d o _ P i n g */
1787/******************************************************************************/
1788
1789int XrdXrootdProtocol::do_Ping()
1790{
1791
1792// Keep Statistics
1793//
1794 SI->Bump(SI->miscCnt);
1795
1796// This is a basic nop
1797//
1798 return Response.Send();
1799}
1800
1801/******************************************************************************/
1802/* d o _ P r e p a r e */
1803/******************************************************************************/
1804
1805int XrdXrootdProtocol::do_Prepare(bool isQuery)
1806{
1807 static XrdXrootdCallBack prpCB("query", XROOTD_MON_QUERY);
1808
1809 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
1810
1811 XrdOucTokenizer pathlist(argp->buff);
1812 XrdOucTList *pFirst=0, *pP, *pLast = 0;
1813 XrdOucTList *oFirst=0, *oP, *oLast = 0;
1814 XrdOucTListHelper pHelp(&pFirst), oHelp(&oFirst);
1815 XrdXrootdPrepArgs pargs(0, 0);
1816 XrdSfsPrep fsprep;
1817
1818 int rc, pathnum = 0;
1819 char reqid[128], nidbuff[512], *path, *opaque, *prpid = 0;
1820 unsigned short optX = ntohs(Request.prepare.optionX);
1821 char opts;
1822 bool isCancel, isEvict, isPrepare;
1823
1824// Check if this is an evict request (similar to stage)
1825//
1826 isEvict = (optX & kXR_evict) != 0;
1827
1828// Establish what we are really doing here
1829//
1830 if (isQuery)
1831 {opts = 0;
1832 isCancel = false;
1833 } else {
1834 if (Request.prepare.options & kXR_cancel)
1835 {opts = 0;
1836 isCancel = true;
1837 } else {
1838 opts = (isEvict ? 0 : Request.prepare.options);
1839 isCancel = false;
1840 }
1841 }
1842 isPrepare = !(isCancel || isQuery);
1843
1844// Apply prepare limits, as necessary.
1845//
1846 if (isPrepare && (PrepareLimit >= 0) && (++PrepareCount > PrepareLimit)) {
1847 if (LimitError) {
1848 return Response.Send( kXR_overQuota,
1849 "Surpassed this connection's prepare limit.");
1850 } else {
1851 return Response.Send();
1852 }
1853 }
1854
1855// Check for static routing
1856//
1857 if ((opts & kXR_stage) || isCancel) {STATIC_REDIRECT(RD_prepstg);}
1858 STATIC_REDIRECT(RD_prepare);
1859
1860// Prehandle requests that must have a requestID. Otherwise, generate one.
1861// Note that prepare request id's have two formats. The external format is
1862// is qualifiaed by this host while the internal one removes the qualification.
1863// The internal one is only used for the native prepare implementation.
1864// To wit: prpid is the unqualified ID while reqid is the qualified one for
1865// generated id's while prpid is always the specified request id.
1866//
1867 if (isCancel || isQuery)
1868 {if (!(prpid = pathlist.GetLine()))
1869 return Response.Send(kXR_ArgMissing, "Prepare requestid not specified");
1870 fsprep.reqid = prpid;
1871 fsprep.opts = (isCancel ? Prep_CANCEL : Prep_QUERY);
1872 if (!PrepareAlt)
1873 {char hname[256];
1874 int hport;
1875 prpid = PrepID->isMine(prpid, hport, hname, sizeof(hname));
1876 if (!prpid)
1877 {if (!hport) return Response.Send(kXR_ArgInvalid,
1878 "Prepare requestid owned by an unknown server");
1879 TRACEI(REDIR, Response.ID() <<" redirecting prepare to "
1880 << hname <<':' <<hport);
1881 return Response.Send(kXR_redirect, hport, hname);
1882 }
1883 }
1884 } else {
1885 if (opts & kXR_stage)
1886 {prpid = PrepID->ID(reqid, sizeof(reqid));
1887 fsprep.reqid = reqid;
1888 fsprep.opts = Prep_STAGE | (opts & kXR_coloc ? Prep_COLOC : 0);
1889 } else {
1890 reqid[0]='*'; reqid[1]='\0';
1891 fsprep.reqid = prpid = reqid;
1892 fsprep.opts = (isEvict ? Prep_EVICT : 0);
1893 }
1894 }
1895
1896// Initialize the file system prepare arg list
1897//
1898 fsprep.paths = 0;
1899 fsprep.oinfo = 0;
1900 fsprep.notify = 0;
1901
1902// Cycle through all of the paths in the list
1903//
1904 while((path = pathlist.GetLine()))
1905 {if (rpCheck(path, &opaque)) return rpEmsg("Preparing", path);
1906 if (!Squash(path)) return vpEmsg("Preparing", path);
1907 pP = new XrdOucTList(path, pathnum);
1908 (pLast ? (pLast->next = pP) : (pFirst = pP)); pLast = pP;
1909 oP = new XrdOucTList(opaque, 0);
1910 (oLast ? (oLast->next = oP) : (oFirst = oP)); oLast = oP;
1911 pathnum++;
1912 }
1913 fsprep.paths = pFirst;
1914 fsprep.oinfo = oFirst;
1915
1916// We support callbacks but only for alternate prepare processing
1917//
1918 if (PrepareAlt) myError.setErrCB(&prpCB, ReqID.getID());
1919
1920// Process cancel requests here; they are simple at this point.
1921//
1922 if (isCancel)
1923 {if (SFS_OK != (rc = osFS->prepare(fsprep, myError, CRED)))
1924 return fsError(rc, XROOTD_MON_PREP, myError, path, 0);
1925 rc = Response.Send();
1927 return rc;
1928 }
1929
1930// Process query requests here; they are simple at this point.
1931//
1932 if (isQuery)
1933 {if (PrepareAlt)
1934 {if (SFS_OK != (rc = osFS->prepare(fsprep, myError, CRED)))
1935 return fsError(rc, XROOTD_MON_PREP, myError, path, 0);
1936 rc = Response.Send();
1937 } else {
1938 char *mBuff = myError.getMsgBuff(rc);
1939 pargs.reqid = prpid;
1940 pargs.user = Link->ID;
1941 pargs.paths = pFirst;
1942 rc = XrdXrootdPrepare::List(pargs, mBuff, rc);
1943 if (rc < 0) rc = Response.Send("No information found.");
1944 else rc = Response.Send(mBuff);
1945 }
1946 return rc;
1947 }
1948
1949// Make sure we have at least one path
1950//
1951 if (!pFirst)
1952 return Response.Send(kXR_ArgMissing, "No prepare paths specified");
1953
1954// Handle evict. We only support the evicts for alternate prepare handlers.
1955//
1956 if (isEvict)
1957 {if (PrepareAlt
1958 && (SFS_OK != (rc = osFS->prepare(fsprep, myError, CRED))))
1959 return fsError(rc, XROOTD_MON_PREP, myError, path, 0);
1960 return Response.Send();
1961 }
1962
1963// Handle notification parameter. The notification depends on whether or not
1964// we have a custom prepare handler.
1965//
1966 if (opts & kXR_notify)
1967 {const char *nprot = (opts & kXR_usetcp ? "tcp" : "udp");
1968 fsprep.notify = nidbuff;
1969 if (PrepareAlt)
1970 {if (Request.prepare.port == 0) fsprep.notify = 0;
1971 else snprintf(nidbuff, sizeof(nidbuff), "%s://%s:%d/",
1972 nprot, Link->Host(), ntohs(Request.prepare.port));
1973 } else sprintf(nidbuff, Notify, nprot, Link->FDnum(), Link->ID);
1974 if (fsprep.notify)
1975 fsprep.opts |= (opts & kXR_noerrs ? Prep_SENDAOK : Prep_SENDACK);
1976 }
1977
1978// Complete prepare options
1979//
1980 fsprep.opts |= (opts & kXR_fresh ? Prep_FRESH : 0);
1981 if (opts & kXR_wmode) fsprep.opts |= Prep_WMODE;
1982 if (PrepareAlt)
1983 {switch(Request.prepare.prty)
1984 {case 0: fsprep.opts |= Prep_PRTY0; break;
1985 case 1: fsprep.opts |= Prep_PRTY1; break;
1986 case 2: fsprep.opts |= Prep_PRTY2; break;
1987 case 3: fsprep.opts |= Prep_PRTY3; break;
1988 default: break;
1989 }
1990 } else {
1991 if (Request.prepare.prty == 0) fsprep.opts |= Prep_PRTY0;
1992 else fsprep.opts |= Prep_PRTY1;
1993 }
1994
1995// Issue the prepare request
1996//
1997 if (SFS_OK != (rc = osFS->prepare(fsprep, myError, CRED)))
1998 return fsError(rc, XROOTD_MON_PREP, myError, pFirst->text, oFirst->text);
1999
2000// Perform final processing
2001//
2002 if (!(opts & kXR_stage)) rc = Response.Send();
2003 else {rc = Response.Send(reqid, strlen(reqid));
2004 if (!PrepareAlt)
2005 {pargs.reqid = prpid;
2006 pargs.user = Link->ID;
2007 pargs.paths = pFirst;
2008 XrdXrootdPrepare::Log(pargs);
2009 }
2010 }
2011 return rc;
2012}
2013
2014/******************************************************************************/
2015/* d o _ P r o t o c o l */
2016/******************************************************************************/
2017
2018namespace XrdXrootd
2019{
2020extern char *bifResp[2];
2021extern int bifRLen[2];
2022}
2023
2024int XrdXrootdProtocol::do_Protocol()
2025{
2026 static kXR_int32 verNum = static_cast<kXR_int32>(htonl(kXR_PROTOCOLVERSION));
2027 static kXR_int32 theRle = static_cast<kXR_int32>(htonl(myRole));
2028 static kXR_int32 theRlf = static_cast<kXR_int32>(htonl(myRolf));
2029 static kXR_int32 theRlt = static_cast<kXR_int32>(htonl(myRole|kXR_gotoTLS));
2030
2031 ServerResponseBody_Protocol theResp;
2032 struct iovec ioVec[4] = {{0,0},{&theResp,kXR_ShortProtRespLen},{0,0},{0,0}};
2033
2034 int rc, iovN = 2, RespLen = kXR_ShortProtRespLen;
2035 bool wantTLS = false;
2036
2037// Keep Statistics
2038//
2039 SI->Bump(SI->miscCnt);
2040
2041// Determine which response to provide
2042//
2043 if (Request.protocol.clientpv)
2044 {int cvn = XrdOucEI::uVMask & ntohl(Request.protocol.clientpv);
2045 if (!Status || !(clientPV & XrdOucEI::uVMask))
2046 clientPV = (clientPV & ~XrdOucEI::uVMask) | cvn;
2047 else cvn = (clientPV & XrdOucEI::uVMask);
2048
2049 if (Request.protocol.flags & ClientProtocolRequest::kXR_bifreqs
2050 && XrdXrootd::bifResp[0])
2051 {int k =( Link->AddrInfo()->isPrivate() ? 1 : 0);
2052 ioVec[iovN ].iov_base = XrdXrootd::bifResp[k];
2053 ioVec[iovN++].iov_len = XrdXrootd::bifRLen[k];
2054 RespLen += XrdXrootd::bifRLen[k];
2055 }
2056
2057 if (DHS && cvn >= kXR_PROTSIGNVERSION
2058 && Request.protocol.flags & ClientProtocolRequest::kXR_secreqs)
2059 {int n = DHS->ProtResp(theResp.secreq, *(Link->AddrInfo()), cvn);
2060 ioVec[iovN ].iov_base = (void *)&theResp.secreq;
2061 ioVec[iovN++].iov_len = n;
2062 RespLen += n;
2063 }
2064
2065 if ((myRole & kXR_haveTLS) != 0 && !(Link->hasTLS()))
2066 {wantTLS = (Request.protocol.flags &
2068 ableTLS = wantTLS || (Request.protocol.flags &
2070 if (ableTLS) doTLS = tlsCap;
2071 else doTLS = tlsNot;
2072 if (ableTLS && !wantTLS)
2073 switch(Request.protocol.expect & ClientProtocolRequest::kXR_ExpMask)
2075 wantTLS = (doTLS & Req_TLSData) != 0;
2076 break;
2078 wantTLS = (doTLS & Req_TLSLogin) != 0;
2079 break;
2081 wantTLS = (doTLS & Req_TLSTPC) != 0 ||
2082 (doTLS & Req_TLSLogin) != 0;
2083 break;
2084 default: break;
2085 }
2086 }
2087 theResp.flags = (wantTLS ? theRlt : theRle);
2088 } else {
2089 theResp.flags = theRlf;
2090 doTLS = tlsNot;
2091 }
2092
2093// Send the response
2094//
2095 theResp.pval = verNum;
2096 rc = Response.Send(ioVec, iovN, RespLen);
2097
2098// If the client wants to start using TLS, enable it now. If we fail then we
2099// have no choice but to terminate the connection. Note that incapable clients
2100// don't want TLS but if we require TLS anyway, they will get an error either
2101// pre-login or post-login or on a bind later on.
2102//
2103 if (rc == 0 && wantTLS)
2104 {if (Link->setTLS(true, tlsCtx))
2105 {Link->setProtName("xroots");
2106 isTLS = true;
2107 } else {
2108 eDest.Emsg("Xeq", "Unable to enable TLS for", Link->ID);
2109 rc = -1;
2110 }
2111 }
2112 return rc;
2113}
2114
2115/******************************************************************************/
2116/* d o _ Q c o n f */
2117/******************************************************************************/
2118
2119int XrdXrootdProtocol::do_Qconf()
2120{
2121 static const int fsctl_cmd = SFS_FSCTL_STATCC|SFS_O_LOCAL;
2122 XrdOucTokenizer qcargs(argp->buff);
2123 char *val, buff[4096], *bp=buff;
2124 int n, bleft = sizeof(buff);
2125
2126// Get the first argument
2127//
2128 if (!qcargs.GetLine() || !(val = qcargs.GetToken()))
2129 return Response.Send(kXR_ArgMissing, "query config argument not specified.");
2130
2131// The first item can be xrootd or cmsd to display the config file
2132//
2133 if (!strcmp(val, "cmsd") || !strcmp(val, "xrootd"))
2134 return do_QconfCX(qcargs, val);
2135
2136// Trace this query variable
2137//
2138 do {TRACEP(DEBUG, "query config " <<val);
2139
2140 // Now determine what the user wants to query
2141 //
2142 if (!strcmp("bind_max", val))
2143 {n = snprintf(bp, bleft, "%d\n", maxStreams-1);
2144 bp += n; bleft -= n;
2145 }
2146 else if (!strcmp("chksum", val))
2147 {const char *csList = getenv("XRD_CSLIST");
2148 if (!JobCKT || !csList)
2149 {n = snprintf(bp, bleft, "chksum\n");
2150 bp += n; bleft -= n;
2151 continue;
2152 }
2153 n = snprintf(bp, bleft, "%s\n", csList);
2154 bp += n; bleft -= n;
2155 }
2156 else if (!strcmp("cid", val))
2157 {const char *cidval = getenv("XRDCMSCLUSTERID");
2158 if (!cidval || !(*cidval)) cidval = "cid";
2159 n = snprintf(bp, bleft, "%s\n", cidval);
2160 bp += n; bleft -= n;
2161 }
2162 else if (!strcmp("cms", val))
2163 {XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2164 if (osFS->fsctl(fsctl_cmd, ".", myError, CRED) == SFS_DATA)
2165 n = snprintf(bp, bleft, "%s\n", myError.getErrText());
2166 else n = snprintf(bp, bleft, "%s\n", "cms");
2167 bp += n; bleft -= n;
2168 }
2169 else if (!strcmp("pio_max", val))
2170 {n = snprintf(bp, bleft, "%d\n", maxPio+1);
2171 bp += n; bleft -= n;
2172 }
2173 else if (!strcmp("proxy", val))
2174 {const char* pxyOrigin = "proxy";
2175 if (myRole & kXR_attrProxy)
2176 {pxyOrigin = getenv("XRDXROOTD_PROXY");
2177 if (!pxyOrigin) pxyOrigin = "proxy";
2178 }
2179 n = snprintf(bp,bleft,"%s\n",pxyOrigin);
2180 bp += n; bleft -= n;
2181 }
2182 else if (!strcmp("readv_ior_max", val))
2183 {n = snprintf(bp,bleft,"%d\n",maxReadv_ior);
2184 bp += n; bleft -= n;
2185 }
2186 else if (!strcmp("readv_iov_max", val))
2187 {n = snprintf(bp, bleft, "%d\n", XrdProto::maxRvecsz);
2188 bp += n; bleft -= n;
2189 }
2190 else if (!strcmp("role", val))
2191 {const char *theRole = getenv("XRDROLE");
2192 n = snprintf(bp, bleft, "%s\n", (theRole ? theRole : "none"));
2193 bp += n; bleft -= n;
2194 }
2195 else if (!strcmp("sitename", val))
2196 {const char *siteName = getenv("XRDSITE");
2197 n = snprintf(bp, bleft, "%s\n", (siteName ? siteName : "sitename"));
2198 bp += n; bleft -= n;
2199 }
2200 else if (!strcmp("start", val))
2201 {n = snprintf(bp, bleft, "%s\n", startUP);
2202 bp += n; bleft -= n;
2203 }
2204 else if (!strcmp("sysid", val))
2205 {const char *cidval = getenv("XRDCMSCLUSTERID");
2206 const char *nidval = getenv("XRDCMSVNID");
2207 if (!cidval || !(*cidval) || !nidval || !(*nidval))
2208 {cidval = "sysid"; nidval = "";}
2209 n = snprintf(bp, bleft, "%s %s\n", nidval, cidval);
2210 bp += n; bleft -= n;
2211 }
2212 else if (!strcmp("tpc", val))
2213 {char *tpcval = getenv("XRDTPC");
2214 n = snprintf(bp, bleft, "%s\n", (tpcval ? tpcval : "tpc"));
2215 bp += n; bleft -= n;
2216 }
2217 else if (!strcmp("tpcdlg", val))
2218 {char *tpcval = getenv("XRDTPCDLG");
2219 n = snprintf(bp, bleft, "%s\n", (tpcval ? tpcval : "tpcdlg"));
2220 bp += n; bleft -= n;
2221 }
2222 else if (!strcmp("tls_port", val) && tlsPort)
2223 {n = snprintf(bp, bleft, "%d\n", tlsPort);
2224 bp += n; bleft -= n;
2225 }
2226 else if (!strcmp("window", val) && Window)
2227 {n = snprintf(bp, bleft, "%d\n", Window);
2228 bp += n; bleft -= n;
2229 }
2230 else if (!strcmp("version", val))
2231 {n = snprintf(bp, bleft, "%s\n", XrdVSTRING);
2232 bp += n; bleft -= n;
2233 }
2234 else if (!strcmp("vnid", val))
2235 {const char *nidval = getenv("XRDCMSVNID");
2236 if (!nidval || !(*nidval)) nidval = "vnid";
2237 n = snprintf(bp, bleft, "%s\n", nidval);
2238 }
2239 else if (!strcmp("fattr", val))
2240 {n = snprintf(bp, bleft, "%s\n", usxParms);
2241 bp += n; bleft -= n;
2242 }
2243 else {n = strlen(val);
2244 if (bleft <= n) break;
2245 strcpy(bp, val); bp +=n; *bp = '\n'; bp++;
2246 bleft -= (n+1);
2247 }
2248 } while(bleft > 0 && (val = qcargs.GetToken()));
2249
2250// Make sure all ended well
2251//
2252 if (val)
2253 return Response.Send(kXR_ArgTooLong, "too many query config arguments.");
2254
2255// All done
2256//
2257 return Response.Send(buff, sizeof(buff) - bleft);
2258}
2259
2260/******************************************************************************/
2261/* d o _ Q c o n f C X */
2262/******************************************************************************/
2263
2264int XrdXrootdProtocol::do_QconfCX(XrdOucTokenizer &qcargs, char *val)
2265{
2266 extern XrdOucString *XrdXrootdCF;
2267 bool isCMSD = (*val == 'c');
2268
2269// Make sure there is nothing else following the token
2270//
2271 if ((val = qcargs.GetToken()))
2272 return Response.Send(kXR_ArgInvalid, "too many query config arguments.");
2273
2274// If this is a cms just return a null for now
2275//
2276 if (isCMSD) return Response.Send((void *)"\n", 2);
2277
2278// Display the xrootd configuration
2279//
2280 if (XrdXrootdCF && isTLS && getenv("XROOTD_QCFOK"))
2281 return Response.Send((void *)XrdXrootdCF->c_str(), XrdXrootdCF->length());
2282
2283// Respond with a null
2284//
2285 return Response.Send((void *)"\n", 2);
2286}
2287
2288/******************************************************************************/
2289/* d o _ Q f h */
2290/******************************************************************************/
2291
2292int XrdXrootdProtocol::do_Qfh()
2293{
2294 static XrdXrootdCallBack qryCB("query", XROOTD_MON_QUERY);
2295 XrdXrootdFHandle fh(Request.query.fhandle);
2296 XrdXrootdFile *fp;
2297 const char *fArg = 0, *qType = "";
2298 int rc;
2299 short qopt = (short)ntohs(Request.query.infotype);
2300
2301// Update misc stats count
2302//
2303 SI->Bump(SI->miscCnt);
2304
2305// Find the file object
2306//
2307 if (!FTab || !(fp = FTab->Get(fh.handle)))
2308 return Response.Send(kXR_FileNotOpen,
2309 "query does not refer to an open file");
2310
2311// The query is elegible for a deferred response, indicate we're ok with that
2312//
2313 fp->XrdSfsp->error.setErrCB(&qryCB, ReqID.getID());
2314
2315// Perform the appropriate query
2316//
2317 switch(qopt)
2318 {case kXR_QFinfo: qType = "QFinfo";
2319 fArg = (Request.query.dlen ? argp->buff : 0);
2320 rc = fp->XrdSfsp->fctl(SFS_FCTL_QFINFO,
2321 Request.query.dlen, fArg,
2322 CRED);
2323 break;
2324 case kXR_Qopaqug: qType = "Qopaqug";
2325 fArg = (Request.query.dlen ? argp->buff : 0);
2326 rc = fp->XrdSfsp->fctl(SFS_FCTL_SPEC1,
2327 Request.query.dlen, fArg,
2328 CRED);
2329 break;
2330 case kXR_Qvisa: qType = "Qvisa";
2331 rc = fp->XrdSfsp->fctl(SFS_FCTL_STATV, 0,
2332 fp->XrdSfsp->error);
2333 break;
2334 default: return Response.Send(kXR_ArgMissing,
2335 "Required query argument not present");
2336 }
2337
2338// Preform the actual function
2339//
2340 TRACEP(FS, "fh=" <<fh.handle <<" query " <<qType <<" rc=" <<rc);
2341
2342// Return appropriately
2343//
2344 if (SFS_OK != rc)
2345 return fsError(rc, XROOTD_MON_QUERY, fp->XrdSfsp->error, 0, 0);
2346 return Response.Send();
2347}
2348
2349/******************************************************************************/
2350/* d o _ Q o p a q u e */
2351/******************************************************************************/
2352
2353int XrdXrootdProtocol::do_Qopaque(short qopt)
2354{
2355 static XrdXrootdCallBack qpqCB("query", XROOTD_MON_QUERY);
2356 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2357 XrdSfsFSctl myData;
2358 const char *Act, *AData;
2359 char *opaque;
2360 int fsctl_cmd, rc, dlen = Request.query.dlen;
2361
2362// Process unstructured as well as structured (path/opaque) requests
2363//
2364 if (qopt == kXR_Qopaque)
2365 {myData.Arg1 = argp->buff; myData.Arg1Len = dlen;
2366 myData.Arg2 = 0; myData.Arg2Len = 0;
2367 fsctl_cmd = SFS_FSCTL_PLUGIO;
2368 Act = " qopaque '"; AData = "...";
2369 } else {
2370 // Check for static routing (this falls under stat)
2371 //
2372 STATIC_REDIRECT(RD_stat);
2373
2374 // Prescreen the path
2375 //
2376 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Querying", argp->buff);
2377 if (!Squash(argp->buff)) return vpEmsg("Querying", argp->buff);
2378
2379 // Setup arguments
2380 //
2381 myData.Arg1 = argp->buff;
2382 myData.Arg1Len = (opaque ? opaque - argp->buff - 1 : dlen);
2383 myData.Arg2 = opaque;
2384 myData.Arg2Len = (opaque ? argp->buff + dlen - opaque : 0);
2385 if (qopt == kXR_QFSinfo)
2386 {fsctl_cmd = SFS_FSCTL_PLUGFS;
2387 Act = " qfsinfo '";
2388 } else {
2389 fsctl_cmd = SFS_FSCTL_PLUGIN;
2390 Act = " qopaquf '";
2391 }
2392 AData = argp->buff;
2393 }
2394
2395// The query is elegible for a deferred response, indicate we're ok with that
2396//
2397 myError.setErrCB(&qpqCB, ReqID.getID());
2398
2399// Preform the actual function using the supplied arguments
2400//
2401 rc = osFS->FSctl(fsctl_cmd, myData, myError, CRED);
2402 TRACEP(FS, "rc=" <<rc <<Act <<AData <<"'");
2403 if (rc == SFS_OK) return Response.Send("");
2404 return fsError(rc, 0, myError, 0, 0);
2405}
2406
2407/******************************************************************************/
2408/* d o _ Q s p a c e */
2409/******************************************************************************/
2410
2411int XrdXrootdProtocol::do_Qspace()
2412{
2413 static const int fsctl_cmd = SFS_FSCTL_STATLS;
2414 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2415 char *opaque;
2416 int n, rc;
2417
2418// Check for static routing
2419//
2420 STATIC_REDIRECT(RD_stat);
2421
2422// Prescreen the path
2423//
2424 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Stating", argp->buff);
2425 if (!Squash(argp->buff)) return vpEmsg("Stating", argp->buff);
2426
2427// Add back the opaque info
2428//
2429 if (opaque)
2430 {n = strlen(argp->buff); argp->buff[n] = '?';
2431 if ((argp->buff)+n != opaque-1)
2432 memmove(&argp->buff[n+1], opaque, strlen(opaque)+1);
2433 }
2434
2435// Preform the actual function using the supplied logical FS name
2436//
2437 rc = osFS->fsctl(fsctl_cmd, argp->buff, myError, CRED);
2438 TRACEP(FS, "rc=" <<rc <<" qspace '" <<argp->buff <<"'");
2439 if (rc == SFS_OK) return Response.Send("");
2440 return fsError(rc, XROOTD_MON_QUERY, myError, argp->buff, opaque);
2441}
2442
2443/******************************************************************************/
2444/* d o _ Q u e r y */
2445/******************************************************************************/
2446
2447int XrdXrootdProtocol::do_Query()
2448{
2449 short qopt = (short)ntohs(Request.query.infotype);
2450
2451// Perform the appropriate query
2452//
2453 switch(qopt)
2454 {case kXR_QStats: return SI->Stats(Response,
2455 (Request.header.dlen ? argp->buff : "a"));
2456 case kXR_Qcksum: return do_CKsum(0);
2457 case kXR_Qckscan: return do_CKsum(1);
2458 case kXR_Qconfig: return do_Qconf();
2459 case kXR_Qspace: return do_Qspace();
2460 case kXR_Qxattr: return do_Qxattr();
2461 case kXR_QFSinfo:
2462 case kXR_Qopaque:
2463 case kXR_Qopaquf: return do_Qopaque(qopt);
2464// case kXR_Qvisa:
2465 case kXR_QFinfo:
2466 case kXR_Qopaqug: return do_Qfh();
2467 case kXR_QPrep: return do_Prepare(true);
2468 default: break;
2469 }
2470
2471// Whatever we have, it's not valid
2472//
2473 return Response.Send(kXR_ArgInvalid,
2474 "Invalid information query type code");
2475}
2476
2477/******************************************************************************/
2478/* d o _ Q x a t t r */
2479/******************************************************************************/
2480
2481int XrdXrootdProtocol::do_Qxattr()
2482{
2483 static XrdXrootdCallBack statCB("stat", XROOTD_MON_QUERY);
2484 static const int fsctl_cmd = SFS_FSCTL_STATXA;
2485 int rc;
2486 char *opaque;
2487 XrdOucErrInfo myError(Link->ID,&statCB,ReqID.getID(),Monitor.Did,clientPV);
2488
2489// Check for static routing
2490//
2491 STATIC_REDIRECT(RD_stat);
2492
2493// Prescreen the path
2494//
2495 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Stating", argp->buff);
2496 if (!Squash(argp->buff)) return vpEmsg("Stating", argp->buff);
2497
2498// Add back opaque information is present
2499//
2500 if (opaque)
2501 {int n = strlen(argp->buff); argp->buff[n] = '?';
2502 if ((argp->buff)+n != opaque-1)
2503 memmove(&argp->buff[n+1], opaque, strlen(opaque)+1);
2504 }
2505
2506// Preform the actual function
2507//
2508 rc = osFS->fsctl(fsctl_cmd, argp->buff, myError, CRED);
2509 TRACEP(FS, "rc=" <<rc <<" qxattr " <<argp->buff);
2510 return fsError(rc, XROOTD_MON_QUERY, myError, argp->buff, opaque);
2511}
2512
2513/******************************************************************************/
2514/* d o _ R e a d */
2515/******************************************************************************/
2516
2517int XrdXrootdProtocol::do_Read()
2518{
2519 int pathID, retc;
2520 XrdXrootdFHandle fh(Request.read.fhandle);
2521 numReads++;
2522
2523// We first handle the pre-read list, if any. We do it this way because of
2524// a historical glitch in the protocol. One should really not piggy back a
2525// pre-read on top of a read, though it is allowed.
2526//
2527 if (!Request.header.dlen) pathID = 0;
2528 else if (do_ReadNone(retc, pathID)) return retc;
2529
2530// Unmarshall the data
2531//
2532 IO.IOLen = ntohl(Request.read.rlen);
2533 n2hll(Request.read.offset, IO.Offset);
2534
2535// Find the file object
2536//
2537 if (!FTab || !(IO.File = FTab->Get(fh.handle)))
2538 return Response.Send(kXR_FileNotOpen,
2539 "read does not refer to an open file");
2540
2541// Trace and verify read length is not negative
2542//
2543 TRACEP(FSIO, pathID <<" fh=" <<fh.handle <<" read " <<IO.IOLen
2544 <<'@' <<IO.Offset);
2545 if ( IO.IOLen < 0) return Response.Send(kXR_ArgInvalid,
2546 "Read length is negative");
2547
2548// If we are monitoring, insert a read entry
2549//
2550 if (Monitor.InOut())
2551 Monitor.Agent->Add_rd(IO.File->Stats.FileID, Request.read.rlen,
2552 Request.read.offset);
2553
2554// Short circuit processing if read length is zero
2555//
2556 if (!IO.IOLen) return Response.Send();
2557
2558// There are many competing ways to accomplish a read. Pick the one we
2559// will use and if possible, do a fast dispatch.
2560//
2561 if (IO.File->isMMapped) IO.Mode = XrdXrootd::IOParms::useMMap;
2562 else if (IO.File->sfEnabled && !isTLS && IO.IOLen >= as_minsfsz
2563 && IO.Offset+IO.IOLen <= IO.File->Stats.fSize)
2565 else if (IO.File->AsyncMode && IO.IOLen >= as_miniosz
2566 && IO.Offset+IO.IOLen <= IO.File->Stats.fSize+as_seghalf
2568 {XrdXrootdProtocol *pP;
2569 XrdXrootdNormAio *aioP=0;
2570
2571 if (!pathID) pP = this;
2572 else {if (!(pP = VerifyStream(retc, pathID, false))) return retc;
2573 if (pP->linkAioReq >= as_maxperlnk) pP = 0;
2574 }
2575 if (pP)
2576 {// Use of TmpRsp here is to avoid modying pP. It is built
2577 // to contain the correct streamid for this request and the
2578 // right Link for the pathID. It's used by Alloc to call
2579 // XrdXrootdAioTask::Init which in turn makes a copy of TmpRsp
2580 // to its own response object and also keeps the Link pointer.
2581 XrdXrootdResponse TmpRsp;
2582 TmpRsp = Response;
2583 TmpRsp.Set(pP->Link);
2584 aioP = XrdXrootdNormAio::Alloc(pP,TmpRsp,IO.File);
2585 }
2586 if (aioP)
2587 {if (!IO.File->aioFob) IO.File->aioFob = new XrdXrootdAioFob;
2588 aioP->Read(IO.Offset, IO.IOLen);
2589 return 0;
2590 }
2591 SI->AsyncRej++;
2593 }
2594 else IO.Mode = XrdXrootd::IOParms::useBasic;
2595
2596// See if an alternate path is required, offload the read
2597//
2598 if (pathID) return do_Offload(&XrdXrootdProtocol::do_ReadAll, pathID);
2599
2600// Now read all of the data (do pre-reads first)
2601//
2602 return do_ReadAll();
2603}
2604
2605/******************************************************************************/
2606/* d o _ R e a d A l l */
2607/******************************************************************************/
2608
2609// IO.File = file to be read
2610// IO.Offset = Offset at which to read
2611// IO.IOLen = Number of bytes to read from file and write to socket
2612
2613int XrdXrootdProtocol::do_ReadAll()
2614{
2615 int rc, xframt, Quantum = (IO.IOLen > maxBuffsz ? maxBuffsz : IO.IOLen);
2616 char *buff;
2617
2618// If this file is memory mapped, short ciruit all the logic and immediately
2619// transfer the requested data to minimize latency.
2620//
2621 if (IO.Mode == XrdXrootd::IOParms::useMMap)
2622 {if (IO.Offset >= IO.File->Stats.fSize) return Response.Send();
2623 if (IO.Offset+IO.IOLen <= IO.File->Stats.fSize)
2624 {IO.File->Stats.rdOps(IO.IOLen);
2625 return Response.Send(IO.File->mmAddr+IO.Offset, IO.IOLen);
2626 }
2627 xframt = IO.File->Stats.fSize -IO.Offset;
2628 IO.File->Stats.rdOps(xframt);
2629 return Response.Send(IO.File->mmAddr+IO.Offset, xframt);
2630 }
2631
2632// If we are sendfile enabled, then just send the file if possible
2633//
2634 if (IO.Mode == XrdXrootd::IOParms::useSF)
2635 {IO.File->Stats.rdOps(IO.IOLen);
2636 if (IO.File->fdNum >= 0)
2637 return Response.Send(IO.File->fdNum, IO.Offset, IO.IOLen);
2638 rc = IO.File->XrdSfsp->SendData((XrdSfsDio *)this, IO.Offset, IO.IOLen);
2639 if (rc == SFS_OK)
2640 {if (!IO.IOLen) return 0;
2641 if (IO.IOLen < 0) return -1; // Otherwise retry using read()
2642 } else return fsError(rc, 0, IO.File->XrdSfsp->error, 0, 0);
2643 }
2644
2645// Make sure we have a large enough buffer
2646//
2647 if (!argp || Quantum < halfBSize || Quantum > argp->bsize)
2648 {if ((rc = getBuff(1, Quantum)) <= 0) return rc;}
2649 else if (hcNow < hcNext) hcNow++;
2650 buff = argp->buff;
2651
2652// Now read all of the data. For statistics, we need to record the orignal
2653// amount of the request even if we really do not get to read that much!
2654//
2655 IO.File->Stats.rdOps(IO.IOLen);
2656 do {if ((xframt = IO.File->XrdSfsp->read(IO.Offset, buff, Quantum)) <= 0) break;
2657 if (xframt >= IO.IOLen) return Response.Send(buff, xframt);
2658 if (Response.Send(kXR_oksofar, buff, xframt) < 0) return -1;
2659 IO.Offset += xframt; IO.IOLen -= xframt;
2660 if (IO.IOLen < Quantum) Quantum = IO.IOLen;
2661 } while(IO.IOLen);
2662
2663// Determine why we ended here
2664//
2665 if (xframt == 0) return Response.Send();
2666 return fsError(xframt, 0, IO.File->XrdSfsp->error, 0, 0);
2667}
2668
2669/******************************************************************************/
2670/* d o _ R e a d N o n e */
2671/******************************************************************************/
2672
2673int XrdXrootdProtocol::do_ReadNone(int &retc, int &pathID)
2674{
2675 XrdXrootdFHandle fh;
2676 int ralsz = Request.header.dlen;
2677 struct read_args *rargs=(struct read_args *)(argp->buff);
2678 struct readahead_list *ralsp = (readahead_list *)(rargs+1);
2679
2680// Return the pathid
2681//
2682 pathID = static_cast<int>(rargs->pathid);
2683 if ((ralsz -= sizeof(read_args)) <= 0) return 0;
2684
2685// Make sure that we have a proper pre-read list
2686//
2687 if (ralsz%sizeof(readahead_list))
2688 {Response.Send(kXR_ArgInvalid, "Invalid length for read ahead list");
2689 return 1;
2690 }
2691
2692// Run down the pre-read list
2693//
2694 while(ralsz > 0)
2695 {IO.IOLen = ntohl(ralsp->rlen);
2696 n2hll(ralsp->offset, IO.Offset);
2697 memcpy((void *)&fh.handle, (const void *)ralsp->fhandle,
2698 sizeof(fh.handle));
2699 TRACEP(FSIO, "fh="<<fh.handle<<" read "<<IO.IOLen<<'@'<<IO.Offset);
2700 if (!FTab || !(IO.File = FTab->Get(fh.handle)))
2701 {retc = Response.Send(kXR_FileNotOpen,
2702 "preread does not refer to an open file");
2703 return 1;
2704 }
2705 IO.File->XrdSfsp->read(IO.Offset, IO.IOLen);
2706 ralsz -= sizeof(struct readahead_list);
2707 ralsp++;
2708 numReads++;
2709 };
2710
2711// All done
2712//
2713 return 0;
2714}
2715
2716/******************************************************************************/
2717/* d o _ R e a d V */
2718/******************************************************************************/
2719
2720int XrdXrootdProtocol::do_ReadV()
2721{
2722// This will read multiple buffers at the same time in an attempt to avoid
2723// the latency in a network. The information with the offsets and lengths
2724// of the information to read is passed as a data buffer... then we decode
2725// it and put all the individual buffers in a single one it's up to the
2726// client to interpret it. Code originally developed by Leandro Franco, CERN.
2727// The readv file system code originally added by Brian Bockelman, UNL.
2728//
2729 const int hdrSZ = sizeof(readahead_list);
2730 struct XrdOucIOVec rdVec[XrdProto::maxRvecsz+1];
2731 struct readahead_list *raVec, respHdr;
2732 long long totSZ;
2733 XrdSfsXferSize rdVAmt, rdVXfr, xfrSZ = 0;
2734 int rdVBeg, rdVBreak, rdVNow, rdVNum, rdVecNum;
2735 int currFH, i, k, Quantum, Qleft, rdVecLen = Request.header.dlen;
2736 int rvMon = Monitor.InOut();
2737 int ioMon = (rvMon > 1);
2738 char *buffp, vType = (ioMon ? XROOTD_MON_READU : XROOTD_MON_READV);
2739
2740// Compute number of elements in the read vector and make sure we have no
2741// partial elements.
2742//
2743 rdVecNum = rdVecLen / sizeof(readahead_list);
2744 if ( (rdVecNum <= 0) || (rdVecNum*hdrSZ != rdVecLen) )
2745 return Response.Send(kXR_ArgInvalid, "Read vector is invalid");
2746
2747// Make sure that we can copy the read vector to our local stack. We must impose
2748// a limit on it's size. We do this to be able to reuse the data buffer to
2749// prevent cross-cpu memory cache synchronization.
2750//
2751 if (rdVecNum > XrdProto::maxRvecsz)
2752 return Response.Send(kXR_ArgTooLong, "Read vector is too long");
2753
2754// So, now we account for the number of readv requests and total segments
2755//
2756 numReadV++; numSegsV += rdVecNum;
2757
2758// Run down the list and compute the total size of the read. No individual
2759// read may be greater than the maximum transfer size. We also use this loop
2760// to copy the read ahead list to our readv vector for later processing.
2761//
2762 raVec = (readahead_list *)argp->buff;
2763 totSZ = rdVecLen; Quantum = maxReadv_ior;
2764 for (i = 0; i < rdVecNum; i++)
2765 {totSZ += (rdVec[i].size = ntohl(raVec[i].rlen));
2766 if (rdVec[i].size < 0) return Response.Send(kXR_ArgInvalid,
2767 "Readv length is negative");
2768 if (rdVec[i].size > Quantum) return Response.Send(kXR_NoMemory,
2769 "Single readv transfer is too large");
2770 rdVec[i].offset = ntohll(raVec[i].offset);
2771 memcpy(&rdVec[i].info, raVec[i].fhandle, sizeof(int));
2772 }
2773
2774// Now add an extra dummy element to force flushing of the read vector.
2775//
2776 rdVec[i].offset = -1;
2777 rdVec[i].size = 0;
2778 rdVec[i].info = -1;
2779 rdVBreak = rdVecNum;
2780 rdVecNum++;
2781
2782// We limit the total size of the read to be 2GB for convenience
2783//
2784 if (totSZ > 0x80000000LL)
2785 return Response.Send(kXR_NoMemory, "Total readv transfer is too large");
2786
2787// Calculate the transfer unit which will be the smaller of the maximum
2788// transfer unit and the actual amount we need to transfer.
2789//
2790 Quantum = totSZ < maxTransz ? totSZ : maxTransz;
2791
2792// Now obtain the right size buffer
2793//
2794 if ((Quantum < halfBSize && Quantum > 1024) || Quantum > argp->bsize)
2795 {if ((k = getBuff(1, Quantum)) <= 0) return k;}
2796 else if (hcNow < hcNext) hcNow++;
2797
2798// Check that we really have at least one file open. This needs to be done
2799// only once as this code runs in the control thread.
2800//
2801 if (!FTab) return Response.Send(kXR_FileNotOpen,
2802 "readv does not refer to an open file");
2803
2804// Preset the previous and current file handle to be the handle of the first
2805// element and make sure the file is actually open.
2806//
2807 currFH = rdVec[0].info;
2808 memcpy(respHdr.fhandle, &currFH, sizeof(respHdr.fhandle));
2809 if (!(IO.File = FTab->Get(currFH))) return Response.Send(kXR_FileNotOpen,
2810 "readv does not refer to an open file");
2811
2812// Setup variables for running through the list.
2813//
2814 Qleft = Quantum; buffp = argp->buff; rvSeq++;
2815 rdVBeg = rdVNow = 0; rdVXfr = rdVAmt = 0;
2816
2817// Now run through the elements
2818//
2819 for (i = 0; i < rdVecNum; i++)
2820 {if (rdVec[i].info != currFH)
2821 {xfrSZ = IO.File->XrdSfsp->readv(&rdVec[rdVNow], i-rdVNow);
2822 if (xfrSZ != rdVAmt) break;
2823 rdVNum = i - rdVBeg; rdVXfr += rdVAmt;
2824 IO.File->Stats.rvOps(rdVXfr, rdVNum);
2825 if (rvMon)
2826 {Monitor.Agent->Add_rv(IO.File->Stats.FileID, htonl(rdVXfr),
2827 htons(rdVNum), rvSeq, vType);
2828 if (ioMon) for (k = rdVBeg; k < i; k++)
2829 Monitor.Agent->Add_rd(IO.File->Stats.FileID,
2830 htonl(rdVec[k].size), htonll(rdVec[k].offset));
2831 }
2832 rdVXfr = rdVAmt = 0;
2833 if (i == rdVBreak) break;
2834 rdVBeg = rdVNow = i; currFH = rdVec[i].info;
2835 memcpy(respHdr.fhandle, &currFH, sizeof(respHdr.fhandle));
2836 if (!(IO.File = FTab->Get(currFH)))
2837 return Response.Send(kXR_FileNotOpen,
2838 "readv does not refer to an open file");
2839 }
2840
2841 if (Qleft < (rdVec[i].size + hdrSZ))
2842 {if (rdVAmt)
2843 {xfrSZ = IO.File->XrdSfsp->readv(&rdVec[rdVNow], i-rdVNow);
2844 if (xfrSZ != rdVAmt) break;
2845 }
2846 if (Response.Send(kXR_oksofar,argp->buff,Quantum-Qleft) < 0)
2847 return -1;
2848 Qleft = Quantum;
2849 buffp = argp->buff;
2850 rdVNow = i; rdVXfr += rdVAmt; rdVAmt = 0;
2851 }
2852
2853 xfrSZ = rdVec[i].size; rdVAmt += xfrSZ;
2854 respHdr.rlen = htonl(xfrSZ);
2855 respHdr.offset = htonll(rdVec[i].offset);
2856 memcpy(buffp, &respHdr, hdrSZ);
2857 rdVec[i].data = buffp + hdrSZ;
2858 buffp += (xfrSZ+hdrSZ); Qleft -= (xfrSZ+hdrSZ);
2859 TRACEP(FSIO,"fh=" <<currFH<<" readV "<< xfrSZ <<'@'<<rdVec[i].offset);
2860 }
2861
2862// Check if we have an error here. This is indicated when rdVAmt is not zero.
2863//
2864 if (rdVAmt)
2865 {if (xfrSZ >= 0)
2866 {xfrSZ = SFS_ERROR;
2867 IO.File->XrdSfsp->error.setErrInfo(-ENODATA,"readv past EOF");
2868 }
2869 return fsError(xfrSZ, 0, IO.File->XrdSfsp->error, 0, 0);
2870 }
2871
2872// All done, return result of the last segment or just zero
2873//
2874 return (Quantum != Qleft ? Response.Send(argp->buff, Quantum-Qleft) : 0);
2875}
2876
2877/******************************************************************************/
2878/* d o _ R m */
2879/******************************************************************************/
2880
2881int XrdXrootdProtocol::do_Rm()
2882{
2883 int rc;
2884 char *opaque;
2885 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2886
2887// Check for static routing
2888//
2889 STATIC_REDIRECT(RD_rm);
2890
2891// Prescreen the path
2892//
2893 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Removing", argp->buff);
2894 if (!Squash(argp->buff)) return vpEmsg("Removing", argp->buff);
2895
2896// Preform the actual function
2897//
2898 rc = osFS->rem(argp->buff, myError, CRED, opaque);
2899 TRACEP(FS, "rc=" <<rc <<" rm " <<argp->buff);
2900 if (SFS_OK == rc) return Response.Send();
2901
2902// An error occurred
2903//
2904 return fsError(rc, XROOTD_MON_RM, myError, argp->buff, opaque);
2905}
2906
2907/******************************************************************************/
2908/* d o _ R m d i r */
2909/******************************************************************************/
2910
2911int XrdXrootdProtocol::do_Rmdir()
2912{
2913 int rc;
2914 char *opaque;
2915 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2916
2917// Check for static routing
2918//
2919 STATIC_REDIRECT(RD_rmdir);
2920
2921// Prescreen the path
2922//
2923 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Removing", argp->buff);
2924 if (!Squash(argp->buff)) return vpEmsg("Removing", argp->buff);
2925
2926// Preform the actual function
2927//
2928 rc = osFS->remdir(argp->buff, myError, CRED, opaque);
2929 TRACEP(FS, "rc=" <<rc <<" rmdir " <<argp->buff);
2930 if (SFS_OK == rc) return Response.Send();
2931
2932// An error occurred
2933//
2934 return fsError(rc, XROOTD_MON_RMDIR, myError, argp->buff, opaque);
2935}
2936
2937/******************************************************************************/
2938/* d o _ S e t */
2939/******************************************************************************/
2940
2941int XrdXrootdProtocol::do_Set()
2942{
2943 XrdOucTokenizer setargs(argp->buff);
2944 char *val, *rest;
2945
2946// Get the first argument
2947//
2948 if (!setargs.GetLine() || !(val = setargs.GetToken(&rest)))
2949 return Response.Send(kXR_ArgMissing, "set argument not specified.");
2950
2951// Trace this set
2952//
2953 TRACEP(DEBUG, "set " <<val <<' ' <<rest);
2954
2955// Now determine what the user wants to set
2956//
2957 if (!strcmp("appid", val))
2958 {while(*rest && *rest == ' ') rest++;
2959 eDest.Emsg("Xeq", Link->ID, "appid", rest);
2960 return Response.Send();
2961 }
2962 else if (!strcmp("monitor", val)) return do_Set_Mon(setargs);
2963 else if (!strcmp("cache", val)) return do_Set_Cache(setargs);
2964
2965// All done
2966//
2967 return Response.Send(kXR_ArgInvalid, "invalid set parameter");
2968}
2969
2970/******************************************************************************/
2971/* d o _ S e t _ C a c h e */
2972/******************************************************************************/
2973
2974// Process: set cache <cmd> <args>
2975
2976int XrdXrootdProtocol::do_Set_Cache(XrdOucTokenizer &setargs)
2977{
2978 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2979 XrdSfsFSctl myData;
2980 char *cmd, *cargs, *opaque = nullptr;
2981 const char *myArgs[2];
2982
2983// This set is valid only if we implement a cache
2984//
2985 if ((fsFeatures & XrdSfs::hasCACH) == 0)
2986 return Response.Send(kXR_ArgInvalid, "invalid set parameter");
2987
2988// Get the command and argument
2989//
2990 if (!(cmd = setargs.GetToken(&cargs)))
2991 return Response.Send(kXR_ArgMissing,"set cache argument not specified.");
2992
2993// Prescreen the path if the next token starts with a slash
2994//
2995 if (cargs && *cargs == '/')
2996 {if (rpCheck(cargs, &opaque)) return rpEmsg("Setting", cargs);
2997 if (!Squash(cargs)) return vpEmsg("Setting", cargs);
2998 myData.ArgP = myArgs; myData.Arg2Len = -2;
2999 myArgs[0] = cargs;
3000 myArgs[1] = opaque;
3001 } else {
3002 myData.Arg2 = opaque; myData.Arg2Len = (opaque ? strlen(opaque) : 0);
3003 }
3004 myData.Arg1 = cmd; myData.Arg1Len = strlen(cmd);
3005
3006// Preform the actual function using the supplied arguments
3007//
3008 int rc = osFS->FSctl(SFS_FSCTL_PLUGXC, myData, myError, CRED);
3009 TRACEP(FS, "rc=" <<rc <<"set cache " <<myData.Arg1 <<' ' <<cargs);
3010 if (rc == SFS_OK) return Response.Send("");
3011 return fsError(rc, 0, myError, 0, 0);
3012}
3013
3014/******************************************************************************/
3015/* d o _ S e t _ M o n */
3016/******************************************************************************/
3017
3018// Process: set monitor {off | on} {[appid] | info [info]}
3019
3020int XrdXrootdProtocol::do_Set_Mon(XrdOucTokenizer &setargs)
3021{
3022 char *val, *appid;
3023 kXR_unt32 myseq = 0;
3024
3025// Get the first argument
3026//
3027 if (!(val = setargs.GetToken(&appid)))
3028 return Response.Send(kXR_ArgMissing,"set monitor argument not specified.");
3029
3030// For info requests, nothing changes. However, info events must have been
3031// enabled for us to record them. Route the information via the static
3032// monitor entry, since it knows how to forward the information.
3033//
3034 if (!strcmp(val, "info"))
3035 {if (appid && Monitor.Info())
3036 {while(*appid && *appid == ' ') appid++;
3037 if (strlen(appid) > 1024) appid[1024] = '\0';
3038 if (*appid) myseq = Monitor.MapInfo(appid);
3039 }
3040 return Response.Send((void *)&myseq, sizeof(myseq));
3041 }
3042
3043// Determine if on do appropriate processing
3044//
3045 if (!strcmp(val, "on"))
3046 {Monitor.Enable();
3047 if (appid && Monitor.InOut())
3048 {while(*appid && *appid == ' ') appid++;
3049 if (*appid) Monitor.Agent->appID(appid);
3050 }
3051 if (!Monitor.Did && Monitor.Logins()) MonAuth();
3052 return Response.Send();
3053 }
3054
3055// Determine if off and do appropriate processing
3056//
3057 if (!strcmp(val, "off"))
3058 {if (appid && Monitor.InOut())
3059 {while(*appid && *appid == ' ') appid++;
3060 if (*appid) Monitor.Agent->appID(appid);
3061 }
3062 Monitor.Disable();
3063 return Response.Send();
3064 }
3065
3066// Improper request
3067//
3068 return Response.Send(kXR_ArgInvalid, "invalid set monitor argument");
3069}
3070
3071/******************************************************************************/
3072/* d o _ S t a t */
3073/******************************************************************************/
3074
3075int XrdXrootdProtocol::do_Stat()
3076{
3077 static XrdXrootdCallBack statCB("stat", XROOTD_MON_STAT);
3078 static const int fsctl_cmd = SFS_FSCTL_STATFS;
3079 bool doDig;
3080 int rc;
3081 char *opaque, xxBuff[1024];
3082 struct stat buf;
3083 XrdOucErrInfo myError(Link->ID,&statCB,ReqID.getID(),Monitor.Did,clientPV);
3084
3085// Update misc stats count
3086//
3087 SI->Bump(SI->miscCnt);
3088
3089// The stat request may refer to an open file handle. So, screen this out.
3090//
3091 if (!argp || !Request.header.dlen)
3092 {XrdXrootdFile *fp;
3093 XrdXrootdFHandle fh(Request.stat.fhandle);
3094 if (Request.stat.options & kXR_vfs)
3095 {Response.Send(kXR_ArgMissing, "Required argument not present");
3096 return 0;
3097 }
3098 if (!FTab || !(fp = FTab->Get(fh.handle)))
3099 return Response.Send(kXR_FileNotOpen,
3100 "stat does not refer to an open file");
3101 rc = fp->XrdSfsp->stat(&buf);
3102 TRACEP(FS, "fh=" <<fh.handle <<" stat rc=" <<rc);
3103 if (SFS_OK == rc) return Response.Send(xxBuff,
3104 StatGen(buf,xxBuff,sizeof(xxBuff)));
3105 return fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
3106 }
3107
3108// Check if we are handling a dig type path
3109//
3110 doDig = (digFS && SFS_LCLROOT(argp->buff));
3111
3112// Check for static routing
3113//
3114 if (!doDig) {STATIC_REDIRECT(RD_stat);}
3115
3116// Prescreen the path
3117//
3118 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Stating", argp->buff);
3119 if (!doDig && !Squash(argp->buff))return vpEmsg("Stating", argp->buff);
3120
3121// Preform the actual function, we may been to add back the opaque info
3122//
3123 if (Request.stat.options & kXR_vfs)
3124 {if (opaque)
3125 {int n = strlen(argp->buff); argp->buff[n] = '?';
3126 if ((argp->buff)+n != opaque-1)
3127 memmove(&argp->buff[n+1], opaque, strlen(opaque)+1);
3128 }
3129 rc = osFS->fsctl(fsctl_cmd, argp->buff, myError, CRED);
3130 TRACEP(FS, "rc=" <<rc <<" statfs " <<argp->buff);
3131 if (rc == SFS_OK) Response.Send("");
3132 } else {
3133 if (doDig) rc = digFS->stat(argp->buff, &buf, myError, CRED, opaque);
3134 else rc = osFS->stat(argp->buff, &buf, myError, CRED, opaque);
3135 TRACEP(FS, "rc=" <<rc <<" stat " <<argp->buff);
3136 if (rc == SFS_OK) return Response.Send(xxBuff,
3137 StatGen(buf,xxBuff,sizeof(xxBuff)));
3138 }
3139 return fsError(rc, (doDig ? 0 : XROOTD_MON_STAT),myError,argp->buff,opaque);
3140}
3141
3142/******************************************************************************/
3143/* d o _ S t a t x */
3144/******************************************************************************/
3145
3146int XrdXrootdProtocol::do_Statx()
3147{
3148 static XrdXrootdCallBack statxCB("xstat", XROOTD_MON_STAT);
3149 int rc;
3150 char *path, *opaque, *respinfo = argp->buff;
3151 mode_t mode;
3152 XrdOucErrInfo myError(Link->ID,&statxCB,ReqID.getID(),Monitor.Did,clientPV);
3153 XrdOucTokenizer pathlist(argp->buff);
3154
3155// Check for static routing
3156//
3157 STATIC_REDIRECT(RD_stat);
3158
3159// Cycle through all of the paths in the list
3160//
3161 while((path = pathlist.GetLine()))
3162 {if (rpCheck(path, &opaque)) return rpEmsg("Stating", path);
3163 if (!Squash(path)) return vpEmsg("Stating", path);
3164 rc = osFS->stat(path, mode, myError, CRED, opaque);
3165 TRACEP(FS, "rc=" <<rc <<" stat " <<path);
3166 if (rc != SFS_OK)
3167 return fsError(rc, XROOTD_MON_STAT, myError, path, opaque);
3168 else {if (mode == (mode_t)-1) *respinfo = (char)kXR_offline;
3169 else if (S_ISDIR(mode)) *respinfo = (char)kXR_isDir;
3170 else *respinfo = (char)kXR_file;
3171 }
3172 respinfo++;
3173 }
3174
3175// Return result
3176//
3177 return Response.Send(argp->buff, respinfo-argp->buff);
3178}
3179
3180/******************************************************************************/
3181/* d o _ S y n c */
3182/******************************************************************************/
3183
3184int XrdXrootdProtocol::do_Sync()
3185{
3186 static XrdXrootdCallBack syncCB("sync", 0);
3187 int rc;
3188 XrdXrootdFile *fp;
3189 XrdXrootdFHandle fh(Request.sync.fhandle);
3190
3191// Keep Statistics
3192//
3193 SI->Bump(SI->syncCnt);
3194
3195// Find the file object
3196//
3197 if (!FTab || !(fp = FTab->Get(fh.handle)))
3198 return Response.Send(kXR_FileNotOpen,"sync does not refer to an open file");
3199
3200// The sync is elegible for a deferred response, indicate we're ok with that
3201//
3202 fp->XrdSfsp->error.setErrCB(&syncCB, ReqID.getID());
3203
3204// Sync the file
3205//
3206 rc = fp->XrdSfsp->sync();
3207 TRACEP(FS, "fh=" <<fh.handle <<" sync rc=" <<rc);
3208 if (SFS_OK != rc) return fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
3209
3210// Respond that all went well
3211//
3212 return Response.Send();
3213}
3214
3215/******************************************************************************/
3216/* d o _ T r u n c a t e */
3217/******************************************************************************/
3218
3219int XrdXrootdProtocol::do_Truncate()
3220{
3221 static XrdXrootdCallBack truncCB("trunc", 0);
3222 XrdXrootdFile *fp;
3223 XrdXrootdFHandle fh(Request.truncate.fhandle);
3224 long long theOffset;
3225 int rc;
3226
3227// Unmarshall the data
3228//
3229 n2hll(Request.truncate.offset, theOffset);
3230
3231// Check if this is a truncate for an open file (no path given)
3232//
3233 if (!Request.header.dlen)
3234 {
3235 // Update misc stats count
3236 //
3237 SI->Bump(SI->miscCnt);
3238
3239 // Find the file object
3240 //
3241 if (!FTab || !(fp = FTab->Get(fh.handle)))
3242 return Response.Send(kXR_FileNotOpen,
3243 "trunc does not refer to an open file");
3244
3245 // Truncate the file (it is eligible for async callbacks)
3246 //
3247 fp->XrdSfsp->error.setErrCB(&truncCB, ReqID.getID());
3248 rc = fp->XrdSfsp->truncate(theOffset);
3249 TRACEP(FS, "fh=" <<fh.handle <<" trunc rc=" <<rc <<" sz=" <<theOffset);
3250 if (SFS_OK != rc) return fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
3251
3252 } else {
3253
3254 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
3255 char *opaque;
3256
3257 // Check for static routing
3258 //
3259 STATIC_REDIRECT(RD_trunc);
3260
3261 // Verify the path and extract out the opaque information
3262 //
3263 if (rpCheck(argp->buff,&opaque)) return rpEmsg("Truncating",argp->buff);
3264 if (!Squash(argp->buff)) return vpEmsg("Truncating",argp->buff);
3265
3266 // Preform the actual function
3267 //
3268 rc = osFS->truncate(argp->buff, (XrdSfsFileOffset)theOffset, myError,
3269 CRED, opaque);
3270 TRACEP(FS, "rc=" <<rc <<" trunc " <<theOffset <<' ' <<argp->buff);
3271 if (SFS_OK != rc)
3272 return fsError(rc, XROOTD_MON_TRUNC, myError, argp->buff, opaque);
3273 }
3274
3275// Respond that all went well
3276//
3277 return Response.Send();
3278}
3279
3280/******************************************************************************/
3281/* d o _ W r i t e */
3282/******************************************************************************/
3283
3284int XrdXrootdProtocol::do_Write()
3285{
3286 int pathID;
3287 XrdXrootdFHandle fh(Request.write.fhandle);
3288 numWrites++;
3289
3290// Unmarshall the data
3291//
3292 IO.IOLen = Request.header.dlen;
3293 n2hll(Request.write.offset, IO.Offset);
3294 pathID = static_cast<int>(Request.write.pathid);
3295
3296// Find the file object. We will drain socket data on the control path only!
3297// .
3298 if (!FTab || !(IO.File = FTab->Get(fh.handle)))
3299 {IO.File = 0;
3300 return do_WriteNone(pathID);
3301 }
3302
3303// Trace and verify that length is not negative
3304//
3305 TRACEP(FSIO, pathID<<" fh="<<fh.handle<<" write "<<IO.IOLen<<'@'<<IO.Offset);
3306 if ( IO.IOLen < 0) return Response.Send(kXR_ArgInvalid,
3307 "Write length is negative");
3308
3309// If we are monitoring, insert a write entry
3310//
3311 if (Monitor.InOut())
3312 Monitor.Agent->Add_wr(IO.File->Stats.FileID, Request.write.dlen,
3313 Request.write.offset);
3314
3315// If zero length write, simply return
3316//
3317 if (!IO.IOLen) return Response.Send();
3318 IO.File->Stats.wrOps(IO.IOLen); // Optimistically correct
3319
3320// If async write allowed and it is a true write request (e.g. not chkpoint) and
3321// current conditions permit async; schedule the write to occur asynchronously
3322//
3323 if (IO.File->AsyncMode && Request.header.requestid == kXR_write
3324 && !as_syncw && IO.IOLen >= as_miniosz && srvrAioOps < as_maxpersrv)
3325 {if (myStalls < as_maxstalls)
3326 {if (pathID) return do_Offload(&XrdXrootdProtocol::do_WriteAio,pathID);
3327 return do_WriteAio();
3328 }
3329 SI->AsyncRej++;
3330 myStalls--;
3331 }
3332
3333// See if an alternate path is required
3334//
3335 if (pathID) return do_Offload(&XrdXrootdProtocol::do_WriteAll, pathID);
3336
3337// Just to the i/o now
3338//
3339 return do_WriteAll();
3340}
3341
3342/******************************************************************************/
3343/* d o _ W r i t e A i o */
3344/******************************************************************************/
3345
3346// IO.File = file to be written
3347// IO.Offset = Offset at which to write
3348// IO.IOLen = Number of bytes to read from socket and write to file
3349
3350int XrdXrootdProtocol::do_WriteAio()
3351{
3352 XrdXrootdNormAio *aioP;
3353
3354// Allocate an aio request object if client hasn't exceeded the link limit
3355//
3357 || !(aioP = XrdXrootdNormAio::Alloc(this, Response, IO.File)))
3358 {SI->AsyncRej++;
3359 if (myStalls > 0) myStalls--;
3360 return do_WriteAll();
3361 }
3362
3363// Issue the write request
3364//
3365 return aioP->Write(IO.Offset, IO.IOLen);
3366}
3367
3368/******************************************************************************/
3369/* d o _ W r i t e A l l */
3370/******************************************************************************/
3371
3372// IO.File = file to be written
3373// IO.Offset = Offset at which to write
3374// IO.IOLen = Number of bytes to read from socket and write to file
3375
3376int XrdXrootdProtocol::do_WriteAll()
3377{
3378 int rc, Quantum = (IO.IOLen > maxBuffsz ? maxBuffsz : IO.IOLen);
3379
3380// Make sure we have a large enough buffer
3381//
3382 if (!argp || Quantum < halfBSize || Quantum > argp->bsize)
3383 {if ((rc = getBuff(0, Quantum)) <= 0) return rc;}
3384 else if (hcNow < hcNext) hcNow++;
3385
3386// Now write all of the data (XrdXrootdProtocol.C defines getData())
3387//
3388 while(IO.IOLen > 0)
3389 {if ((rc = getData("data", argp->buff, Quantum)))
3390 {if (rc > 0)
3391 {Resume = &XrdXrootdProtocol::do_WriteCont;
3392 myBlast = Quantum;
3393 }
3394 return rc;
3395 }
3396 if ((rc = IO.File->XrdSfsp->write(IO.Offset, argp->buff, Quantum)) < 0)
3397 {IO.IOLen = IO.IOLen-Quantum; IO.EInfo[0] = rc; IO.EInfo[1] = 0;
3398 return do_WriteNone();
3399 }
3400 IO.Offset += Quantum; IO.IOLen -= Quantum;
3401 if (IO.IOLen < Quantum) Quantum = IO.IOLen;
3402 }
3403
3404// All done
3405//
3406 return Response.Send();
3407}
3408
3409/******************************************************************************/
3410/* d o _ W r i t e C o n t */
3411/******************************************************************************/
3412
3413// IO.File = file to be written
3414// IO.Offset = Offset at which to write
3415// IO.IOLen = Number of bytes to read from socket and write to file
3416// myBlast = Number of bytes already read from the socket
3417
3418int XrdXrootdProtocol::do_WriteCont()
3419{
3420 int rc;
3421
3422// Write data that was finaly finished comming in
3423//
3424 if ((rc = IO.File->XrdSfsp->write(IO.Offset, argp->buff, myBlast)) < 0)
3425 {IO.IOLen = IO.IOLen-myBlast; IO.EInfo[0] = rc; IO.EInfo[1] = 0;
3426 return do_WriteNone();
3427 }
3428 IO.Offset += myBlast; IO.IOLen -= myBlast;
3429
3430// See if we need to finish this request in the normal way
3431//
3432 if (IO.IOLen > 0) return do_WriteAll();
3433 return Response.Send();
3434}
3435
3436/******************************************************************************/
3437/* d o _ W r i t e N o n e */
3438/******************************************************************************/
3439
3440int XrdXrootdProtocol::do_WriteNone()
3441{
3442 char *buff, dbuff[4096];
3443 int rlen, blen;
3444
3445// Determine which buffer we will use
3446//
3447 if (argp && argp->bsize > (int)sizeof(dbuff))
3448 {buff = argp->buff;
3449 blen = argp->bsize;
3450 } else {
3451 buff = dbuff;
3452 blen = sizeof(dbuff);
3453 }
3454 if (IO.IOLen < blen) blen = IO.IOLen;
3455
3456// Discard any data being transmitted
3457//
3458 TRACEP(REQ, "discarding " <<IO.IOLen <<" bytes");
3459 while(IO.IOLen > 0)
3460 {rlen = Link->Recv(buff, blen, readWait);
3461 if (rlen < 0) return Link->setEtext("link read error");
3462 IO.IOLen -= rlen;
3463 if (rlen < blen)
3464 {myBlen = 0;
3465 Resume = &XrdXrootdProtocol::do_WriteNone;
3466 return 1;
3467 }
3468 if (IO.IOLen < blen) blen = IO.IOLen;
3469 }
3470
3471// Send final message
3472//
3473 return do_WriteNoneMsg();
3474}
3475
3476/******************************************************************************/
3477
3478int XrdXrootdProtocol::do_WriteNone(int pathID, XErrorCode ec,
3479 const char *emsg)
3480{
3481// We can't recover when the data is arriving on a foriegn bound path as there
3482// no way to properly drain the socket. So, we terminate the connection.
3483//
3484 if (pathID != PathID)
3485 {if (ec && emsg) Response.Send(ec, emsg);
3486 else do_WriteNoneMsg();
3487 return Link->setEtext("write protocol violation");
3488 }
3489
3490// Set error code if present
3491//
3492 if (ec != kXR_noErrorYet)
3493 {IO.EInfo[1] = ec;
3494 if (IO.File)
3495 {if (!emsg) emsg = XProtocol::errName(ec);
3496 IO.File->XrdSfsp->error.setErrInfo(0, emsg);
3497 }
3498 }
3499
3500// Otherwise, continue to darin the socket
3501//
3502 return do_WriteNone();
3503}
3504
3505/******************************************************************************/
3506/* d o _ W r i t e N o n e M s g */
3507/******************************************************************************/
3508
3509int XrdXrootdProtocol::do_WriteNoneMsg()
3510{
3511// Send our the error message and return
3512//
3513 if (!IO.File) return
3514 Response.Send(kXR_FileNotOpen,"write does not refer to an open file");
3515
3516 if (IO.EInfo[1])
3517 return Response.Send((XErrorCode)IO.EInfo[1],
3518 IO.File->XrdSfsp->error.getErrText());
3519
3520 if (IO.EInfo[0]) return fsError(IO.EInfo[0], 0, IO.File->XrdSfsp->error, 0, 0);
3521
3522 return Response.Send(kXR_FSError, IO.File->XrdSfsp->error.getErrText());
3523}
3524
3525/******************************************************************************/
3526/* d o _ W r i t e S p a n */
3527/******************************************************************************/
3528
3530{
3531 int rc;
3532 XrdXrootdFHandle fh(Request.write.fhandle);
3533 numWrites++;
3534
3535// Unmarshall the data
3536//
3537 IO.IOLen = Request.header.dlen;
3538 n2hll(Request.write.offset, IO.Offset);
3539
3540// Find the file object. We will only drain socket data on the control path.
3541// .
3542 if (!FTab || !(IO.File = FTab->Get(fh.handle)))
3543 {IO.IOLen -= myBlast;
3544 IO.File = 0;
3545 return do_WriteNone(Request.write.pathid);
3546 }
3547
3548// If we are monitoring, insert a write entry
3549//
3550 if (Monitor.InOut())
3551 Monitor.Agent->Add_wr(IO.File->Stats.FileID, Request.write.dlen,
3552 Request.write.offset);
3553 IO.File->Stats.wrOps(IO.IOLen); // Optimistically correct
3554
3555// Trace this entry
3556//
3557 TRACEP(FSIO, "fh=" <<fh.handle <<" write " <<IO.IOLen <<'@' <<IO.Offset);
3558
3559// Write data that was already read
3560//
3561 if ((rc = IO.File->XrdSfsp->write(IO.Offset, myBuff, myBlast)) < 0)
3562 {IO.IOLen = IO.IOLen-myBlast; IO.EInfo[0] = rc; IO.EInfo[1] = 0;
3563 return do_WriteNone();
3564 }
3565 IO.Offset += myBlast; IO.IOLen -= myBlast;
3566
3567// See if we need to finish this request in the normal way
3568//
3569 if (IO.IOLen > 0) return do_WriteAll();
3570 return Response.Send();
3571}
3572
3573/******************************************************************************/
3574/* d o _ W r i t e V */
3575/******************************************************************************/
3576
3577int XrdXrootdProtocol::do_WriteV()
3578{
3579// This will write multiple buffers at the same time in an attempt to avoid
3580// the disk latency. The information with the offsets and lengths of the data
3581// to write is passed as a data buffer. We attempt to optimize as best as
3582// possible, though certain combinations may result in multiple writes. Since
3583// socket flushing is nearly impossible when an error occurs, most errors
3584// simply terminate the connection.
3585//
3586 const int wveSZ = sizeof(XrdProto::write_list);
3587 struct trackInfo
3588 {XrdXrootdWVInfo **wvInfo; bool doit;
3589 trackInfo(XrdXrootdWVInfo **wvP) : wvInfo(wvP), doit(true) {}
3590 ~trackInfo() {if (doit && *wvInfo) {free(*wvInfo); *wvInfo = 0;}}
3591 } freeInfo(&wvInfo);
3592
3593 struct XrdProto::write_list *wrLst;
3594 XrdOucIOVec *wrVec;
3595 long long totSZ, maxSZ;
3596 int curFH, k, Quantum, wrVecNum, wrVecLen = Request.header.dlen;
3597
3598// Compute number of elements in the write vector and make sure we have no
3599// partial elements.
3600//
3601 wrVecNum = wrVecLen / wveSZ;
3602 if ( (wrVecLen <= 0) || (wrVecNum*wveSZ != wrVecLen) )
3603 {Response.Send(kXR_ArgInvalid, "Write vector is invalid");
3604 return -1;
3605 }
3606
3607// Make sure that we can make a copy of the read vector. So, we impose a limit
3608// on it's size.
3609//
3610 if (wrVecNum > XrdProto::maxWvecsz)
3611 {Response.Send(kXR_ArgTooLong, "Write vector is too long");
3612 return -1;
3613 }
3614
3615// Create the verctor write information structure sized as needed.
3616//
3617 if (wvInfo) free(wvInfo);
3618 wvInfo = (XrdXrootdWVInfo *)malloc(sizeof(XrdXrootdWVInfo) +
3619 sizeof(XrdOucIOVec)*(wrVecNum-1));
3620 memset(wvInfo, 0, sizeof(XrdXrootdWVInfo) - sizeof(XrdOucIOVec));
3621 wvInfo->wrVec = wrVec = wvInfo->ioVec;
3622
3623// Run down the list and compute the total size of the write. No individual
3624// write may be greater than the maximum transfer size. We also use this loop
3625// to copy the write list to our writev vector for later processing.
3626//
3627 wrLst = (XrdProto::write_list *)argp->buff;
3628 totSZ = 0; maxSZ = 0; k = 0; Quantum = maxTransz; curFH = 0;
3629 for (int i = 0; i < wrVecNum; i++)
3630 {if (wrLst[i].wlen == 0) continue;
3631 memcpy(&wrVec[k].info, wrLst[i].fhandle, sizeof(int));
3632 wrVec[k].size = ntohl(wrLst[i].wlen);
3633 if (wrVec[k].size < 0)
3634 {Response.Send(kXR_ArgInvalid, "Writev length is negtive");
3635 return -1;
3636 }
3637 if (wrVec[k].size > Quantum)
3638 {Response.Send(kXR_NoMemory,"Single writev transfer is too large");
3639 return -1;
3640 }
3641 wrVec[k].offset = ntohll(wrLst[i].offset);
3642 if (wrVec[k].info == curFH) totSZ += wrVec[k].size;
3643 else {if (maxSZ < totSZ) maxSZ = totSZ;
3644 totSZ = wrVec[k].size;
3645 }
3646 k++;
3647 }
3648
3649// Check if we are not actually writing anything, simply return success
3650//
3651 if (maxSZ < totSZ) maxSZ = totSZ;
3652 if (maxSZ == 0) return Response.Send();
3653
3654// So, now we account for the number of writev requests and total segments
3655//
3656 numWritV++; numSegsW += k; wrVecNum = k;
3657
3658// Calculate the transfer unit which will be the smaller of the maximum
3659// transfer unit and the actual amount we need to transfer.
3660//
3661 if (maxSZ > maxTransz) Quantum = maxTransz;
3662 else Quantum = static_cast<int>(maxSZ);
3663
3664// Now obtain the right size buffer
3665//
3666 if ((Quantum < halfBSize && Quantum > 1024) || Quantum > argp->bsize)
3667 {if (getBuff(0, Quantum) <= 0) return -1;}
3668 else if (hcNow < hcNext) hcNow++;
3669
3670// Check that we really have at least the first file open (part of setup)
3671//
3672 if (!FTab || !(IO.File = FTab->Get(wrVec[0].info)))
3673 {Response.Send(kXR_FileNotOpen, "writev does not refer to an open file");
3674 return -1;
3675 }
3676
3677// Setup to do the complete transfer
3678//
3679 wvInfo->curFH = wrVec[0].info;
3680 wvInfo->vBeg = 0;
3681 wvInfo->vPos = 0;
3682 wvInfo->vEnd = wrVecNum;
3683 wvInfo->vMon = 0;
3684 wvInfo->doSync= (Request.writev.options & ClientWriteVRequest::doSync) != 0;
3685 wvInfo->wvMon = Monitor.InOut();
3686 wvInfo->ioMon = (wvInfo->vMon > 1);
3687// wvInfo->vType = (wvInfo->ioMon ? XROOTD_MON_WRITEU : XROOTD_MON_WRITEV);
3688 IO.WVBytes = 0;
3689 IO.IOLen = wrVec[0].size;
3690 myBuff = argp->buff;
3691 myBlast = 0;
3692
3693// Now we simply start the write operations if this is a true writev request.
3694// Otherwise return to the caller for additional processing.
3695//
3696 freeInfo.doit = false;
3697 if (Request.header.requestid == kXR_writev) return do_WriteVec();
3698 return 0;
3699}
3700
3701/******************************************************************************/
3702/* d o _ W r i t e V e c */
3703/******************************************************************************/
3704
3705int XrdXrootdProtocol::do_WriteVec()
3706{
3707 XrdSfsXferSize xfrSZ;
3708 int rc, wrVNum, vNow = wvInfo->vPos;
3709 bool done, newfile;
3710
3711// Read the complete data from the socket for the current element. Note that
3712// should we enter a resume state; upon re-entry all of the data will be read.
3713//
3714do{if (IO.IOLen > 0)
3715 {wvInfo->wrVec[vNow].data = argp->buff + myBlast;
3716 myBlast += IO.IOLen;
3717 if ((rc = getData("data", myBuff, IO.IOLen)))
3718 {if (rc < 0) return rc;
3719 IO.IOLen = 0;
3720 Resume = &XrdXrootdProtocol::do_WriteVec;
3721 return rc;
3722 }
3723 }
3724
3725// Establish the state at this point as this will tell us what to do next.
3726//
3727 vNow++;
3728 done = newfile = false;
3729 if (vNow >= wvInfo->vEnd) done = true;
3730 else if (wvInfo->wrVec[vNow].info != wvInfo->curFH) newfile = true;
3731 else if (myBlast + wvInfo->wrVec[vNow].size <= argp->bsize)
3732 {IO.IOLen = wvInfo->wrVec[vNow].size;
3733 myBuff = argp->buff + myBlast;
3734 wvInfo->vPos = vNow;
3735 continue;
3736 }
3737
3738// We need to write out what we have.
3739//
3740 wrVNum = vNow - wvInfo->vBeg;
3741 xfrSZ = IO.File->XrdSfsp->writev(&(wvInfo->wrVec[wvInfo->vBeg]), wrVNum);
3742 TRACEP(FSIO,"fh=" <<wvInfo->curFH <<" writeV " << xfrSZ <<':' <<wrVNum);
3743 if (xfrSZ != myBlast) break;
3744
3745// Check if we need to do monitoring or a sync with no deferal. Note that
3746// we currently do not support detailed monitoring for vector writes!
3747//
3748 if (done || newfile)
3749 {int monVnum = vNow - wvInfo->vMon;
3750 IO.File->Stats.wvOps(IO.WVBytes, monVnum);
3760 wvInfo->vMon = vNow;
3761 IO.WVBytes = 0;
3762 if (wvInfo->doSync)
3763 {IO.File->XrdSfsp->error.setErrCB(0,0);
3764 xfrSZ = IO.File->XrdSfsp->sync();
3765 if (xfrSZ< 0) break;
3766 }
3767 }
3768
3769// If we are done, the finish up
3770//
3771 if (done)
3772 {if (wvInfo) {free(wvInfo); wvInfo = 0;}
3773 return Response.Send();
3774 }
3775
3776// Sequence to a new file if we need to do so
3777//
3778 if (newfile)
3779 {if (!FTab || !(IO.File = FTab->Get(wvInfo->wrVec[vNow].info)))
3780 {Response.Send(kXR_FileNotOpen,"writev does not refer to an open file");
3781 return -1;
3782 }
3783 wvInfo->curFH = wvInfo->wrVec[vNow].info;
3784 }
3785
3786// Setup to resume transfer
3787//
3788 myBlast = 0;
3789 myBuff = argp->buff;
3790 IO.IOLen = wvInfo->wrVec[vNow].size;
3791 wvInfo->vBeg = vNow;
3792 wvInfo->vPos = vNow;
3793
3794} while(true);
3795
3796// If we got here then there was a write error (file pointer is valid).
3797//
3798 if (wvInfo) {free(wvInfo); wvInfo = 0;}
3799 return fsError((int)xfrSZ, 0, IO.File->XrdSfsp->error, 0, 0);
3800}
3801
3802/******************************************************************************/
3803/* S e n d F i l e */
3804/******************************************************************************/
3805
3807{
3808
3809// Make sure we have some data to send
3810//
3811 if (!IO.IOLen) return 1;
3812
3813// Send off the data
3814//
3815 IO.IOLen = Response.Send(fildes, IO.Offset, IO.IOLen);
3816 return IO.IOLen;
3817}
3818
3819/******************************************************************************/
3820
3822{
3823 int i, xframt = 0;
3824
3825// Make sure we have some data to send
3826//
3827 if (!IO.IOLen) return 1;
3828
3829// Verify the length, it can't be greater than what the client wants
3830//
3831 for (i = 1; i < sfvnum; i++) xframt += sfvec[i].sendsz;
3832 if (xframt > IO.IOLen) return 1;
3833
3834// Send off the data
3835//
3836 if (xframt) IO.IOLen = Response.Send(sfvec, sfvnum, xframt);
3837 else {IO.IOLen = 0; Response.Send();}
3838 return IO.IOLen;
3839}
3840
3841/******************************************************************************/
3842/* S e t F D */
3843/******************************************************************************/
3844
3846{
3847 if (fildes < 0) IO.File->sfEnabled = 0;
3848 else IO.File->fdNum = fildes;
3849}
3850
3851/******************************************************************************/
3852/* U t i l i t y M e t h o d s */
3853/******************************************************************************/
3854/******************************************************************************/
3855/* f s E r r o r */
3856/******************************************************************************/
3857
3858int XrdXrootdProtocol::fsError(int rc, char opC, XrdOucErrInfo &myError,
3859 const char *Path, char *Cgi)
3860{
3861 int ecode, popt, rs;
3862 const char *eMsg = myError.getErrText(ecode);
3863
3864// Process standard errors
3865//
3866 if (rc == SFS_ERROR)
3867 {SI->errorCnt++;
3868 rc = XProtocol::mapError(ecode);
3869
3870 if (Path && (rc == kXR_Overloaded) && (opC == XROOTD_MON_OPENR
3871 || opC == XROOTD_MON_OPENW || opC == XROOTD_MON_OPENC))
3872 {if (myError.extData()) myError.Reset();
3873 return fsOvrld(opC, Path, Cgi);
3874 }
3875
3876 if (Path && (rc == kXR_NotFound) && RQLxist && opC
3877 && (popt = RQList.Validate(Path)))
3880 Route[popt].Host[rdType],
3881 Route[popt].Port[rdType],
3883 if (Cgi) rs = fsRedirNoEnt(eMsg, Cgi, popt);
3884 else rs = Response.Send(kXR_redirect,
3885 Route[popt].Port[rdType],
3886 Route[popt].Host[rdType]);
3887 } else rs = Response.Send((XErrorCode)rc, eMsg);
3888 if (myError.extData()) myError.Reset();
3889 return rs;
3890 }
3891
3892// Process the redirection (error msg is host:port)
3893//
3894 if (rc == SFS_REDIRECT)
3895 {SI->redirCnt++;
3896 // if the plugin set some redirect flags but the client does not
3897 // support them, clear the flags (set -1)
3898 if( ecode < -1 && !( clientPV & XrdOucEI::uRedirFlgs ) )
3899 ecode = -1;
3900 if (XrdXrootdMonitor::Redirect() && Path && opC)
3902 if (TRACING(TRACE_REDIR))
3903 {if (ecode < 0)
3904 {TRACEI(REDIR, Response.ID() <<"redirecting to " << eMsg);}
3905 else {TRACEI(REDIR, Response.ID() <<"redirecting to "
3906 << eMsg <<':' <<ecode);
3907 }
3908 }
3909 if (RedirPI) rs = fsRedirPI(eMsg, ecode, myError.getErrTextLen());
3910 else rs = Response.Send(kXR_redirect, ecode, eMsg,
3911 myError.getErrTextLen());
3912 if (myError.extData()) myError.Reset();
3913 return rs;
3914 }
3915
3916// Process the deferal. We also synchronize sending the deferal response with
3917// sending the actual deferred response by calling Done() in the callback object.
3918// This allows the requestor of he callback know that we actually send the
3919// kXR_waitresp to the end client and avoid violating time causality.
3920//
3921 if (rc == SFS_STARTED)
3922 {SI->stallCnt++;
3923 if (ecode <= 0) ecode = 1800;
3924 TRACEI(STALL, Response.ID() <<"delaying client up to " <<ecode <<" sec");
3925 rc = Response.Send(kXR_waitresp, ecode, eMsg);
3926 if (myError.getErrCB()) myError.getErrCB()->Done(ecode, &myError);
3927 if (myError.extData()) myError.Reset();
3928 return (rc ? rc : 1);
3929 }
3930
3931// Process the data response
3932//
3933 if (rc == SFS_DATA)
3934 {if (ecode) rs = Response.Send((void *)eMsg, ecode);
3935 else rs = Response.Send();
3936 if (myError.extData()) myError.Reset();
3937 return rs;
3938 }
3939
3940// Process the data response via an iovec
3941//
3942 if (rc == SFS_DATAVEC)
3943 {if (ecode < 2) rs = Response.Send();
3944 else rs = Response.Send((struct iovec *)eMsg, ecode);
3945 if (myError.getErrCB()) myError.getErrCB()->Done(ecode, &myError);
3946 if (myError.extData()) myError.Reset();
3947 return rs;
3948 }
3949
3950// Process the deferal
3951//
3952 if (rc >= SFS_STALL)
3953 {SI->stallCnt++;
3954 TRACEI(STALL, Response.ID() <<"stalling client for " <<rc <<" sec");
3955 rs = Response.Send(kXR_wait, rc, eMsg);
3956 if (myError.extData()) myError.Reset();
3957 return rs;
3958 }
3959
3960// Unknown conditions, report it
3961//
3962 {char buff[32];
3963 SI->errorCnt++;
3964 sprintf(buff, "%d", rc);
3965 eDest.Emsg("Xeq", "Unknown error code", buff, eMsg);
3966 rs = Response.Send(kXR_ServerError, eMsg);
3967 if (myError.extData()) myError.Reset();
3968 return rs;
3969 }
3970}
3971
3972/******************************************************************************/
3973/* f s O v r l d */
3974/******************************************************************************/
3975
3976int XrdXrootdProtocol::fsOvrld(char opC, const char *Path, char *Cgi)
3977{
3978 static const char *prot = "root://";
3979 static int negOne = -1;
3980 static char quest = '?', slash = '/';
3981
3982 struct iovec rdrResp[8];
3983 char *destP=0, dest[512];
3984 int iovNum=0, pOff, port;
3985
3986// If this is a forwarded path and the client can handle full url's then
3987// redirect the client to the destination in the path. Otherwise, if there is
3988// an alternate destination, send client there. Otherwise, stall the client.
3989//
3991 && (pOff = XrdOucUtils::isFWD(Path, &port, dest, sizeof(dest))))
3992 { rdrResp[1].iov_base = (char *)&negOne;
3993 rdrResp[1].iov_len = sizeof(negOne);
3994 rdrResp[2].iov_base = (char *)prot;
3995 rdrResp[2].iov_len = 7; // root://
3996 rdrResp[3].iov_base = (char *)dest;
3997 rdrResp[3].iov_len = strlen(dest); // host:port
3998 rdrResp[4].iov_base = (char *)&slash;
3999 rdrResp[4].iov_len = (*Path == '/' ? 1 : 0); // / or nil for objid
4000 rdrResp[5].iov_base = (char *)(Path+pOff);
4001 rdrResp[5].iov_len = strlen(Path+pOff); // path
4002 if (Cgi && *Cgi)
4003 {rdrResp[6].iov_base = (char *)&quest;
4004 rdrResp[6].iov_len = sizeof(quest); // ?
4005 rdrResp[7].iov_base = (char *)Cgi;
4006 rdrResp[7].iov_len = strlen(Cgi); // cgi
4007 iovNum = 8;
4008 } else iovNum = 6;
4009 destP = dest;
4010 } else if ((destP = Route[RD_ovld].Host[rdType]))
4011 port = Route[RD_ovld].Port[rdType];
4012
4013// If a redirect happened, then trace it.
4014//
4015 if (destP)
4016 {SI->redirCnt++;
4018 XrdXrootdMonitor::Redirect(Monitor.Did, destP, port,
4020 if (iovNum)
4021 {TRACEI(REDIR, Response.ID() <<"redirecting to "<<dest);
4022 return Response.Send(kXR_redirect, rdrResp, iovNum);
4023 } else {
4024 TRACEI(REDIR, Response.ID() <<"redirecting to "<<destP<<':'<<port);
4025 return Response.Send(kXR_redirect, port, destP);
4026 }
4027 }
4028
4029// If there is a stall value, then delay the client
4030//
4031 if (OD_Stall)
4032 {TRACEI(STALL, Response.ID()<<"stalling client for "<<OD_Stall<<" sec");
4033 SI->stallCnt++;
4034 return Response.Send(kXR_wait, OD_Stall, "server is overloaded");
4035 }
4036
4037// We were unsuccessful, return overload as an error
4038//
4039 return Response.Send(kXR_Overloaded, "server is overloaded");
4040}
4041
4042/******************************************************************************/
4043/* f s R e d i r N o E n t */
4044/******************************************************************************/
4045
4046int XrdXrootdProtocol::fsRedirNoEnt(const char *eMsg, char *Cgi, int popt)
4047{
4048 struct iovec ioV[4];
4049 char *tried, *trend, *ptried = 0;
4050 kXR_int32 pnum = htonl(static_cast<kXR_int32>(Route[popt].Port[rdType]));
4051 int tlen;
4052
4053// Try to find the last tried token in the cgi
4054//
4055 if ((trend = Cgi))
4056 {do {if (!(tried = strstr(Cgi, "tried="))) break;
4057 if (tried == trend || *(tried-1) == '&')
4058 {if (!ptried || (*(tried+6) && *(tried+6) != '&')) ptried=tried;}
4059 Cgi = index(tried+6, '&');
4060 } while(Cgi);
4061 }
4062
4063// If we did find a tried, bracket it out with a leading comma (we can modify
4064// the passed cgi string here because this is the last time it will be used.
4065//
4066 if ((tried = ptried))
4067 {tried += 5;
4068 while(*(tried+1) && *(tried+1) == ',') tried++;
4069 trend = index(tried, '&');
4070 if (trend) {tlen = trend - tried; *trend = 0;}
4071 else tlen = strlen(tried);
4072 *tried = ',';
4073 } else tlen = 0;
4074
4075// Check if we are in a redirect loop (i.e. we are listed in the client's cgi).
4076// If so, then treat this and file not found as we've been here before.
4077//
4078 if ((trend = tried) && eMsg)
4079 do {if ((trend = strstr(trend, myCName)))
4080 {if (*(trend+myCNlen) == '\0' || *(trend+myCNlen) == ',')
4081 return Response.Send(kXR_NotFound, eMsg);
4082 trend = index(trend+myCNlen, ',');
4083 }
4084 } while(trend);
4085
4086
4087// If we have not found a tried token or that token far too large to propogate
4088// (i.e. it's likely we have an undetected loop), then do a simple redirect.
4089//
4090 if (!tried || !tlen || tlen > 16384)
4091 return Response.Send(kXR_redirect,
4092 Route[popt].Port[rdType],
4093 Route[popt].Host[rdType]);
4094
4095// We need to append the client's tried list to the one we have to avoid loops
4096//
4097
4098 ioV[1].iov_base = (char *)&pnum;
4099 ioV[1].iov_len = sizeof(pnum);
4100 ioV[2].iov_base = Route[popt].Host[rdType];
4101 ioV[2].iov_len = Route[popt].RDSz[rdType];
4102 ioV[3].iov_base = tried;
4103 ioV[3].iov_len = tlen;
4104
4105// Compute total length
4106//
4107 tlen += sizeof(pnum) + Route[popt].RDSz[rdType];
4108
4109// Send off the redirect
4110//
4111 return Response.Send(kXR_redirect, ioV, 4, tlen);
4112}
4113
4114/******************************************************************************/
4115/* f s R e d i r I P */
4116/******************************************************************************/
4117
4118namespace XrdXrootd
4119{
4120 struct netInfo
4123 char* netID;
4124 time_t expTime = 0;
4126
4127 netInfo(const char*id) : netID(strdup(id)) {}
4128 ~netInfo() {if (netID) free(netID);}
4129 };
4130}
4131
4132XrdXrootd::netInfo* XrdXrootdProtocol::fsRedirIP(const char *netID, int port)
4133{
4134 auto cmpchr = [](const char* a, const char* b) {return strcmp(a,b)<0;};
4135 static std::map<const char*,XrdXrootd::netInfo*,decltype(cmpchr)> niMap(cmpchr);
4136 static XrdSysMutex niMapMtx;
4137
4138 XrdXrootd::netInfo* niP;
4139
4140// First, chek if we have an entry for this item. We need a lock because
4141// some oher thread may be adding a node to the map. Nodes are never deleted.
4142//
4143 niMapMtx.Lock();
4144 auto it = niMap.find(netID);
4145 if (it == niMap.end())
4146 {niP = new XrdXrootd::netInfo(netID);
4147 niMap[niP->netID] = niP;
4148 } else niP = it->second;
4149 niMapMtx.UnLock();
4150 niP->niMutex.Lock();
4151
4152// Validate/initialize the corresponding netaddr object. For newly allocated
4153// objects it is always done. For pre-exiting objects only when they expire.
4154// Note: when expTime is zero then refs must be 1 and this is a 1st time init,
4155//
4156 time_t nowT = time(0);
4157 niP->refs++;
4158 if (niP->expTime <= nowT && niP->refs == 1)
4159 {const char* eTxt = niP->netAddr.Set(netID, port);
4160 if (eTxt)
4161 {if (niP->expTime == 0)
4162 {eDest.Emsg("RedirIP", "Unable to init NetInfo for", netID, eTxt);
4163 niP->refs--;
4164 niP->niMutex.UnLock();
4165 return 0;
4166 }
4167 eDest.Emsg("RedirIP", "Unable to refresh NetInfo for", netID, eTxt);
4168 niP->expTime += 60;
4169 } else niP->expTime = nowT + redirIPHold;
4170 }
4171
4172// We have valid network info on the target
4173//
4174 niP->niMutex.UnLock();
4175 return niP;
4176}
4177
4178/******************************************************************************/
4179/* f s R e d i r P I */
4180/******************************************************************************/
4181
4182int XrdXrootdProtocol::fsRedirPI(const char *trg, int port, int trglen)
4183{
4184 struct THandle
4185 {XrdXrootd::netInfo* Info;
4186 THandle() : Info(0) {}
4187 ~THandle() {if (Info) Info->refs--;}
4188 } T;
4189 std::string Target;
4190 int newPort = port;
4191
4192// Handle the most comman case first - a simple host and port.
4193//
4194 if (port >= 0)
4195 {std::string TDst;
4196 const char* TCgi = index(trg, '?');
4197 if (!TCgi) {TCgi = ""; TDst = trg;}
4198 else TDst.assign(trg, TCgi-trg);
4199 T.Info = fsRedirIP(TDst.c_str(), port);
4200 if (!T.Info) return Response.Send(kXR_redirect, port, trg, trglen);
4201 uint16_t TPort = static_cast<uint16_t>(newPort);
4202 Target = RedirPI->Redirect(TDst.c_str(), TPort, TCgi, T.Info->netAddr,
4203 *(Link->AddrInfo()));
4204 newPort = static_cast<int>(TPort);
4205 } else {
4206
4207// This is a url which requires additional handling. If the url is not
4208// valid we skip calling the plugin as the situation is not salvageable
4209// and the client will complain. We also require that the host and optional
4210// port be atleast two characters long.
4211//
4212 std::string urlHead, TDst, urlPort;
4213 const char* urlTail;
4214 const char* hBeg = strstr(trg, "://");
4215 if (!hBeg)
4216 {eDest.Emsg("RedirPI", "Invalid redirect URL -", trg);
4217 return Response.Send(kXR_redirect, port, trg, trglen);
4218 }
4219 hBeg += 3;
4220 urlHead.assign(trg, hBeg-trg);
4221 urlTail = strstr(hBeg, "/");
4222 if (!urlTail) {urlTail = ""; TDst = hBeg;}
4223 else {if (urlTail-hBeg < 3)
4224 {eDest.Emsg("RedirPI", "Mlalformed URL -", trg);
4225 return Response.Send(kXR_redirect, port, trg, trglen);
4226 }
4227 TDst.assign(hBeg, urlTail-hBeg);
4228 }
4229 T.Info = fsRedirIP(TDst.c_str(), port);
4230 if (!T.Info) return Response.Send(kXR_redirect, port, trg, trglen);
4231 size_t colon = TDst.find(":");
4232 if (colon != std::string::npos)
4233 {urlPort.assign(TDst, colon+1, std::string::npos);
4234 TDst.erase(colon);
4235 }
4236 Target = RedirPI->RedirectURL(urlHead.c_str(), Target.c_str(),
4237 urlPort.c_str(), urlTail, newPort,
4238 T.Info->netAddr, *(Link->AddrInfo()));
4239 if (port == -1 || newPort >= 0) newPort = port;
4240 }
4241
4242// Handle the result of calling the plugin
4243//
4244 if (!Target.size()) return Response.Send(kXR_redirect, port, trg, trglen);
4245
4246 if (Target.front() != '!')
4247 {TRACEI(REDIR, Response.ID() <<"plugin redirects to "
4248 <<Target.c_str() <<" portarg="<<newPort);
4249
4250 return Response.Send(kXR_redirect,port,Target.c_str(),Target.size());
4251 }
4252
4253// The redirect plgin enountered an error, so we bail.
4254//
4255 char mbuff[1024];
4256 snprintf(mbuff,sizeof(mbuff),"Redirect failed; %s",Target.c_str());
4257 eDest.Emsg("Xeq_RedirPI", mbuff);
4258 return Response.Send(kXR_ServerError, mbuff);
4259}
4260
4261/******************************************************************************/
4262/* g e t B u f f */
4263/******************************************************************************/
4264
4265int XrdXrootdProtocol::getBuff(const int isRead, int Quantum)
4266{
4267
4268// Check if we need to really get a new buffer
4269//
4270 if (!argp || Quantum > argp->bsize) hcNow = hcPrev;
4271 else if (Quantum >= halfBSize || hcNow-- > 0) return 1;
4272 else if (hcNext >= hcMax) hcNow = hcMax;
4273 else {int tmp = hcPrev;
4274 hcNow = hcNext;
4275 hcPrev = hcNext;
4276 hcNext = tmp+hcNext;
4277 }
4278
4279// Get a new buffer
4280//
4281 if (argp) BPool->Release(argp);
4282 if ((argp = BPool->Obtain(Quantum))) halfBSize = argp->bsize >> 1;
4283 else return Response.Send(kXR_NoMemory, (isRead ?
4284 "insufficient memory to read file" :
4285 "insufficient memory to write file"));
4286
4287// Success
4288//
4289 return 1;
4290}
4291
4292/******************************************************************************/
4293/* Private: g e t C k s T y p e */
4294/******************************************************************************/
4295
4296char *XrdXrootdProtocol::getCksType(char *opaque, char *cspec, int cslen)
4297{
4298 char *cksT;
4299
4300// Get match for user specified checksum type, if any. Otherwise return default.
4301//
4302 if (opaque && *opaque)
4303 {XrdOucEnv jobEnv(opaque);
4304 if ((cksT = jobEnv.Get("cks.type")))
4305 {XrdOucTList *tP = JobCKTLST;
4306 while(tP && strcasecmp(tP->text, cksT)) tP = tP->next;
4307 if (!tP && cspec) snprintf(cspec, cslen, "%s", cksT);
4308 return (tP ? tP->text : 0);
4309 }
4310 }
4311
4312// Return default
4313//
4314 return JobCKT;
4315}
4316
4317/******************************************************************************/
4318/* Private: l o g L o g i n */
4319/******************************************************************************/
4320
4321bool XrdXrootdProtocol::logLogin(bool xauth)
4322{
4323 const char *uName, *ipName, *tMsg, *zMsg = "";
4324 char lBuff[512], pBuff[512];
4325
4326// Determine ip type
4327//
4329 ipName = (clientPV & XrdOucEI::uIPv64 ? "IP46" : "IPv4");
4330 else ipName = (clientPV & XrdOucEI::uIPv64 ? "IP64" : "IPv6");
4331
4332// Determine client name
4333//
4334 if (xauth) uName = (Client->name ? Client->name : "nobody");
4335 else uName = 0;
4336
4337// Check if TLS was or will be used
4338//
4339 tMsg = Link->verTLS();
4340 if (*tMsg) zMsg = " ";
4341
4342// Format the line
4343//
4344 snprintf(lBuff, sizeof(lBuff), "%s %s %s%slogin%s%s",
4345 (clientPV & XrdOucEI::uPrip ? "pvt" : "pub"), ipName,
4346 tMsg, zMsg,
4347 (xauth ? " as " : ""),
4348 (uName ? uName : ""));
4349
4350// Document the login
4351//
4352 if (Client->tident != Client->pident)
4353 {snprintf(pBuff, sizeof(pBuff), "via %s auth for %s",
4354 Client->prot, Client->pident);
4355 } else *pBuff = 0;
4356 eDest.Log(SYS_LOG_01, "Xeq", Link->ID, lBuff, (*pBuff ? pBuff : 0));
4357
4358// Enable TLS if we need to (note sess setting is off if login setting is on).
4359// If we need to but the client is not TLS capable, send an error and terminate.
4360//
4361 if ((doTLS & Req_TLSSess) && !Link->hasBridge())
4362 {if (ableTLS)
4363 {if (Link->setTLS(true, tlsCtx))
4364 {Link->setProtName("xroots");
4365 isTLS = true;
4366 } else {
4367 eDest.Emsg("Xeq", "Unable to require TLS for", Link->ID);
4368 return false;
4369 }
4370 } else {
4371 eDest.Emsg("Xeq","session requires TLS but",Link->ID,"is incapable.");
4372 Response.Send(kXR_TLSRequired, "session requires TLS support");
4373 return false;
4374 }
4375 }
4376
4377// Record the appname in the final SecEntity object
4378//
4379 if (AppName) Client->eaAPI->Add("xrd.appname", (std::string)AppName);
4380
4381// Assign unique identifier to the final SecEntity object
4382//
4383 Client->ueid = mySID;
4384
4385// Propogate a connect through the whole system
4386//
4387 osFS->Connect(Client);
4388 return true;
4389}
4390
4391/******************************************************************************/
4392/* m a p M o d e */
4393/******************************************************************************/
4394
4395#define Map_Mode(x,y) if (Mode & kXR_ ## x) newmode |= S_I ## y
4396
4397int XrdXrootdProtocol::mapMode(int Mode)
4398{
4399 int newmode = 0;
4400
4401// Map the mode in the obvious way
4402//
4403 Map_Mode(ur, RUSR); Map_Mode(uw, WUSR); Map_Mode(ux, XUSR);
4404 Map_Mode(gr, RGRP); Map_Mode(gw, WGRP); Map_Mode(gx, XGRP);
4405 Map_Mode(or, ROTH); Map_Mode(ox, XOTH);
4406
4407// All done
4408//
4409 return newmode;
4410}
4411
4412/******************************************************************************/
4413/* M o n A u t h */
4414/******************************************************************************/
4415
4417{
4418 char Buff[4096];
4419 const char *bP = Buff;
4420
4421 if (Client == &Entity) bP = Entity.moninfo;
4422 else {snprintf(Buff,sizeof(Buff),
4423 "&p=%s&n=%s&h=%s&o=%s&r=%s&g=%s&m=%s%s&I=%c",
4424 Client->prot,
4425 (Client->name ? Client->name : ""),
4426 (Client->host ? Client->host : ""),
4427 (Client->vorg ? Client->vorg : ""),
4428 (Client->role ? Client->role : ""),
4429 (Client->grps ? Client->grps : ""),
4430 (Client->moninfo ? Client->moninfo : ""),
4431 (Entity.moninfo ? Entity.moninfo : ""),
4432 (clientPV & XrdOucEI::uIPv4 ? '4' : '6')
4433 );
4434 Client->secMon = &Monitor;
4435 }
4436
4437 Monitor.Report(bP);
4438 if (Entity.moninfo) {free(Entity.moninfo); Entity.moninfo = 0;}
4439}
4440
4441/******************************************************************************/
4442/* r p C h e c k */
4443/******************************************************************************/
4444
4445int XrdXrootdProtocol::rpCheck(char *fn, char **opaque)
4446{
4447 char *cp;
4448
4449 if (*fn != '/')
4450 {if (!(XPList.Opts() & XROOTDXP_NOSLASH)) return 1;
4451 if ( XPList.Opts() & XROOTDXP_NOCGI) {*opaque = 0; return 0;}
4452 }
4453
4454 if (!(cp = index(fn, '?'))) *opaque = 0;
4455 else {*cp = '\0'; *opaque = cp+1;
4456 if (!**opaque) *opaque = 0;
4457 }
4458
4459 if (*fn != '/') return 0;
4460
4461 while ((cp = index(fn, '/')))
4462 {fn = cp+1;
4463 if (fn[0] == '.' && fn[1] == '.' && (fn[2] == '/' || fn[2] == '\0'))
4464 return 1;
4465 }
4466 return 0;
4467}
4468
4469/******************************************************************************/
4470/* r p E m s g */
4471/******************************************************************************/
4472
4473int XrdXrootdProtocol::rpEmsg(const char *op, char *fn)
4474{
4475 char buff[2048];
4476 snprintf(buff,sizeof(buff)-1,"%s relative path '%s' is disallowed.",op,fn);
4477 buff[sizeof(buff)-1] = '\0';
4478 return Response.Send(kXR_NotAuthorized, buff);
4479}
4480
4481/******************************************************************************/
4482/* S e t S F */
4483/******************************************************************************/
4484
4485int XrdXrootdProtocol::SetSF(kXR_char *fhandle, bool seton)
4486{
4487 XrdXrootdFHandle fh(fhandle);
4488 XrdXrootdFile *theFile;
4489
4490 if (!FTab || !(theFile = FTab->Get(fh.handle))) return -EBADF;
4491
4492// Turn it off or on if so wanted
4493//
4494 if (!seton) theFile->sfEnabled = 0;
4495 else if (theFile->fdNum >= 0) theFile->sfEnabled = 1;
4496
4497// All done
4498//
4499 return 0;
4500}
4501
4502/******************************************************************************/
4503/* S q u a s h */
4504/******************************************************************************/
4505
4506int XrdXrootdProtocol::Squash(char *fn)
4507{
4508 char *ofn, *ifn = fn;
4509
4510 if (*fn != '/') return XPList.Opts();
4511
4512 while(*ifn)
4513 {if (*ifn == '/')
4514 if (*(ifn+1) == '/'
4515 || (*(ifn+1) == '.' && *(ifn+1) && *(ifn+2) == '/')) break;
4516 ifn++;
4517 }
4518
4519 if (!*ifn) return XPList.Validate(fn, ifn-fn);
4520
4521 ofn = ifn;
4522 while(*ifn) {*ofn = *ifn++;
4523 while(*ofn == '/')
4524 {while(*ifn == '/') ifn++;
4525 if (ifn[0] == '.' && ifn[1] == '/') ifn += 2;
4526 else break;
4527 }
4528 ofn++;
4529 }
4530 *ofn = '\0';
4531
4532 return XPList.Validate(fn, ofn-fn);
4533}
4534
4535/******************************************************************************/
4536/* v p E m s g */
4537/******************************************************************************/
4538
4539int XrdXrootdProtocol::vpEmsg(const char *op, char *fn)
4540{
4541 char buff[2048];
4542 snprintf(buff,sizeof(buff)-1,"%s path '%s' is disallowed.",op,fn);
4543 buff[sizeof(buff)-1] = '\0';
4544 return Response.Send(kXR_NotAuthorized, buff);
4545}
XErrorCode
@ kXR_ArgInvalid
@ kXR_InvalidRequest
@ kXR_ArgMissing
@ kXR_TLSRequired
@ kXR_AuthFailed
@ kXR_NotAuthorized
@ kXR_NotFound
@ kXR_FileLocked
@ kXR_noErrorYet
@ kXR_ChkSumErr
@ kXR_overQuota
@ kXR_FileNotOpen
@ kXR_Unsupported
@ kXR_Cancelled
@ kXR_ServerError
@ kXR_Overloaded
@ kXR_ArgTooLong
@ kXR_FSError
@ kXR_NoMemory
@ kXR_ecredir
Definition XProtocol.hh:401
#define kXR_ShortProtRespLen
#define kXR_gotoTLS
#define kXR_haveTLS
#define kXR_PROTSIGNVERSION
Definition XProtocol.hh:75
kXR_char pathid
Definition XProtocol.hh:689
@ kXR_open_wrto
Definition XProtocol.hh:499
@ kXR_compress
Definition XProtocol.hh:482
@ kXR_async
Definition XProtocol.hh:488
@ kXR_delete
Definition XProtocol.hh:483
@ kXR_prefname
Definition XProtocol.hh:491
@ kXR_nowait
Definition XProtocol.hh:497
@ kXR_open_read
Definition XProtocol.hh:486
@ kXR_open_updt
Definition XProtocol.hh:487
@ kXR_mkpath
Definition XProtocol.hh:490
@ kXR_seqio
Definition XProtocol.hh:498
@ kXR_replica
Definition XProtocol.hh:495
@ kXR_posc
Definition XProtocol.hh:496
@ kXR_refresh
Definition XProtocol.hh:489
@ kXR_new
Definition XProtocol.hh:485
@ kXR_force
Definition XProtocol.hh:484
@ kXR_4dirlist
Definition XProtocol.hh:494
@ kXR_retstat
Definition XProtocol.hh:493
@ kXR_waitresp
Definition XProtocol.hh:948
@ kXR_redirect
Definition XProtocol.hh:946
@ kXR_oksofar
Definition XProtocol.hh:942
@ kXR_ok
Definition XProtocol.hh:941
@ kXR_authmore
Definition XProtocol.hh:944
@ kXR_wait
Definition XProtocol.hh:947
@ kXR_dstat
Definition XProtocol.hh:269
@ kXR_dcksm
Definition XProtocol.hh:270
kXR_char fhandle[4]
Definition XProtocol.hh:695
@ kXR_writev
Definition XProtocol.hh:144
@ kXR_write
Definition XProtocol.hh:132
kXR_int32 rlen
Definition XProtocol.hh:696
@ kXR_vermask
Definition XProtocol.hh:407
@ kXR_asyncap
Definition XProtocol.hh:408
#define kXR_attrProxy
#define kXR_PROTOCOLVERSION
Definition XProtocol.hh:70
kXR_int64 offset
Definition XProtocol.hh:697
@ kXR_vfs
Definition XProtocol.hh:799
@ kXR_mkdirpath
Definition XProtocol.hh:440
@ kXR_wmode
Definition XProtocol.hh:625
@ kXR_evict
Definition XProtocol.hh:630
@ kXR_usetcp
Definition XProtocol.hh:628
@ kXR_cancel
Definition XProtocol.hh:621
@ kXR_fresh
Definition XProtocol.hh:627
@ kXR_notify
Definition XProtocol.hh:622
@ kXR_coloc
Definition XProtocol.hh:626
@ kXR_stage
Definition XProtocol.hh:624
@ kXR_noerrs
Definition XProtocol.hh:623
@ kXR_dup
Definition XProtocol.hh:503
@ kXR_samefs
Definition XProtocol.hh:504
@ kXR_retstatx
Definition XProtocol.hh:505
ServerResponseReqs_Protocol secreq
@ kXR_file
@ kXR_isDir
@ kXR_offline
@ kXR_QPrep
Definition XProtocol.hh:650
@ kXR_Qopaqug
Definition XProtocol.hh:661
@ kXR_Qconfig
Definition XProtocol.hh:655
@ kXR_Qopaquf
Definition XProtocol.hh:660
@ kXR_QFSinfo
Definition XProtocol.hh:658
@ kXR_Qckscan
Definition XProtocol.hh:654
@ kXR_Qxattr
Definition XProtocol.hh:652
@ kXR_Qspace
Definition XProtocol.hh:653
@ kXR_Qvisa
Definition XProtocol.hh:656
@ kXR_QStats
Definition XProtocol.hh:649
@ kXR_Qcksum
Definition XProtocol.hh:651
@ kXR_QFinfo
Definition XProtocol.hh:657
@ kXR_Qopaque
Definition XProtocol.hh:659
@ kXR_ver001
Definition XProtocol.hh:415
@ kXR_ver003
Definition XProtocol.hh:417
@ kXR_ver004
Definition XProtocol.hh:418
@ kXR_ver002
Definition XProtocol.hh:416
@ kXR_readrdok
Definition XProtocol.hh:390
@ kXR_fullurl
Definition XProtocol.hh:388
@ kXR_lclfile
Definition XProtocol.hh:394
@ kXR_multipr
Definition XProtocol.hh:389
@ kXR_redirflags
Definition XProtocol.hh:395
@ kXR_hasipv64
Definition XProtocol.hh:391
int kXR_int32
Definition XPtypes.hh:89
unsigned int kXR_unt32
Definition XPtypes.hh:90
unsigned char kXR_char
Definition XPtypes.hh:65
#define DEBUG(x)
struct stat Stat
Definition XrdCks.cc:49
void usage()
#define TRACE_AUTH
#define TRACE_REDIR
#define ENODATA
#define stat(a, b)
Definition XrdPosix.hh:101
XrdSecBuffer XrdSecParameters
XrdSecBuffer XrdSecCredentials
int Mode
XrdOucString Path
#define eMsg(x)
struct myOpts opts
int emsg(int rc, char *msg)
#define Prep_EVICT
int XrdSfsMode
#define SFS_DATAVEC
#define SFS_O_HNAME
#define Prep_FRESH
#define SFS_DATA
#define SFS_FSCTL_PLUGFS
#define Prep_CANCEL
#define SFS_O_RESET
#define SFS_O_CREATAT
#define SFS_O_DIRLIST
#define SFS_FSCTL_STATFS
#define Prep_QUERY
char * notify
Notification path or 0.
XrdOucTList * paths
List of paths.
XrdOucTList * oinfo
1-to-1 correspondence of opaque info
#define SFS_ERROR
#define Prep_WMODE
#define SFS_LCLROOT(x)
#define SFS_O_SEQIO
#define SFS_O_NOTPC
#define SFS_O_FORCE
#define SFS_O_POSC
#define SFS_FCTL_QFINFO
#define SFS_FCTL_STATV
#define SFS_REDIRECT
#define Prep_PRTY3
#define Prep_PRTY0
#define SFS_O_MKPTH
#define Prep_PRTY2
#define SFS_STALL
#define SFS_O_RDONLY
#define SFS_STARTED
#define Prep_COLOC
#define SFS_O_MULTIW
#define SFS_FSCTL_STATLS
#define Prep_STAGE
#define SFS_FSCTL_STATCC
char * reqid
Request ID.
#define SFS_O_WRONLY
#define SFS_FCTL_GETFD
#define SFS_O_CREAT
#define SFS_FSCTL_STATXA
#define SFS_FSCTL_LOCATE
#define SFS_O_RAWIO
#define SFS_O_RDWR
#define Prep_PRTY1
#define Prep_SENDACK
#define SFS_FSCTL_PLUGIO
#define SFS_O_LOCAL
int XrdSfsFileOpenMode
#define SFS_FCTL_SPEC1
#define SFS_OK
long long XrdSfsFileOffset
#define SFS_LCLPATH(x)
#define SFS_O_NOWAIT
#define SFS_FSCTL_PLUGXC
int opts
Prep_xxx.
#define SFS_O_REPLICA
#define SFS_FSCTL_PLUGIN
#define SFS_O_TRUNC
int XrdSfsXferSize
#define Prep_SENDAOK
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:104
const int SYS_LOG_01
if(ec< 0) ec
XrdSys::RAtomic< unsigned int > RAtomic_uint
#define TRACING(x)
Definition XrdTrace.hh:70
#define TRACEI(act, x)
Definition XrdTrace.hh:66
XrdSysTrace XrdXrootdTrace
XrdOucString * XrdXrootdCF
#define JOB_Sync
const kXR_char XROOTD_MON_OPENW
const kXR_char XROOTD_MON_STAT
const kXR_char XROOTD_MON_REDLOCAL
const kXR_char XROOTD_MON_PREP
const kXR_char XROOTD_MON_OPENC
const kXR_char XROOTD_MON_TRUNC
const kXR_char XROOTD_MON_CLOSE
const kXR_char XROOTD_MON_CHMOD
const kXR_char XROOTD_MON_LOCATE
const kXR_char XROOTD_MON_OPENR
const kXR_char XROOTD_MON_MV
const kXR_char XROOTD_MON_RMDIR
const kXR_char XROOTD_MON_RM
const kXR_char XROOTD_MON_OPENDIR
const kXR_char XROOTD_MON_QUERY
const kXR_char XROOTD_MON_MKDIR
#define XRD_BOUNDPATH
#define XRD_LOGGEDIN
#define XRD_NEED_AUTH
#define TRACE_FS
#define TRACEP(act, x)
#define XROOTDXP_NOLK
#define XROOTDXP_NOSLASH
#define XROOTDXP_NOMWCHK
#define XROOTDXP_NOCGI
#define Map_Mode(x, y)
#define STATIC_REDIRECT(xfnc)
#define CRED
static const char * errName(kXR_int32 errCode)
Definition XProtocol.cc:131
static int mapError(int rc)
static const int ValuSize
Definition XrdCksData.hh:42
static const int NameSize
Definition XrdCksData.hh:41
static bool GetAssumeV4()
Definition XrdInet.hh:65
XrdJob(const char *desc="")
Definition XrdJob.hh:51
static XrdLink * fd2link(int fd)
Definition XrdLinkCtl.hh:72
static bool RegisterCloseRequestCb(XrdLink *lp, XrdProtocol *pp, bool(*cb)(void *), void *cbarg)
bool isMapped() const
bool isIPType(IPType ipType) const
const char * Set(const char *hSpec, int pNum=PortInSpec)
static bool getEA(const char *cgi, int &ecode, int &acode)
virtual void Done(int &Result, XrdOucErrInfo *eInfo, const char *Path=0)=0
XrdOucEICB * getErrCB()
void setErrCB(XrdOucEICB *cb, unsigned long long cbarg=0)
const char * getErrText()
void setUCap(int ucval)
Set user capabilties.
void Reset()
Reset object to no message state. Call this method to release appendages.
int length() const
const char * c_str() const
XrdOucTList * next
char * GetToken(char **rest=0, int lowcase=0)
static void Sanitize(char *instr, char subc='_')
static int isFWD(const char *path, int *port=0, char *hBuff=0, int hBLen=0, bool pTrim=false)
static std::string UrlEncode(const std::string &input)
XrdSfsDio()
Constructor and destructor.
Definition XrdSfsDio.hh:103
virtual int autoStat(struct stat *buf)
virtual int open(const char *path, const XrdSecEntity *client=0, const char *opaque=0)=0
XrdOucErrInfo & error
virtual const char * nextEntry()=0
virtual int close()=0
virtual int sync()=0
XrdOucErrInfo & error
virtual int open(const char *fileName, XrdSfsFileOpenMode openMode, mode_t createMode, const XrdSecEntity *client=0, const char *opaque=0)=0
virtual int Clone(XrdSfsFile &srcFile)
virtual int truncate(XrdSfsFileOffset fsize)=0
virtual const char * FName()=0
virtual int getCXinfo(char cxtype[4], int &cxrsz)=0
virtual int stat(struct stat *buf)=0
virtual void setXio(XrdSfsXio *xioP)
virtual int fctl(const int cmd, const char *args, XrdOucErrInfo &eInfo)=0
static void Snooze(int seconds)
XrdXrootdPgwFob * pgwFob
XrdSfsFile * XrdSfsp
XrdXrootdFileStats Stats
static void Open(XrdXrootdFileStats *fsP, const char *Path, unsigned int uDID, bool isRW)
int Write(long long offs, int dlen) override
void Read(long long offs, int dlen) override
static XrdXrootdNormAio * Alloc(XrdXrootdProtocol *protP, XrdXrootdResponse &resp, XrdXrootdFile *fP)
XrdXrootdPio * Next
XrdXrootd::IOParms IO
int(XrdXrootdProtocol::* ResumePio)()
static XrdXrootdPio * Alloc(int n=1)
kXR_char StreamID[2]
void Set(int(XrdXrootdProtocol::*Invoke)(), XrdXrootd::IOParms &io, const kXR_char *theSID)
static int List(XrdXrootdPrepArgs &pargs, char *resp, int resplen)
static void Log(XrdXrootdPrepArgs &pargs)
static void Logdel(char *reqid)
static XrdXrootdStats * SI
int SendFile(int fildes) override
XrdXrootdProtocol * VerifyStream(int &rc, int pID, bool lok=true)
static XrdSfsFileSystem * digFS
int SetSF(kXR_char *fhandle, bool seton=false)
XrdSecProtect * Protect
XrdNetPMark::Handle * pmHandle
static XrdNetPMark * PMark
XrdXrootdProtocol * Stream[maxStreams]
XrdXrootd::IOParms IO
static XrdXrootdXPath RPList
static const char Req_TLSGPFile
static bool CloseRequestCb(void *cbarg)
void SetFD(int fildes) override
static const char Req_TLSSess
XrdXrootdWVInfo * wvInfo
XrdSysSemaphore * reTry
XrdXrootdFileTable * FTab
static XrdXrootdJob * JobCKS
static XrdSysError & eDest
static unsigned int getSID()
XrdSecProtocol * AuthProt
int getData(gdCallBack *gdcbP, const char *dtype, char *buff, int blen)
XrdXrootdMonitor::User Monitor
static XrdXrootdRedirPI * RedirPI
static const char * myCName
static const char Req_TLSData
static XrdXrootdFileLock * Locker
static const int maxPio
int(XrdXrootdProtocol::* Resume)()
static const char Req_TLSTPC
static XrdTlsContext * tlsCtx
static XrdXrootdXPath XPList
static XrdScheduler * Sched
static const char Req_TLSLogin
XrdXrootdResponse Response
int(XrdXrootdProtocol::* ResumePio)()
static const int maxStreams
static XrdOucTList * JobCKTLST
static XrdXrootdXPath RQList
static struct XrdXrootdProtocol::RD_Table Route[RD_Num]
static XrdSecProtector * DHS
static XrdBuffManager * BPool
XrdSysSemaphore * boundRecycle
static XrdSecService * CIA
static RAtomic_int srvrAioOps
static uint64_t fsFeatures
static XrdOucReqID * PrepID
XrdXrootdPio * pioFirst
XrdSysCondVar2 * endNote
static XrdSfsFileSystem * osFS
void Set(XrdLink *lp)
static const int maxRvecsz
Definition XProtocol.hh:722
static const int maxClonesz
Definition XProtocol.hh:248
static const int maxWvecsz
Definition XProtocol.hh:879
static const uint64_t hasCACH
Feature: Implements a data cache.
static const uint64_t hasSXIO
Feature: Supports SfsXio.
static const uint64_t hasFICL
Feature: Supports file cloning and samefs.
ssize_t Send(int fd, KernelBuffer &buffer)
char * bifResp[2]
static const kXR_int32 doSync
Definition XProtocol.hh:867
char TimeZone
+/- hours from GMT (-128 if not set)
unsigned char Country[2]
Two letter TLD country code.
static const int uRedirFlgs
ucap: Client supports "file://"
static const int uVMask
static const int uUrlOK
ucap: Supports async responses
static const int uIPv64
ucap: Supports only IPv4 info
static const int uReadR
ucap: Supports multiple protocols
static const int uEcRedir
ucap: Client supports redirect flags
static const int uMProt
ucap: Supports url redirects
static const int uLclF
ucap: Client is on a private net
static const int uAsync
ucap: Extract protocol version
static const int uIPv4
ucap: Supports read redirects
static const int uPrip
long long offset
char * buffer
Pointer to the buffer.
int size
Size of the buffer or length of data in the buffer.
const char * Arg1
PLUGFS, PLUGIN, PLUGIO, PLUGXC.
int Arg2Len
Length or -count of args in extension.
int Arg1Len
Length.
unsigned int Sid
unsigned int Inst
static const int useSF
static const int useBasic
static const int useMMap
netInfo(const char *id)