XRootD
Loading...
Searching...
No Matches
XrdOucNSWalk.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d O u c N S W a l k . c c */
4/* */
5/* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University */
6/* All Rights Reserved */
7/* Produced by Andrew Hanushevsky for Stanford University under contract */
8/* DE-AC02-76-SFO0515 with the Department of Energy */
9/* */
10/* This file is part of the XRootD software suite. */
11/* */
12/* XRootD is free software: you can redistribute it and/or modify it under */
13/* the terms of the GNU Lesser General Public License as published by the */
14/* Free Software Foundation, either version 3 of the License, or (at your */
15/* option) any later version. */
16/* */
17/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20/* License for more details. */
21/* */
22/* You should have received a copy of the GNU Lesser General Public License */
23/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25/* */
26/* The copyright holder's institutional names and contributor's names may not */
27/* be used to endorse or promote products derived from this software without */
28/* specific prior written permission of the institution or contributor. */
29/******************************************************************************/
30
31#include <cctype>
32#include <cstring>
33#include <dirent.h>
34#include <unistd.h>
35
37#include "XrdOuc/XrdOucTList.hh"
38#include "XrdSys/XrdSysE2T.hh"
39#include "XrdSys/XrdSysError.hh"
42
43
44/******************************************************************************/
45/* C o n s t r u c t o r */
46/******************************************************************************/
47
49 const char *lkfn, int opts,
50 XrdOucTList *xlist)
51{
52// Set the required fields
53//
54 eDest = erp;
55 mPfx = 0;
56 DList = new XrdOucTList(dpath);
57 if (lkfn) LKFn = strdup(lkfn);
58 else LKFn = 0;
59 Opts = opts;
60 DPfd = LKfd = -1;
61 errOK= opts & skpErrs;
62 DEnts= 0;
63 edCB = 0;
64
65// Copy the exclude list if one exists
66//
67 if (!xlist) XList = 0;
68 else while(xlist)
69 {XList = new XrdOucTList(xlist->text,xlist->ival,XList);
70 xlist = xlist->next;
71 }
72}
73
74/******************************************************************************/
75/* D e s t r u c t o r */
76/******************************************************************************/
77
79{
80 XrdOucTList *tP;
81
82 if (LKFn) free(LKFn);
83
84 while((tP = DList)) {DList = tP->next; delete tP;}
85
86 while((tP = XList)) {XList = tP->next; delete tP;}
87}
88
89/******************************************************************************/
90/* I n d e x */
91/******************************************************************************/
92
93XrdOucNSWalk::NSEnt *XrdOucNSWalk::Index(int &rc, const char **dPath)
94{
95 XrdOucTList *tP;
96 NSEnt *eP;
97
98// Sequence the directory
99//
100 rc = 0; *DPath = '\0';
101 while((tP = DList))
102 {setPath(tP->text);
103 DList = tP->next; delete tP;
104 if (LKFn && (rc = LockFile())) break;
105 rc = Build();
106 if (LKfd >= 0) close(LKfd);
107 if (DEnts || (rc && !errOK)) break;
108 if (edCB && isEmpty) edCB->isEmpty(&dStat, DPath, LKFn);
109 }
110
111// Return the result
112//
113 eP = DEnts; DEnts = 0;
114 if (dPath) *dPath = DPath;
115 return eP;
116}
117
118/******************************************************************************/
119/* P r i v a t e M e t h o d s */
120/******************************************************************************/
121/******************************************************************************/
122/* a d d E n t */
123/******************************************************************************/
124
125void XrdOucNSWalk::addEnt(XrdOucNSWalk::NSEnt *eP)
126{
127 static const int retIxLO = retIDLO | retIILO;
128
129// Complete the entry
130//
131 if (Opts & noPath) {eP->Path = strdup(File); eP->File = eP->Path;}
132 else {eP->Path = strdup(DPath);
133 eP->File = eP->Path + (File - DPath);
134 }
135 eP->Plen = (eP->File - eP->Path) + strlen(eP->File);
136
137// Chain the entry into the list
138//
139 if (!(Opts & retIxLO)) {eP->Next = DEnts; DEnts = eP;}
140 else {NSEnt *pP = 0, *nP = DEnts;
141 if (Opts & retIDLO)
142 while(nP && eP->Plen < nP->Plen) {pP = nP; nP = nP->Next;}
143 else
144 while(nP && eP->Plen > nP->Plen) {pP = nP; nP = nP->Next;}
145 if (pP) {eP->Next = nP; pP->Next = eP;}
146 else {eP->Next = nP; DEnts = eP;}
147 }
148}
149
150/******************************************************************************/
151/* B u i l d */
152/******************************************************************************/
153
154int XrdOucNSWalk::Build()
155{
156 struct Helper {XrdOucNSWalk::NSEnt *P;
157 DIR *D;
158 int F;
159 Helper() : P(0), D(0), F(-1) {}
160 ~Helper() {if (P) delete P;
161 if (D) closedir(D);
162 if (F>0) close(F);
163 }
164 } theEnt;
165 struct dirent *dp;
166 int rc = 0, getLI = Opts & retLink;
167 int nEnt = 0, xLKF = 0, chkED = (edCB != 0) && (LKFn != 0);
168
169// Initialize the empty flag prior to doing anything else
170//
171 isEmpty = 0;
172
173// If we can optimize with a directory file descriptor, get one
174//
175#ifdef HAVE_FSTATAT
176 if ((DPfd = open(DPath, O_RDONLY)) < 0) rc = errno;
177 else theEnt.F = DPfd;
178#else
179 DPfd = -1;
180#endif
181
182// Open the directory
183//
184 if (!(theEnt.D = opendir(DPath)))
185 return Emsg("Build", errno, "open directory", DPath);
186
187// Process the entries
188//
189 errno = 0;
190 while((dp = readdir(theEnt.D)))
191 {if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
192 strcpy(File, dp->d_name); nEnt++;
193 if (!theEnt.P) theEnt.P = new NSEnt();
194 rc = getStat(theEnt.P, getLI);
195 switch(theEnt.P->Type)
196 {case NSEnt::isDir:
197 if (Opts & Recurse && (!getLI || !isSymlink())
198 && (!XList || !inXList(File)))
199 DList = new XrdOucTList(DPath, 0, DList);
200 if (!(Opts & retDir)) continue;
201 break;
202 case NSEnt::isFile:
203 if ((chkED && !xLKF && (xLKF = !strcmp(File, LKFn)))
204 || !(Opts & retFile)) continue;
205 break;
206 case NSEnt::isLink:
207 if ((rc = getLink(theEnt.P)))
208 memset(&theEnt.P->Stat, 0, sizeof(struct stat));
209 else if ((Opts & retStat) && (rc = getStat(theEnt.P)))
210 {theEnt.P->Type = NSEnt::isLink; rc = 0;}
211 break;
212 case NSEnt::isMisc:
213 if (!(Opts & retMisc)) continue;
214 break;
215 default:
216 if (!rc) rc = EINVAL;
217 break;
218 }
219 errno = 0;
220 if (rc) {if (errOK) continue; return rc;}
221 addEnt(theEnt.P); theEnt.P = 0;
222 }
223
224// All done, check if we reached EOF or there is an error
225//
226 *File = '\0';
227 if ((rc = errno) && !errOK)
228 return Emsg("Build", rc, "read directory", DPath);
229
230// Check if we need to do a callback for an empty directory
231//
232 if (edCB && xLKF == nEnt && !DEnts)
233 {if ((DPfd < 0 ? !stat(DPath, &dStat) : !fstat(DPfd, &dStat))) isEmpty=1;
234 else Emsg("Build", errno, "stat directory", DPath);
235 }
236 return 0;
237}
238
239/******************************************************************************/
240/* E m s g */
241/******************************************************************************/
242
243int XrdOucNSWalk::Emsg(const char *pfx, int rc, const char *txt1,
244 const char *txt2)
245{
246 if (eDest) eDest->Emsg(pfx, rc, txt1, txt2);
247 else if (mPfx)
248 {const char *etxt = XrdSysE2T(rc);
249 std::cerr <<mPfx <<": Unable to " <<txt1;
250 if (txt2) std::cerr <<' ' <<txt2;
251 std::cerr <<"; " <<(etxt) <<"\n" << std::flush;
252 }
253 return rc;
254}
255
256/******************************************************************************/
257/* g e t L i n k */
258/******************************************************************************/
259
260int XrdOucNSWalk::getLink(XrdOucNSWalk::NSEnt *eP)
261{
262 char lnkbuff[2048];
263 int rc;
264
265 if ((rc = readlink(DPath, lnkbuff, sizeof(lnkbuff))) < 0)
266 return Emsg("getLink", errno, "read link of", DPath);
267
268 eP->Lksz = rc;
269 eP->Link = (char *)malloc(rc+1);
270 memcpy(eP->Link, lnkbuff, rc);
271 *(eP->Link+rc) = '\0';
272 return 0;
273}
274
275/******************************************************************************/
276/* g e t S t a t */
277/******************************************************************************/
278
279int XrdOucNSWalk::getStat(XrdOucNSWalk::NSEnt *eP, int doLstat)
280{
281 int rc;
282
283// The following code either uses fstatat() or regular stat()
284//
285#ifdef HAVE_FSTATAT
286do{rc = fstatat(DPfd, File, &(eP->Stat), (doLstat ? AT_SYMLINK_NOFOLLOW : 0));
287#else
288do{rc = doLstat ? lstat(DPath, &(eP->Stat)) : stat(DPath, &(eP->Stat));
289#endif
290 } while(rc && errno == EINTR);
291
292// Check for errors
293//
294 if (rc)
295 {rc = errno;
296 if (rc != ENOENT && rc != ELOOP) Emsg("getStat", rc, "stat", DPath);
297 memset(&eP->Stat, 0, sizeof(struct stat));
298 eP->Type = (rc == ENOENT ? NSEnt::isMisc : NSEnt::isBad);
299 return rc;
300 }
301
302// Set appropraite type
303//
304 if ((eP->Stat.st_mode & S_IFMT) == S_IFDIR) eP->Type = NSEnt::isDir;
305 else if ((eP->Stat.st_mode & S_IFMT) == S_IFREG) eP->Type = NSEnt::isFile;
306 else if ((eP->Stat.st_mode & S_IFMT) == S_IFLNK) eP->Type = NSEnt::isLink;
307 else eP->Type = NSEnt::isMisc;
308
309 return 0;
310}
311
312/******************************************************************************/
313/* i n X L i s t */
314/******************************************************************************/
315
316int XrdOucNSWalk::inXList(const char *dName)
317{
318 XrdOucTList *xTP = XList, *pTP = 0;
319
320// Search for the directory entry
321//
322 while(xTP && strcmp(DPath, xTP->text)) {pTP = xTP; xTP = xTP->next;}
323
324// If not found return false. Otherwise, delete the entry and return true.
325//
326 if (!xTP) return 0;
327 if (pTP) pTP->next = xTP->next;
328 else XList = xTP->next;
329 delete xTP;
330 return 1;
331}
332
333/******************************************************************************/
334/* i s S y m l i n k */
335/******************************************************************************/
336
337int XrdOucNSWalk::isSymlink()
338{
339 struct stat buf;
340 int rc;
341
342
343// The following code either uses fstatat() or regular stat()
344//
345#ifdef HAVE_FSTATAT
346do{rc = fstatat(DPfd, File, &buf, AT_SYMLINK_NOFOLLOW);
347#else
348do{rc = lstat(DPath, &buf);
349#endif
350 } while(rc && errno == EINTR);
351
352// Check for errors
353//
354 if (rc) return 0;
355 return (buf.st_mode & S_IFMT) == S_IFLNK;
356}
357
358/******************************************************************************/
359/* L o c k F i l e */
360/******************************************************************************/
361
362int XrdOucNSWalk::LockFile()
363{
364 FLOCK_t lock_args;
365 int rc;
366
367// Construct the path and open the file
368//
369 strcpy(File, LKFn);
370 do {LKfd = open(DPath, O_RDWR);} while(LKfd < 0 && errno == EINTR);
371 if (LKfd < 0)
372 {if (errno == ENOENT) {*File = '\0'; return 0;}
373 {*File = '\0';
374 return Emsg("LockFile", errno, "open", DPath);
375 }
376 }
377
378// Establish locking options
379//
380 bzero(&lock_args, sizeof(lock_args));
381 lock_args.l_type = F_WRLCK;
382
383// Perform action.
384//
385 do {rc = fcntl(LKfd,F_SETLKW,&lock_args);}
386 while(rc < 0 && errno == EINTR);
387 if (rc < 0) rc = Emsg("LockFile", errno, "lock", DPath);
388
389// All done
390//
391 *File = '\0';
392 return rc;
393}
394
395/******************************************************************************/
396/* s e t P a t h */
397/******************************************************************************/
398
399void XrdOucNSWalk::setPath(char *newpath)
400{
401 int n;
402
403 strcpy(DPath, newpath);
404 n = strlen(newpath);
405 if (DPath[n-1] != '/')
406 {DPath[n++] = '/'; DPath[n] = '\0';}
407 File = DPath+n;
408}
struct stat Stat
Definition XrdCks.cc:49
int lstat(const char *path, struct stat *buf)
int fcntl(int fd, int cmd,...)
#define close(a)
Definition XrdPosix.hh:43
#define fstat(a, b)
Definition XrdPosix.hh:57
#define opendir(a)
Definition XrdPosix.hh:73
#define open
Definition XrdPosix.hh:71
#define closedir(a)
Definition XrdPosix.hh:45
#define stat(a, b)
Definition XrdPosix.hh:96
#define readdir(a)
Definition XrdPosix.hh:81
XrdOucString File
struct myOpts opts
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:104
#define FLOCK_t
static const int retFile
XrdOucNSWalk(XrdSysError *erp, const char *dname, const char *LKfn=0, int opts=retAll, XrdOucTList *xP=0)
static const int retIDLO
static const int retLink
static const int skpErrs
static const int noPath
static const int retStat
static const int retMisc
NSEnt * Index(int &rc, const char **dPath=0)
static const int retDir
static const int Recurse
static const int retIILO
XrdOucTList * next
struct NSEnt * Next