vsh@etnibsd.UUCP (Steve Harris) (08/08/90)
Here is a simple-minded implementation of pwd in perl. It's not too fast, but neither is "chop($cwd = `pwd`);" The path to the current directory is returned in an array, which is passed by reference. Any critiques of the algorithm (essentially, walk up the directory tree till you reach the root) will be appreciated. Steve Harris - Eaton Corp. - Beverly, MA - uunet!etnibsd!vsh =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=- ;# pwd.pl -- perl library function ;# ;# usage: ;# die "error in pwd" unless &pwd(*path); ;# print "/", join('/',@path), "\n"; sub pwd { local(*path) = @_; # array to hold path (return value) local($dc,$ic); # dev, ino for parent dir local($dp,$ip); # dev, ino for child dir local($d,$i,$f); # tmp: dev, ino, file name @path = (); # init path ($dc,$ic) = stat('.'); # get initial child_dir info outerloop: for (;;) { ;# chdir to parent_dir unless (chdir('..')) { warn "cannot chdir .., path = ", join('/', @path); return 0; } ;# get parent_dir info ($dp,$ip) = stat('.'); unless (opendir(D,'.')) { warn "cannot opendir ., path = ", join('/', @path); return 0; } ;# look for entry in parent_dir with same dev, ino as child while ($f = readdir(D)) { next if $f eq '..'; # skip '..' next if -l $f; # skip links (but do lstat!) next unless -d _; # skip non-dirs ($d,$i) = stat(_); # get dev, ino for dir next unless ($d == $dc && $i == $ic); # match? last outerloop if ($f eq '.'); # root dir? unshift(@path,$f); # add dir to path ($dc,$ic) = ($dp,$ip); # parent becomes next child last; # exit readdir loop } closedir(D); } 1; } -- Steve Harris - Eaton Corp. - Beverly, MA - uunet!etnibsd!vsh
jsb@no.cs.brown.edu (John Bazik) (08/09/90)
In article <1141@etnibsd.UUCP>, vsh@etnibsd.UUCP (Steve Harris) writes: |> Here is a simple-minded implementation of pwd in perl. [stuff deleted] Yeah, I did that, too. It was significantly faster than `pwd`, but, unfortunately, breaks under SunOS 4.1. The latter has "virtual filesystems" (or whatever they call it), which reside in memory rather than on disk. By default /tmp is one. For some braindead reason, the inode number of /tmp matches that of /. Actually, I suppose if ($dinode == $pinode && $dname eq $pname) { would do it...hmmm. Anyway, I guess the moral is that any algorithm has to anticipate weirdness but a getwd built-in would just always work. John
allbery@NCoast.ORG (Brandon S. Allbery KB8JRR/KT) (08/10/90)
As quoted from <1141@etnibsd.UUCP> by vsh@etnibsd.UUCP (Steve Harris): +--------------- | while ($f = readdir(D)) { | next if $f eq '..'; # skip '..' | next if -l $f; # skip links (but do lstat!) | next unless -d _; # skip non-dirs | ($d,$i) = stat(_); # get dev, ino for dir | next unless ($d == $dc && $i == $ic); # match? | last outerloop if ($f eq '.'); # root dir? | unshift(@path,$f); # add dir to path | ($dc,$ic) = ($dp,$ip); # parent becomes next child | last; # exit readdir loop | } +--------------- How about mount points? I'm almost *never* in the root file system. You should also provide a way to get back where you were before you started; preferably, opendir() a string which is built incrementally ("../../.."...). I just use chop($cwd = `pwd`); myself. If it ever bothers me, I'll port Doug Gwyn's getcwd() to Perl. ++Brandon -- Me: Brandon S. Allbery VHF: KB8JRR/KT on 220 (soon others) Internet: allbery@NCoast.ORG Delphi: ALLBERY uunet!usenet.ins.cwru.edu!ncoast!allbery America OnLine: KB8JRR
meissner@osf.org (Michael Meissner) (08/10/90)
In article <1990Aug9.225658.11586@NCoast.ORG> allbery@NCoast.ORG (Brandon S. Allbery KB8JRR/KT) writes: | As quoted from <1141@etnibsd.UUCP> by vsh@etnibsd.UUCP (Steve Harris): | +--------------- | | while ($f = readdir(D)) { | | next if $f eq '..'; # skip '..' | | next if -l $f; # skip links (but do lstat!) | | next unless -d _; # skip non-dirs | | ($d,$i) = stat(_); # get dev, ino for dir | | next unless ($d == $dc && $i == $ic); # match? | | last outerloop if ($f eq '.'); # root dir? | | unshift(@path,$f); # add dir to path | | ($dc,$ic) = ($dp,$ip); # parent becomes next child | | last; # exit readdir loop | | } | +--------------- | | How about mount points? I'm almost *never* in the root file system. | You should also provide a way to get back where you were before you started; | preferably, opendir() a string which is built incrementally ("../../.."...). | | I just use chop($cwd = `pwd`); myself. If it ever bothers me, I'll port Doug | Gwyn's getcwd() to Perl. | | ++Brandon Another thing that I don't think has been mentioned, is that you might be in a directory whose parent directory you have no access to. In this case reading ..'s files until you find . will not work. This is why pwd must be setuid root, so that it can do the appropriate checks. -- Michael Meissner email: meissner@osf.org phone: 617-621-8861 Open Software Foundation, 11 Cambridge Center, Cambridge, MA, 02142 Do apple growers tell their kids money doesn't grow on bushes?
marc@mit.edu (08/11/90)
|> Another thing that I don't think has been mentioned, is that you might |> be in a directory whose parent directory you have no access to. In |> this case reading ..'s files until you find . will not work. This is |> why pwd must be setuid root, so that it can do the appropriate checks. If you have no access to the parent, how did you get to where you are now? I think it's a safe assumption that you can read all the directories up to the root. My system makes this assumption; /bin/pwd is not setuid root. I think a lot of programs probably depend on getwd() working. Here, if getwd ()doesn't work, the shell won't even do the right thing. I agree that perl should have a portable getwd() function. I think it's clear that there is no straightforward way to do what needs to be done portably, so each port must figure out what is right. The case of the PC was brought up, and the same issue applies to the Amiga, and any other non-unix box perl is ported to. IMHO, this is a useful enough feature that it should be a builtin. My $0.02. Marc
merlyn@iwarp.intel.com (Randal Schwartz) (08/11/90)
In article <1990Aug10.205437.21409@uvaarpa.Virginia.EDU>, marc@mit writes: | |> Another thing that I don't think has been mentioned, is that you might | |> be in a directory whose parent directory you have no access to. In | |> this case reading ..'s files until you find . will not work. This is | |> why pwd must be setuid root, so that it can do the appropriate checks. | | If you have no access to the parent, how did you get to where you are | now? I think it's a safe assumption that you can read all the | directories up to the root. My system makes this assumption; /bin/pwd | is not setuid root. I think a lot of programs probably depend on | getwd() working. Here, if getwd ()doesn't work, the shell won't even | do the right thing. You can have 'x' access and no 'r' access. On your system, /bin/pwd would fail in this case, and so would any call to getwd() or the Perl routine that's been published (or anything from user-level non-setuid code). I think that's why /bin/pwd is setuid on some machines. (Interesting... it's not setuid on Sunos4.1 either) I agree with Larry, though. chop($pwd = `pwd`) is portable and fast, and there are more important features for Larry to add (I know, I've been writing about them for a week now :-). print "Just another Perl hacker," -- /=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\ | on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III | | merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn | \=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/
marc@athena.mit.edu (08/11/90)
You can have 'x' access and no 'r' access. On your system, /bin/pwd would fail in this case, and so would any call to getwd() or the Perl routine that's been published (or anything from user-level non-setuid code). I think that's why /bin/pwd is setuid on some machines. (Interesting... it's not setuid on Sunos4.1 either) Ok, I did that, and I'm convinced. However, our version of tcsh doesn't like it very much (although perl doesn't need tcsh) : <57> portnoy:/tmp# cd /tmp/foo/bar/baz <58> portnoy:/tmp/foo/bar/baz# su marc getwd: can't open .. (Did you su in a fascist directory?) I still think creating a directory like this is a bad idea, but I'll stop flaming about it. So, I propose as an implementation for (a default implementation of) a builtin getwd(): 1) call libc's getwd(). If you get an error, 2) fork /bin/pwd. If you get an error from that, 3) return false and set $@ This has the advantage of only forking if necessary. I agree with Larry, though. chop($pwd = `pwd`) is portable and fast, and there are more important features for Larry to add (I know, I've been writing about them for a week now :-). That's not portable to my IBM PC, and I don't consider forking "fast." We're still using VS2's some places around here, and what's fast on a Sparc may not be elsewhere. Of course, someone would write a version which would work on a PC, and my scripts would run merrily along. Marc