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
#define SFS_DATA
#define SFS_FSCTL_PLUGFS
#define Prep_CANCEL
#define SFS_O_RESET
#define SFS_O_CREATAT
#define SFS_O_DIRLIST
#define SFS_FSCTL_STATFS
#define Prep_QUERY
char * notify
Notification path or 0.
XrdOucTList * paths
List of paths.
XrdOucTList * oinfo
1-to-1 correspondence of opaque info
#define SFS_ERROR
#define Prep_WMODE
#define SFS_LCLROOT(x)
#define SFS_O_SEQIO
#define SFS_O_NOTPC
#define SFS_O_FORCE
#define SFS_O_POSC
#define SFS_FCTL_QFINFO
#define SFS_FCTL_STATV
#define SFS_REDIRECT
#define Prep_PRTY3
#define Prep_PRTY0
#define SFS_O_MKPTH
#define Prep_PRTY2
#define SFS_STALL
#define SFS_O_RDONLY
#define SFS_STARTED
#define Prep_COLOC
#define SFS_O_MULTIW
#define SFS_FSCTL_STATLS
#define Prep_STAGE
#define SFS_FSCTL_STATCC
char * reqid
Request ID.
#define SFS_O_WRONLY
#define SFS_FCTL_GETFD
#define SFS_O_CREAT
#define SFS_FSCTL_STATXA
#define SFS_FSCTL_LOCATE
#define SFS_O_RAWIO
#define SFS_O_RDWR
#define Prep_PRTY1
#define Prep_SENDACK
#define SFS_FSCTL_PLUGIO
#define SFS_O_LOCAL
int XrdSfsFileOpenMode
#define SFS_FCTL_SPEC1
#define SFS_OK
long long XrdSfsFileOffset
#define SFS_LCLPATH(x)
#define SFS_O_NOWAIT
#define SFS_FSCTL_PLUGXC
int opts
Prep_xxx.
#define SFS_O_REPLICA
#define SFS_FSCTL_PLUGIN
#define SFS_O_TRUNC
int XrdSfsXferSize
#define Prep_SENDAOK
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:104
const int SYS_LOG_01
if(ec< 0) ec
#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.
const char * Arg1
PLUGFS, PLUGIN, PLUGIO, PLUGXC.
int Arg2Len
Length or -count of args in extension.
int Arg1Len
Length.
unsigned int Sid
unsigned int Inst
static const int useSF
static const int useBasic
static const int useMMap