00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 const char *XrdOucNSWalkCVSID = "$Id: XrdOucNSWalk.cc 34000 2010-06-21 06:49:56Z ganis $";
00014
00015 #include <string.h>
00016 #include <errno.h>
00017 #include <dirent.h>
00018 #include <unistd.h>
00019
00020 #include "XrdOuc/XrdOucNSWalk.hh"
00021 #include "XrdOuc/XrdOucTList.hh"
00022 #include "XrdSys/XrdSysError.hh"
00023 #include "XrdSys/XrdSysPlatform.hh"
00024
00025
00026
00027
00028
00029 XrdOucNSWalk::XrdOucNSWalk(XrdSysError *erp, const char *dpath,
00030 const char *lkfn, int opts,
00031 XrdOucTList *xlist)
00032 {
00033
00034
00035 eDest = erp;
00036 DList = new XrdOucTList(dpath);
00037 if (lkfn) LKFn = strdup(lkfn);
00038 else LKFn = 0;
00039 Opts = opts;
00040 DPfd = LKfd = -1;
00041 errOK= opts & skpErrs;
00042 DEnts= 0;
00043 edCB = 0;
00044
00045
00046
00047 if (!xlist) XList = 0;
00048 else while(xlist)
00049 {XList = new XrdOucTList(xlist->text,xlist->ival,XList);
00050 xlist = xlist->next;
00051 }
00052 }
00053
00054
00055
00056
00057
00058 XrdOucNSWalk::~XrdOucNSWalk()
00059 {
00060 XrdOucTList *tP;
00061
00062 if (LKFn) free(LKFn);
00063
00064 while((tP = DList)) {DList = tP->next; delete tP;}
00065
00066 while((tP = XList)) {XList = tP->next; delete tP;}
00067 }
00068
00069
00070
00071
00072
00073 XrdOucNSWalk::NSEnt *XrdOucNSWalk::Index(int &rc, const char **dPath)
00074 {
00075 XrdOucTList *tP;
00076 NSEnt *eP;
00077
00078
00079
00080 rc = 0; *DPath = '\0';
00081 while((tP = DList))
00082 {setPath(tP->text);
00083 DList = tP->next; delete tP;
00084 if (LKFn && (rc = LockFile())) break;
00085 rc = Build();
00086 if (LKfd >= 0) close(LKfd);
00087 if (DEnts || (rc && !errOK)) break;
00088 if (edCB && isEmpty) edCB->isEmpty(&dStat, DPath, LKFn);
00089 }
00090
00091
00092
00093 eP = DEnts; DEnts = 0;
00094 if (dPath) *dPath = DPath;
00095 return eP;
00096 }
00097
00098
00099
00100
00101
00102
00103
00104
00105 void XrdOucNSWalk::addEnt(XrdOucNSWalk::NSEnt *eP)
00106 {
00107 static const int retIxLO = retIDLO | retIILO;
00108
00109
00110
00111 if (Opts & noPath) {eP->Path = strdup(File); eP->File = eP->Path;}
00112 else {eP->Path = strdup(DPath);
00113 eP->File = eP->Path + (File - DPath);
00114 }
00115 eP->Plen = (eP->File - eP->Path) + strlen(eP->File);
00116
00117
00118
00119 if (!(Opts & retIxLO)) {eP->Next = DEnts; DEnts = eP;}
00120 else {NSEnt *pP = 0, *nP = DEnts;
00121 if (Opts & retIDLO)
00122 while(nP && eP->Plen < nP->Plen) {pP = nP; nP = nP->Next;}
00123 else
00124 while(nP && eP->Plen > nP->Plen) {pP = nP; nP = nP->Next;}
00125 if (pP) {eP->Next = nP; pP->Next = eP;}
00126 else {eP->Next = nP; DEnts = eP;}
00127 }
00128 }
00129
00130
00131
00132
00133
00134 int XrdOucNSWalk::Build()
00135 {
00136 struct Helper {XrdOucNSWalk::NSEnt *P;
00137 DIR *D;
00138 int F;
00139 Helper() : P(0), D(0), F(-1) {}
00140 ~Helper() {if (P) delete P;
00141 if (D) closedir(D);
00142 if (F>0) close(F);
00143 }
00144 } theEnt;
00145 struct dirent *dp;
00146 int rc = 0, getLI = Opts & retLink;
00147 int nEnt = 0, xLKF = 0, chkED = (edCB != 0) && (LKFn != 0);
00148
00149
00150
00151 isEmpty = 0;
00152
00153
00154
00155 #ifdef HAVE_FSTATAT
00156 if ((DPfd = open(DPath, O_RDONLY)) < 0) rc = errno;
00157 else theEnt.F = DPfd;
00158 #else
00159 DPfd = -1;
00160 #endif
00161
00162
00163
00164 if (!(theEnt.D = opendir(DPath)))
00165 {rc = errno;
00166 if (eDest) eDest->Emsg("Build", rc, "open directory", DPath);
00167 return rc;
00168 }
00169
00170
00171
00172 errno = 0;
00173 while((dp = readdir(theEnt.D)))
00174 {if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
00175 strcpy(File, dp->d_name); nEnt++;
00176 if (!theEnt.P) theEnt.P = new NSEnt();
00177 rc = getStat(theEnt.P, getLI);
00178 switch(theEnt.P->Type)
00179 {case NSEnt::isDir:
00180 if (Opts & Recurse && (!getLI || !isSymlink())
00181 && (!XList || !inXList(File)))
00182 DList = new XrdOucTList(DPath, 0, DList);
00183 if (!(Opts & retDir)) continue;
00184 break;
00185 case NSEnt::isFile:
00186 if ((chkED && !xLKF && (xLKF = !strcmp(File, LKFn)))
00187 || !(Opts & retFile)) continue;
00188 break;
00189 case NSEnt::isLink:
00190 if ((rc = getLink(theEnt.P)))
00191 memset(&theEnt.P->Stat, 0, sizeof(struct stat));
00192 else if ((Opts & retStat) && (rc = getStat(theEnt.P)))
00193 {theEnt.P->Type = NSEnt::isLink; rc = 0;}
00194 break;
00195 case NSEnt::isMisc:
00196 if (!(Opts & retMisc)) continue;
00197 break;
00198 default:
00199 if (!rc) rc = EINVAL;
00200 break;
00201 }
00202 errno = 0;
00203 if (rc) {if (errOK) continue; return rc;}
00204 addEnt(theEnt.P); theEnt.P = 0;
00205 }
00206
00207
00208
00209 *File = '\0';
00210 if ((rc = errno) && !errOK)
00211 {eDest->Emsg("Build", rc, "reading directory", DPath); return rc;}
00212
00213
00214
00215 if (edCB && xLKF == nEnt && !DEnts)
00216 {if ((DPfd < 0 ? !stat(DPath, &dStat) : !fstat(DPfd, &dStat))) isEmpty=1;
00217 else eDest->Emsg("Build", errno, "stating directory", DPath);
00218 }
00219 return 0;
00220 }
00221
00222
00223
00224
00225
00226 int XrdOucNSWalk::getLink(XrdOucNSWalk::NSEnt *eP)
00227 {
00228 char lnkbuff[2048];
00229 int rc;
00230
00231 if ((rc = readlink(DPath, lnkbuff, sizeof(lnkbuff))) < 0)
00232 {rc = errno;
00233 if (eDest) eDest->Emsg("getLink", rc, "read link of", DPath);
00234 return rc;
00235 }
00236
00237 eP->Lksz = rc;
00238 eP->Link = (char *)malloc(rc+1);
00239 memcpy(eP->Link, lnkbuff, rc);
00240 *(eP->Link+rc) = '\0';
00241 return 0;
00242 }
00243
00244
00245
00246
00247
00248 int XrdOucNSWalk::getStat(XrdOucNSWalk::NSEnt *eP, int doLstat)
00249 {
00250 int rc;
00251
00252
00253
00254 #ifdef HAVE_FSTATAT
00255 do{rc = fstatat(DPfd, File, &(eP->Stat), (doLstat ? AT_SYMLINK_NOFOLLOW : 0));
00256 #else
00257 do{rc = doLstat ? lstat(DPath, &(eP->Stat)) : stat(DPath, &(eP->Stat));
00258 #endif
00259 } while(rc && errno == EINTR);
00260
00261
00262
00263 if (rc)
00264 {rc = errno;
00265 if (eDest && rc != ENOENT && rc != ELOOP)
00266 eDest->Emsg("getStat", rc, "stat", DPath);
00267 memset(&eP->Stat, 0, sizeof(struct stat));
00268 eP->Type = NSEnt::isBad;
00269 return rc;
00270 }
00271
00272
00273
00274 if ((eP->Stat.st_mode & S_IFMT) == S_IFDIR) eP->Type = NSEnt::isDir;
00275 else if ((eP->Stat.st_mode & S_IFMT) == S_IFREG) eP->Type = NSEnt::isFile;
00276 else if ((eP->Stat.st_mode & S_IFMT) == S_IFLNK) eP->Type = NSEnt::isLink;
00277 else eP->Type = NSEnt::isMisc;
00278
00279 return 0;
00280 }
00281
00282
00283
00284
00285
00286 int XrdOucNSWalk::inXList(const char *dName)
00287 {
00288 XrdOucTList *xTP = XList, *pTP = 0;
00289
00290
00291
00292 while(xTP && strcmp(DPath, xTP->text)) {pTP = xTP; xTP = xTP->next;}
00293
00294
00295
00296 if (!xTP) return 0;
00297 if (pTP) pTP->next = xTP->next;
00298 else XList = xTP->next;
00299 delete xTP;
00300 return 1;
00301 }
00302
00303
00304
00305
00306
00307 int XrdOucNSWalk::isSymlink()
00308 {
00309 struct stat buf;
00310 int rc;
00311
00312
00313
00314
00315 #ifdef HAVE_FSTATAT
00316 do{rc = fstatat(DPfd, File, &buf, AT_SYMLINK_NOFOLLOW);
00317 #else
00318 do{rc = lstat(DPath, &buf);
00319 #endif
00320 } while(rc && errno == EINTR);
00321
00322
00323
00324 if (rc) return 0;
00325 return (buf.st_mode & S_IFMT) == S_IFLNK;
00326 }
00327
00328
00329
00330
00331
00332 int XrdOucNSWalk::LockFile()
00333 {
00334 FLOCK_t lock_args;
00335 int rc;
00336
00337
00338
00339 strcpy(File, LKFn);
00340 do {LKfd = open(DPath, O_RDWR);} while(LKfd < 0 && errno == EINTR);
00341 if (LKfd < 0)
00342 {if (errno == ENOENT) {*File = '\0'; return 0;}
00343 {rc = errno;
00344 if (eDest) eDest->Emsg("LockFile", rc, "open", DPath);
00345 *File = '\0'; return rc;
00346 }
00347 }
00348
00349
00350
00351 bzero(&lock_args, sizeof(lock_args));
00352 lock_args.l_type = F_WRLCK;
00353
00354
00355
00356 do {rc = fcntl(LKfd,F_SETLKW,&lock_args);}
00357 while(rc < 0 && errno == EINTR);
00358 if (rc < 0)
00359 {rc = -errno;
00360 if (eDest) eDest->Emsg("LockFile", errno, "lock", DPath);
00361 }
00362
00363
00364
00365 *File = '\0';
00366 return rc;
00367 }
00368
00369
00370
00371
00372
00373 void XrdOucNSWalk::setPath(char *newpath)
00374 {
00375 int n;
00376
00377 strcpy(DPath, newpath);
00378 n = strlen(newpath);
00379 if (DPath[n-1] != '/')
00380 {DPath[n++] = '/'; DPath[n] = '\0';}
00381 File = DPath+n;
00382 }