From: terry@thisbe.Eng.Sandy.Novell.COM (Terry Lambert)
Subject: Re: cd-ing to a dir using a process, then staying there on exit.
Organization: Novell NPD -- Sandy, UT
Date: Thu, 20 Aug 1992 22:47:18 GMT

In article <1992Aug20.190826.7119@prl.dec.com> boyd@prl.dec.com (Boyd Roberts) writes:
>If you're going to post any such pretense get the details right:
>
>In article <1992Aug20.163507.4449@gateway.novell.com>, terry@thisbe.Eng.Sandy.Novell.COM (Terry Lambert) writes:
>> 
>> 5)	Get the locked vnode number for the process' cwd from the u struct.
>> 
>
>The [igv]node will most probably _not_ be locked, and the u-area
>holds a pointer to it.

Sorry; I meant "pointer".  In ufs_inode.c in the routine iget(), (which
is used to get the vnode pointer), VN_HOLD() is called on the vnode, locking
it.  On Solaris 2.0 and NCRs SMP 5.4.0 R2, this is mediated by a mutex.  In
Unisys' SMP 5.4.0 R2, the inode access is mediated by a mutex.  In all
cases, vp->v_count is either set to 1 (an initial reference) or incremented
(a reference from a cache hit in the dnlc).  This is the locking to which
I was referring (as opposed to the inode locking, which will also take place,
and the fact that the vnode address returned is the address of the vnode
within the in core inode).  This can easily be demonstrated (follows).  The
problem which is potentially going to kill you is the decrement v_count to
zero, which could happen if you were attempting to remove the vnode reference
and replace it with something else, rather than just swapping it between the
parent and the child (as I have suggested).

	1)	Login as root to UNIX terminal #1
	2)	Login as root to UNIX terminal #2
	3)	Mount a mountable file system on /mnt from terminal #1
	4)	cd to /mnt on UNIX terminal #2
	5)	Unmount /mnt from UNIX terminal #1
	6)	See the "busy" message from a non-0 vp->v_count
-OR-
	1)	Download UFS source from gatekeeper.dec.com
	2)	look at ufs_inode.c, function iget().

>> 7)	To avoid having to reverse map the parent's cwd to a file name and
>> 	to avoid dorking the reference count on the vnodes and locking up
>> 	your file system, swap cwd's between the parent and the child
>> 	process.  Do this by writing the childs vnode number into the
>> 	parent's u struct, and the parent's into the childs.  This is the
>> 	sneaky bit.
>
>It's a _pointer_ and all of this will break for a variety of reasons.

Again, *sorry*... *meant* "pointer"!  Sheesh; like someone doing this would
not have to look at the /usr/include/sys/vnode.h or
/usr/include/sys/fs/ufs_inode.h files and see it for themselves!

Not because it's a pointer.  As a matter of fact, the pointer is a reference
to a kernel space address, which, as we all know is invariant for all user
processes.  This is beacause vnodes aren't mapped out of the processes
address space, they are mapped from the inode cache (since vnodes are
declared as objects rather than pointers to objects).  Since we are
referencing from /dev/kmem (and possibly /dev/swap, depending on the box),
the addresses can be looked up *without kernel to user page translation*
or vice versa.  Thus the pointer is a simple lseek() offset, which we
don't care about, since we want to swap the pointer *values*, not
*contents* (ie: we never need to dereference the things).

The one thing that might bite you is accessing things as other than longs,
since a lot of processors hate unaligned data access (which is why you
can crash some boxes by cat'ing /dev/kmem).  Fortuantely, these values will
be aligned in the kernel on such architectures.

Using the kstuff package (Ftp from wuarchive.wustl.edu), I wrote something
that does this in less than 20 minutes on a Sun SPARCStation IPX (so it
didn't break, as predicted)... but the idea itself, and the concept of
having to have write access to /dev/kmem for it s so radioactive (many,
many bogons!) in the first place, I doubt if I would ever use it anywhere
for any reason, or even if there is any potential legitimate use for it at
all.

A better (but more expensive in terms of having to really remap pages) is
to write a system call to do it.  This is fairly easy on the Sun, as well,
as the u struct is permanently mapped to different kernel addresses for
each process (unlike SysV, that likes to map the struct into the same
(and in many cases on the 386, user writable) area for all users, and it's
relatively easy to assume the parents credentials and make the chdir() call
on the parent's behalf.  The nifty thing about this is that you just need
to declare 3 int's as parameters to the "chpdir()" call (my parental chdir())
and then call the real chdir() from within the system call on the parent's
behalf.  When you are done, zap everything back, and return to the caller.

Sequence of events:

	1)	Find the parent u struct offset for cwd for the parent.
	2)	Swap child, parent cwd's
	3)	Save child credentials
	4)	Copy parent credentials to user
	5)	Call chdir to change "childs" dir
	6)	Restore childs credentials
	7)	Swap child, parent cwd's (paren has new cwd, child has same
		cwd as started with)
	8)	Return to caller (child).

By virtue of kernel serialization, we don't even have a problem if the
child is executing and the parent is running in the background.

Note: This is not recommended for processes which are children of init. 8-).

The system call implementation (I decided to make it hard on myself and
reverse engineer loadable system calls from the SPARCPrinter drivers rather
than just modding /sys/os/init_sysent.c) took about 30 minutes, most of
that dedicated to the loading of the calls.

Note that the first method has the advantage of potentially working under
AIX, since the u struct is not directly available to loaded system calls in
AIX.  The second has the advantage of not being really dangerous to let normal
users use, since you can check if the parent is init before allowing it to
proceed.  If you disallow differences in parent and child credentials, then
you don't even have to do the credentials swap.

In any case, both implementations work... I'd have to posit that it's
possible to do.

					Terry Lambert
					terry_lambert@gateway.novell.com
					terry@icarus.weber.edu

---
Disclaimer:  Any opinions in this posting are my own and not those of
my present or previous employers.
Newsgroups: comp.unix.internals