This readme describes mostly the VPATH part of libtricks. For
information on fakeroot (faked), see fakeroot(1), or the source code...

BLURB:

With libtriks you can:
  - specify a VPATH that's not just seen by make, 
    but by every dynamically linked program.
  - Give programs a different view of for example /etc
  - check for sucpicious /tmp writes.
  - Fake a different UID (root, whatever). This is usefull for
    generation of Debian packages (And Digital Unix, Red Hat, ...).
  - Make your CD appear writable. (also works for /etc/passwd)
  - Use your imagination, to do a lot more.


The usual ((GNU) make) VPATH mechanism always works for the current
direcotry only, and works using the current directory (the one GNU make
started in) as ``root''. So, setting VPATH=foo:bar will cause 
the file "hello.c" to be searched in `pwd`, `pwd`/foo, and `pwd`/bar.
Whenever the cwd changes, a new VPATH has to be set.

The VPATH used by `libtricks' by default tries to be (somewhat) 
compatible with the way GNU make treats it's VPATH, although I don't
think this is the most useful kind of VPATH implementation. To be more
useful, libtricks lets you specify your VPATH ``root'' directory with
a regular expression: every filename that matches the regular expression
you specify will be eligible for VPATH replament. Also, the first path
it tries to find the file in doesn't have to be in the current directory,
or even the path specified in the open() call. This way a user can
run a program that has hardcoded in it a config filename of "/etc/foo",
and make it read /tmp/foo.


About the terminology:
  VPATH is the environement variable, that may contain one or more 
        vpath's (seperated by ::'s)

  vpath is what traditionaly is called the vpath (one list of
        directories seperated by :'s)
  
  path  a directory or file to look for


*********************************************************************


Introduction:

I'm not quite sure how to explain all of them details clearly, so I'll
start off with a few examples. In all examples I'll be using sh syntax.

The easiest would be:


  cd /home/me/project
  rm project.c; touch newfile.c
  LD_PRELOAD=/usr/lib/libtricks/libtricks.so.0 
  VPATH='/home/me/project:/usr/src/project'
  export VPATH LD_PRELOAD
  sh
  gcc project.c
  ls -al newfile.c

In this example, as the file 'project.c' doesn't exist in me's home directory,
(but it does exist in /usr/src/project), libtriks will open 
/usr/src/project/project.c for the C compiler. But the file 'newfile.c'
will be found in /home/me/project/, as that is earlier in the VPATH.

Now there's the question ``to what files does this search mechanism apply''?
In the above example libtriks wasn't told an answer to this question (see
below), so it assumes it applies to every file/directory that starts
in the same directory as the current dir the a process was started in.
So, to continue the above example

  /bin/cat subdir/somefile.c

will search for the file in /home/me/project/subdir, and then in in
/usr/src/project/subdir. But

  /bin/cat /etc/passwd

will directly open /etc/passwd. Note that when a new process is started,
the current directory is taken anew, so that

  cd subdir
  /bin/cat somefile.c

will search for somefile.c in /home/me/project/subdir,
and then in /home/me/project, and in /usr/src/project. This is because
now the current dir is '/home/me/project/subdir'. Also, doing now
  /bin/cat ../project.c
will fail, as /home/me/project/project.c itself doesn't exist, and
"../project.c" isn't inside the cwd when the cat started (so libtriks will
not consider to try the other paths in the VPATH).

This is the behaviour of GNU make, so I implemented this as the default.
It is however generally not what I would want. Usually, I want to specify
a fixed vpath ``root'' directory (/home/me/project in this case) that does
not depend on the cwd. You can do that like this:

  VPATH='chop("/home/me/project")=:/home/me/project:/usr/src/project'

(everything before a "=" sign in a path is used to specify options)
The chop(RE) function tells libtriks to first match the regular expression
RE for any opened file, and if it match, remove (chop) the matching part
off the pathname. So when you now do open(/home/me/project/subdir/file),
libtriks will try the RE, see that it matches, and then remove the
matching part form the path, leaving "/subdir/file". Then it will
prepend "/home/me/project" and "/usr/src/project" in turn and try if
it finds an existing file. So now the results don't depend on the
cwd when a process started.

It's quite easy to make typing errors in this VPATH, causing libtricks
to interpret it in stange ways. To view how libtricks reads your VPATH
specification, use detrick(1), as in:

VPATH='needexec&chop("/bin/")=:/usr/lib/libtricks/bin/:/bin' detrick

To make things somewhat more interesting, suppose you only want to
see the files in /usr/src/project that you cannot write to:

  VPATH='chop("/home/me/project")=:/home/me/project:!iswrite=/usr/src/project'

See? the "=" sign really isn't an exception for the first chop() function,
you can use it for every path. Here the iswrite function tests
for the writability for the file in the /usr/src/project directory, and
the ! means 'not'. So, only use a file in /usr/src/project if it's not
writable. Full list of "is*" functions: isread,iswrite,isexec,isdir.

You can AND two or more functions with a & sign, like !iswrite&isdir.
Se below for how to distinguish between various ways of opening modes
(needwrite etc).

As a last example, let me return to the first example:

  VPATH=`pwd`:/home/me/pro:/usr/src/pro

As mentioned, this checks for files in the cwd, as though you specified
chop("`pwd`")=: before the first vpath element. And this isn't very far 
from what actually happens: For every vpath that doesn't have a "=" in
the first pathelement, libtriks adds nice&chop("`pwd`")=: to the beginning
of the vpath. So the vpath above is internally translated to:

  VPATH="nice&chop(\"`pwd`\")=:isexist=/home/me/pro:isexist=/usr/src/pro"

The "nice" is an internally used function to remove things like 
"../dir" from the pathname (it always returns true). The only difference
with this VPATH and "/home/me/pro:usr/src/pro" is that for the latter, the
cwd is evaluated every time a process is started. So, when you do
"cd dir; /bin/cat", then /bin/cat will re-evaluate the cwd. Note that
I type "/bin/cat", as some shells may have a builtin cat -- then no new
process is started, and pwd isn't re-evaluated (bash, ksh, csh, tcsh
don't have a builtin cat).


*********************************************************************

More Detailed information:


VPATH       =vpath | vpath "::" VPATH
vpath       =component | component:vpath
component   =typespec=path
path        =     | UNIX path specification
typespec    =type | type "&" typespec
type        =["!", "-"]func

func can be preceded by "!" to invert the return value, or "-" to ignore it.

"func" is one of:

  check   interpret the following path as a regex. Only consider this path
          if the regex matches filename.
         
  chop    regex used to determine the ``vpath-root'' dir.
          Exampe: 
 	    VPATH='chop("/usr/(local|)/")=:/usr/:/usr/local/:/'
	  if open("/usr/local/bin/bash") is called, then first the
 	  matching "/usr/local/" part is removed, obtaining "bin/bash". Then
 	  this file is searched by prepending (in order) /usr/, /usr/local/,
	  and then /. (resulting in stat's for /usr/bin/bash,
	  /usr/local/bin/bash, /bin/bash).
	  If the regex does not match, this vpath isn't considered. 
	  (go to next vpath if available).

	  If the vpath doesn't contain any "chop=" types, then the
	  current working directory is used (to mimic GNU make's 
	  VPATH behaviour).

  needread,needwrite,needexec,needdir,needexcl,needstat,needchfl,needunl
          Only consider this path if the requesting function
	  needs the specified permissions/modes. For example:
 	     open(pathname,O_RDONLY)          needread,
	     chdir(pathname)                  needdir,
	     exec(pathname,"arg")             needexec.
	     stat(filename)                   needstat,
	     chown(filename) chmod(filename)  needchfl
	     rm(filename), unlink(filename)   needunl
	     mv(file1,file2)                  needunl for file2 
					      needchfl for file1
	  Does _not_ check whether the file/dir found actually matches
	  the specified permissions. The file opened/used depends on 
	  the arguments of the calling (wrapped) function, not on the
	  permissions of the files in the file system.
	  
  isread,iswrite,isexec,isdir
          checks for the filepermissions of the file in the specified
	  dir (the dir after the "=" sign).
	  Does _not_ check for the arguments of the calling(wrapped) 
	  function. The file/dir opened/used depends on the permissions
	  of the files in the filesystem, not on how the file is accessed.

  isexist
          True if file exists in this path.
	  NOTE: because it seems silly to force the specification of 
	  "exist" for every vpath component, "isexist" is silently added to 
	  just before the first isread/iswrite/isexec/isdir function,
	  unless [ |!|-]exists already was used. The stat() call actually
	  happens in the 'isexist'. If you specify "-isexist" before
	  any of the other is* functions, the results are undetermined.
	  (-isexist doesn't behave as it's described here: although the
	  results are ignored, it will still do the stat()).

  nice    Mostly internally used function. After this call the to lookup
          filename is made ``nicer'', that is, dir/.. pairs are removed etc.
	  this function is added silently before chop is used. 
	  (Intended improvement: If you don't want it to be added, use 
	  "-nice" before chop(). This doesn't work at the moment)

  stdout(arg)/stderr(arg)/syslog(arg)
	  use stdout/stderr/syslog to print the argument, after
	  doing the following substitutions:

	  $name->the (original) pathname
	  $need->(octal) flags given to open() etc, tested by the need* funcs.
	  $is  ->(octal) filemode on the filsystem, as tested by is* functions
	  $PID ->(decimal) process ID.
	  $cmd ->command and arguments (LINUX only, needs /proc/$$/cmdline).

	  Example: stdout("FILE  is-$is ne-$need $name")

	  Note 1: libtricks will not automatically insert a 'nice&isexist'
	    before stdout/stderr/syslog/system, so the $is variable may
	    be garbage (the stat of the file is only done in the 'isexist'
	    function, that is silently inserted before is* functions).

	  Note 2: If you do insert a 'isexist' all at the beginning (before
	    even the chop), as in the following example:
LD_PRELOAD=/usr/lib/libtricks/libtricks.so.0 \
VPATH='isexist&stdout("is=$is ne=$need f=$name")&chop("/tmp")=:='`pwd`:/tmp \
   sh -c "ls -al /tmp/*"
	    then the result will be drastically different: In the example
	    above, if I have a file 'hello' in my cwd, but /tmp/hello
	    doesn't exist, the "exist" test will fail. 
	  
	  Note 3: the $need reported is an OR of the following values:
  		   NEED_READ   01
  		   NEED_WRITE  02 
  		   NEED_EXEC   04  (for an exec(() call)
  		  
  		   NEED_DIR    010 (chdir)
  		   NEED_EXCL   020 (O_EXCL mode used in open() call)
 		   NEED_STAT   040 (stat(), lstat)
      		   NEED_CHFL  0100 ( change-file, like chmod, chown, 
      			  	      first arg to rename )
      		   NEED_UNLI  0200 (unlink, rm, second arg to rename )

          (return value: 1)

  writefile(filename,string)
          Simmilar to stderr etc. First parameter is the name of the file
	  to write output to. 

	  Example:   writefile("/tmp/libtricks-output","open file $name")

	  Note: see notes for stdout() etc.

  system()
          simmilar to syslog(). Returns: !(exit status of command)


  settime(arg0)
          set mod,access,etc time to arg0 seconds + 13.5 days after 
	  birth of the author. (also known as Unix Epoch). Return 
	  true if succesful (file exist, we are owner)

  agetime(arg0)
          make mod,access,etc times look arg0 seconds older.
	  
  return(int|errno_str)
          If a VPATH is processed while looking for a file, and
	  this function is seen, then the calling function will return
	  immediately, with errno set to the indicated value.
	  The return value of the function will indicate an error.

	  errno_str is one of the symbolic strings defined in
	  errno.h. 
	  Example: return(EPERM)

  regex(arg0)
          true if filename matches arg0
	  (not implemented)
  
  seds(match,subst)
          substitute ("sed -e 's/match/subst/" like) the filename.
	  (not implemented)


Exceptions:
  - chop and check
    causes an extra nice to be inserted before chop()/check().
    can only be specified once for each vpath.
  - isread, iswrite, isexec, settime(), agetime(), regex()
    cause an extra isexist to be inserted before the isread etc
    functions. (internally, the stat() is done in the isexist function).


**************************

legal (but possibly useless) examples:

    
  VPATH="chop(\"/tmp/\")=:needwrite&!needexcl&stderr("Warning- program tried to write to $name without specifying EXCL")=/tmp:/tmp"
    Issue a warning every time a program attempts to open files in /tmp
    without specifying O_EXCL (thus opening up symlink security holes).
    (would have been useful, had this been implemented)

  VPATH="chop(\".*/\")=`echo :$PATH|sed -e 's/:/:exec=/g'`"
    Might work to mimic $PATH if your shell were not to support it.
    (Though there's no way to run your $HOME/c/test executable any more)

  VPATH="chop(/bin)=:/home/me/bin:/bin::chop(/sbin)=:/home/me/sbin:/sbin"
    I've got executables in /home/me/{bin,sbin} that I want to be
    used instead of those in /bin and /sbin (works even for programs 
    that do system("/bin/ls")).

  VPATH="$VPATH::chop(/cd/Debian-2.0/source):/home/me/debian:!needwrite=/cd/Debian-2.0/source:-isexist=/home/me/debian"
    But I've also got the full Debian 2.0 source tree on a CD mounted on
    /cd/Debian-2.0/source, and my own local additions in /home/me/debian
    (Now the CD appears to be writable).
    
  VPATH='needexec&chop("/bin/")=:/usr/bin/libtricks/:/bin'
    Used in /usr/bin/fakeroot, causes files in /usr/bin/libtricks/ to
    be used in preference over those in /bin. Thus, by putting a
    custom su script in /usr/bin/libtricks/, one can make 'su sys'
    work. The 'needexec' at the beginning makes the whole thing much faster,
    as libtricks will now not attemt the regular expression matching
    (and many extra stats etc) for every file, but only for files to be
    exec-ed. It also makes it more interesting for unaware users, as
    'ls -al /bin/su' and 'cat /bin/su' will now both show the real
    file /bin/su (that will not be used when 'su' is executed)


**********************************************************


below is the new VPATH, to be implemented (soon, hopefully):

VPATH='test1&!(test2|test3)=:(/usr:/bin)*(/local:/mydir)'

which is identical to:

VPATH='test1&!test2&!test3=:/usr/local:/usr/mydir:/bin/local:/bin/mydir'
VPATH='?(func1&func2) 
VPATH       = vpath        | VPATH '::' vpath
vpath       = path         | test '=:' bracketpath
test        = test-term    | test '|' test-term
test-term   = test-faktor  | test-term '&' test-faktor
test-faktor = test-primary | '!' test-primary | '-' test-primary
test-primary= test         | test-funk
test-funk   = 'needread' | ... | 'stderr' '(' string ')' | ...

path=bracketpath, maar dan zonder de haakjes.

bracketpath = path-term    | path ':' path-term
path-term   = path-faktor  | path-faktor '*' path-term
path-faktor = path-primary | '(' path ')'
path-primary= '/'... | './'...

to be implemented! (=not yet implemented)

f=/a : g=/b : /c : /a

f&(g&T(/a), h&T(/b), (T(/c), T(/d)) ^ (T(/f), T(/g))), (volgende_vpath)

chop("/usr")|skip=/tmp:/

~!@#$%^&*(){}[]|:;,<>?  /
[chop("/tmp")]/usr

if(expression1)
  testvpath1_1
  testvpath1_2
  ...
if(expression2)
  testvpath2_1
  testvpath2_2
  ...

