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