home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / ixemul-45.0-src.tgz / tar.out / contrib / ixemul / library / __plock.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  16KB  |  586 lines

  1. /*
  2.  *  This file is part of ixemul.library for the Amiga.
  3.  *  Copyright (C) 1991, 1992  Markus M. Wild
  4.  *
  5.  *  This library is free software; you can redistribute it and/or
  6.  *  modify it under the terms of the GNU Library General Public
  7.  *  License as published by the Free Software Foundation; either
  8.  *  version 2 of the License, or (at your option) any later version.
  9.  *
  10.  *  This library is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  *  Library General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU Library General Public
  16.  *  License along with this library; if not, write to the Free
  17.  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19.  
  20. /*
  21.  * Lock() and LLock() emulation. Takes care of expanding paths that contain
  22.  * symlinks. 
  23.  * Call __plock() if you need a lock to the parent directory as used in
  24.  * other packets, that way you always get the "right" thing
  25.  */
  26.  
  27. #define _KERNEL
  28. #include "ixemul.h"
  29. #include "kprintf.h"
  30.  
  31. #include <stdlib.h>
  32. #include <string.h>
  33.  
  34. static struct DevProc    *get_device_proc (char *, struct DevProc *, int *);
  35. static int        unslashify (char *);
  36.  
  37. static void lock(struct MsgPort *handler, struct StandardPacket *sp, BPTR lock, char *s)
  38. {
  39.   sp->sp_Pkt.dp_Type = ACTION_LOCATE_OBJECT;
  40.   sp->sp_Pkt.dp_Arg1 = lock;
  41.   sp->sp_Pkt.dp_Arg2 = CTOBPTR(s);
  42.   sp->sp_Pkt.dp_Arg3 = ACCESS_READ;
  43.  
  44.   PutPacket(handler, sp);
  45.   __wait_sync_packet(sp);
  46. }
  47.  
  48. static void unlock(struct MsgPort *handler, struct StandardPacket *sp, BPTR lock)
  49. {
  50.   sp->sp_Pkt.dp_Port = __srwport;
  51.   sp->sp_Pkt.dp_Type = ACTION_FREE_LOCK;
  52.   sp->sp_Pkt.dp_Arg1 = lock;
  53.  
  54.   PutPacket(handler, sp);
  55.   __wait_sync_packet(sp);
  56. }
  57.  
  58. static void readlink(struct MsgPort *handler, struct StandardPacket *sp, BPTR lock, char *s)
  59. {
  60.   sp->sp_Pkt.dp_Port = __srwport;
  61.   sp->sp_Pkt.dp_Type = ACTION_READ_LINK;
  62.   sp->sp_Pkt.dp_Arg1 = lock;
  63.   sp->sp_Pkt.dp_Arg2 = (long)s; /* read as cstr */
  64.   sp->sp_Pkt.dp_Arg3 = (long)s; /* write as cstr, same place */
  65.   sp->sp_Pkt.dp_Arg4 = 255; /* what a BSTR can address */
  66.  
  67.   PutPacket(handler, sp);
  68.   __wait_sync_packet(sp);
  69. }
  70.  
  71. int is_pseudoterminal(char *name)
  72. {
  73.   int i = 1;
  74.  
  75.   if (!memcmp(name, "/dev/", 5) || !(i = memcmp(name, "dev:", 4)))
  76.     {
  77.       if (i)
  78.         name++;
  79.       if ((name[4] == 'p' || name[4] == 't') && name[5] == 't'
  80.           && name[6] == 'y' && name[7] >= 'p' && name[7] <= 'u'
  81.           && strchr("0123456789abcdef", name[8]) && !name[9])
  82.         return i + 4;
  83.     }
  84.   return 0;
  85. }
  86.  
  87. BPTR
  88. __plock (const char *file_name, int (*last_func)(), void *last_arg)
  89. {
  90.   BPTR parent_lock;
  91.   struct MsgPort *handler;
  92.   struct StandardPacket *sp;
  93.   unsigned char *bstr;
  94.   char *sep, *next = NULL, *cp;
  95.   int len;
  96.   /* true when we're processing the last element of name */
  97.   int is_last;
  98.   /* true after first pass, we should unlock all locks except the one
  99.    * we get as a sideeffect of DeviceProc */
  100.   int unlock_parent;
  101.   int link_levels;
  102.   int no_error;
  103.   BPTR result;
  104.   int res_res2;
  105.   int omask;
  106.   int is_fs;
  107.   short is_root = u.u_is_root;
  108.   struct DevProc *dp;
  109.   char *orig_name, *name;
  110.  
  111.   KPRINTF (("__plock: file_name = %s, last_func = $%lx\n", 
  112.         file_name ? file_name : "(none)", last_func));
  113.  
  114.   /* ``fix'' until I find the real problem in pdksh.. */
  115.   if (! file_name)
  116.     return 0;
  117.  
  118.   /* need to operate on a backup of the passed name, so I can do
  119.    * /sys -> sys: conversion in place */
  120.   name = alloca (strlen (file_name) + 2);
  121.   strcpy (name + 1, file_name);
  122.   if (is_root)
  123.     name[0] = '/';
  124.   else
  125.     name++;
  126.   orig_name = name;
  127.  
  128.   /* now get a LONG aligned packet */
  129.   sp = alloca (sizeof(*sp)+2);
  130.   sp = LONG_ALIGN (sp);
  131.   __init_std_packet(sp);
  132.  
  133.   /* allocate one BSTR-buffer. A length of 255 is enough, since a bstr
  134.    * can't address any more ;-) */
  135.   bstr = alloca (256 + 2);
  136.   bstr = LONG_ALIGN (bstr);
  137.   
  138.   /* NOTE: although we don't use any DOS calls here, we have to block
  139.    * any signals, since the locks we obtain in this function have to
  140.    * be freed before anything else can be done. This function is
  141.    * *not* reentrant in the sense that it can be interrupted without
  142.    * being finished */
  143.   omask = syscall (SYS_sigsetmask, ~0);
  144.  
  145.   dp = 0;
  146.  
  147. retry_multi_assign:
  148.  
  149.   name = orig_name;
  150.   if (ix.ix_flags & ix_translate_slash)
  151.     unslashify (name);
  152.  
  153.   if (!strcmp(name, ":"))
  154.     {
  155.       result = 0;
  156.       res_res2 = 6262;  /* another special special (the root directory) */
  157.       dp = 0;
  158.       goto do_return;
  159.     }
  160.   if (!strcasecmp(name, "nil:") || !strcasecmp(name, "/nil") ||
  161.       !strcmp(name, "/dev/null") || !strcmp(name, "dev:null"))
  162.     {
  163.     result = 0;
  164.         res_res2 = 4242;    /* special special ;-)))) */
  165.         dp = 0;
  166.         goto do_return;
  167.     }
  168.   if (is_pseudoterminal(name))
  169.     {
  170.       result = 0;
  171.       res_res2 = 5252;    /* special special ;-)))) */
  172.       dp = 0;
  173.       goto do_return;
  174.     }
  175.   if (!strcmp(name, "*") || !strcasecmp(name, "console:") ||
  176.       !strcmp(name, "/dev/tty"))
  177.     {
  178.       handler = (struct MsgPort *)(((struct Process *)FindTask (0))->pr_ConsoleTask);
  179.       parent_lock = 0;
  180.       name = "*";  /* Apparently use of CONSOLE: gave problems with
  181.                       Emacs, so we continue to use "*" instead. */
  182.       FreeDeviceProc (dp);
  183.       is_fs = 0;
  184.       dp = 0;
  185.     }
  186.   else
  187.     {
  188.       dp = get_device_proc (name, dp, &is_fs);
  189.  
  190.       handler = dp ? dp->dvp_Port : 0;
  191.       parent_lock = dp ? dp->dvp_Lock : 0;
  192.     }
  193.  
  194.   is_last = 0;
  195.   unlock_parent = 0;
  196.   link_levels = 0;
  197.   result = 0;
  198.  
  199.   if (! handler) 
  200.     {
  201.       res_res2 = ERROR_OBJECT_NOT_FOUND;
  202.       goto do_return;
  203.     }
  204.  
  205.   link_levels = 0;
  206.  
  207.   /* this seems logical, doesn't it? don't know ;-)) */
  208.   sep = index (name, ':');
  209.   if (sep) name = sep+1;
  210.  
  211.   do
  212.     {
  213.       KPRINTF (("__plock: solving for %s.\n", name));
  214.  
  215.       if (is_fs)
  216.         {
  217.       /* fetch the first part of "name", thus stopping at either a : or a / 
  218.        * next points at the start of the next directory component to be
  219.        * processed in the next run
  220.        */
  221.  
  222.       sep = index (name, ':');
  223.       if (sep) 
  224.         {
  225.           sep++; /* the : is part of the filename */
  226.           next = sep;
  227.         }
  228.       else
  229.         {
  230.           sep = index (name, '/');
  231.           
  232.           /* map foo/bar/ into foo/bar, but keep foo/bar// */
  233.           if (sep && sep[1]==0)
  234.             {
  235.               is_last = 1;
  236.               next = sep;
  237.             }
  238.           else if (! sep)
  239.             {
  240.               sep = name + strlen (name);
  241.               next = sep;
  242.               is_last = 1;
  243.             }
  244.           else
  245.             {
  246.               if (ix.ix_flags & ix_translate_slash)
  247.             for (next = sep + 1; *next == '/'; next ++) ;
  248.           else
  249.             next = sep + 1;
  250.         
  251.               /* if the slash is the first character, it means "parent",
  252.                * so we have to pass it literally to Lock() */
  253.               if (sep == name) sep = next;
  254.             }
  255.         }
  256.     }
  257.       else
  258.     {
  259.       sep = name + strlen (name);
  260.       is_last = 1;
  261.     }
  262.  
  263.       len = sep - name;
  264.       if (len) bcopy (name, bstr + 1, len);
  265.       *bstr = len;
  266.  
  267.       /* turn a ".." into a "/", and a "." into a "" */
  268.  
  269.       if (bcmp (bstr, "\2..", 3) == 0)
  270.         {
  271.       bstr[0] = 1; bstr[1] = '/';
  272.         }
  273.       else if (bcmp (bstr, "\1.", 2) == 0)
  274.         bstr[0] = 0;
  275.  
  276.       do
  277.     {
  278.       int res = 1;
  279.  
  280.           sp->sp_Pkt.dp_Port = __srwport;
  281.       if (! is_last)
  282.         {
  283.           lock(handler, sp, parent_lock, bstr);
  284.           no_error = sp->sp_Pkt.dp_Res1 > 0;
  285.         }
  286.       else
  287.         res = (*last_func)(sp, handler, parent_lock, CTOBPTR (bstr),
  288.                                last_arg, &no_error);
  289.  
  290.           is_root = 0;
  291.       /* if no error, fine */
  292.           if (no_error)
  293.             break;
  294.           if (is_fs && sp->sp_Pkt.dp_Res2 == ERROR_OBJECT_NOT_FOUND && !bcmp(bstr, "\1/", 2))
  295.             {
  296.               is_root = 1;
  297.               orig_name = next - 1;
  298.               *orig_name = '/';
  299.               FreeDeviceProc (dp);
  300.               dp = 0;
  301.               if (unlock_parent)
  302.                 unlock(handler, sp, parent_lock);
  303.               goto retry_multi_assign;
  304.             }
  305.           if (!res)
  306.             break;
  307.  
  308.       /* else check whether ordinary error or really symlink */
  309.           if (sp->sp_Pkt.dp_Res2 != ERROR_IS_SOFT_LINK) break;
  310.  
  311.       /* read the link. temporarily use our bstr as a cstr, thus setting
  312.            * a terminating zero byte and skipping the length byte */
  313.       bstr[*bstr + 1] = 0;
  314.  
  315.       readlink(handler, sp, parent_lock, bstr + 1);
  316.  
  317.       /* error (no matter which...) couldn't read the link */
  318.       if (sp->sp_Pkt.dp_Res1 <= 0)
  319.         {
  320.           /* this is our error-"lock", so make sure it is really zero */
  321.           sp->sp_Pkt.dp_Res1 = 0;
  322.           break;
  323.             }
  324.  
  325.       /* okay, new name. Set up as bstr and retry to lock it */
  326.       *bstr = sp->sp_Pkt.dp_Res1;
  327.  
  328.       /* if the read name is absolute, we may have to change the
  329.        * handler and the parent lock. Check for this */
  330.       bstr[*bstr + 1] = 0;
  331.       
  332.       if ((cp = index ((char *)bstr + 1, ':')))
  333.         {
  334.               if (unlock_parent)
  335.                 unlock(handler, sp, parent_lock);
  336.  
  337.           /* if this is ":foobar", then the handler stays the same, and the
  338.            * parent_lock gets zero. Don't need get_device_proc() to find
  339.            * this out ;-) */
  340.           if (cp == (char *)bstr+1)
  341.         parent_lock = 0;
  342.           else
  343.             {
  344.               /*
  345.                * NOTE: Multiassigns are currently only supported as the first
  346.                *       part of a path name. I don't like the idea of setting up
  347.                *       another recursion level here just to parse them...
  348.                *
  349.                * This approach also makes symbolic links to non-fs devices
  350.                * limited, which is bad. I'll HAVE to think something up
  351.                * (so you can't for example have dev:tty -> con:0/0/640/100/tty)
  352.                */
  353.                   handler = DeviceProc (bstr + 1);    /* XXX fix !!! */
  354.                   parent_lock = IoErr ();
  355.                 }
  356.               unlock_parent = 0;
  357.               
  358.               if (! handler)
  359.                 {
  360.                   /* interesting bug.. long not noticed... */
  361.  
  362.           sp->sp_Pkt.dp_Res1 = 0;
  363.           if (! strcasecmp (bstr + 1, "nil:"))
  364.             sp->sp_Pkt.dp_Res2 = 4242;
  365.           else
  366.             sp->sp_Pkt.dp_Res2 = ERROR_OBJECT_NOT_FOUND;
  367.           break;
  368.         }
  369.         }
  370.  
  371.       ++link_levels;
  372.     }
  373.       while (link_levels < MAXSYMLINKS);
  374.  
  375.       if (link_levels == MAXSYMLINKS)
  376.     {
  377.       result = 0;
  378.       res_res2 = ERROR_TOO_MANY_LEVELS;
  379.     }
  380.       else
  381.     {
  382.       result = sp->sp_Pkt.dp_Res1;
  383.       res_res2 = sp->sp_Pkt.dp_Res2;
  384.     }
  385.  
  386.       if (unlock_parent)
  387.         unlock(handler, sp, parent_lock);
  388.       else
  389.     unlock_parent = (result != 0);
  390.  
  391.       parent_lock = result;
  392.       name = next;
  393.     }
  394.   while (no_error && ! is_last);
  395.  
  396.   /* yes I know it's ugly ... */
  397.   if (!no_error && res_res2 == ERROR_OBJECT_NOT_FOUND 
  398.       && dp && (dp->dvp_Flags & DVPF_ASSIGN))
  399.     goto retry_multi_assign;
  400.  
  401. do_return:
  402.   FreeDeviceProc (dp);
  403.  
  404.   /* set up Result2 so that the IoErr() works */
  405.   ((struct Process *)FindTask (0))->pr_Result2 = res_res2;
  406.  
  407.   syscall (SYS_sigsetmask, omask);
  408.  
  409.   KPRINTF (("__plock: returning %ld, res2 = %ld.\n", result, res_res2));
  410.  
  411.   return result;
  412. }
  413.  
  414. /*
  415.  * this is somewhat similar to DeviceProc(), but with the difference that it's
  416.  * strictly passive, it won't start the handler, if it doesn't exist. This is
  417.  * vital to be able to deal with stubborn console handlers, that don't answer
  418.  * packets until they're really open..
  419.  * Returns:
  420.  *    HAN_UNDEF    device doesn't exist
  421.  *    HAN_CLONE_DEV    device exists, handler zero
  422.  *    HAN_FS_DEV    device exists, handler non-zero
  423.  *    HAN_NOT_A_DEV    exists, but is not a device
  424.  */
  425.  
  426. #define HAN_UNDEF    0
  427. #define HAN_CLONE_DEV    1
  428. #define HAN_FS_DEV    2
  429. #define    HAN_NOT_A_DEV    3
  430.  
  431. static int
  432. find_handler (char *bstr)
  433. {
  434.   struct RootNode *rn;
  435.   struct DosInfo *di;
  436.   struct DevInfo *dv;
  437.   int res = HAN_UNDEF;
  438.   
  439.   /* could probably use less drastic measures under 2.0... */
  440.   Forbid ();
  441.   rn = (struct RootNode *) DOSBase->dl_Root;
  442.   di = BTOCPTR (rn->rn_Info);
  443.   for (dv = BTOCPTR (di->di_DevInfo); dv; dv = BTOCPTR (dv->dvi_Next))
  444.     {
  445.       if (! strncasecmp (bstr, BTOCPTR (dv->dvi_Name), bstr[0]+1))
  446.         {
  447.           if (dv->dvi_Type == DLT_DEVICE)
  448.             res = dv->dvi_Task ? HAN_FS_DEV : HAN_CLONE_DEV;
  449.           else
  450.         res = HAN_NOT_A_DEV;
  451.       break;
  452.     }
  453.     }
  454.   Permit ();
  455.   return res;
  456. }
  457.  
  458. /*
  459.  * feels very much like GetDeviceProc(), but works under 1.3 as well. Under
  460.  * 2.0, we're using the dos-library GetDeviceProc(), under 1.3 that is
  461.  * emulated with own structures.
  462.  * is_fs is filled out with a best guess approach, since we can't use the
  463.  * proper packet on a handler that isn't yet fully operating (as after just
  464.  * calling DevProc)
  465.  * if calling with prev!=0, is_fs is not touched.
  466.  */
  467.  
  468. static struct DevProc *
  469. get_device_proc (char *name, struct DevProc *prev, int *is_fs)
  470. {
  471.   char *cp, *f = NULL;
  472.   int len;
  473.   struct DevProc *dp;
  474.   int han = HAN_UNDEF;
  475.   struct Process *this_proc = NULL;
  476.   APTR oldwin = NULL;
  477.  
  478.   if (! prev)
  479.     {
  480.       /* have to prove the opposite */
  481.       *is_fs = 1;
  482.  
  483.       cp = index (name, ':');
  484.       if (cp && cp != name)    /* ":..." has to be a filesystem */
  485.         {
  486.           len = cp - name;
  487.           f = alloca (len + 1);
  488.           f[0] = len;
  489.           bcopy (name, f + 1, len);
  490.  
  491.           /* try to find it */
  492.           han = find_handler (f);
  493.  
  494.       KPRINTF (("  find_handler(%s) = %ld\n", name, han));
  495.  
  496.       /* this might be wrong, we'll know more after GetDeviceProc() */
  497.           if (han == HAN_CLONE_DEV) 
  498.             *is_fs = 0;
  499.         }
  500.     }
  501.  
  502.   if (ix.ix_flags & ix_no_insert_disk_requester)
  503.     {
  504.       this_proc = (struct Process *)FindTask (0);
  505.       oldwin = this_proc->pr_WindowPtr;
  506.       this_proc->pr_WindowPtr = (APTR)-1;
  507.     }
  508.   dp = GetDeviceProc (name, prev);  
  509.   if (ix.ix_flags & ix_no_insert_disk_requester)
  510.     this_proc->pr_WindowPtr = oldwin;
  511.  
  512.   /* Second approach to verify, if a handler probably is a file system or not.
  513.    * If the device is a filesystem, but didn't contain a volume before, then
  514.    * the GetDeviceProc() call will have popped up the `please insert a disk'
  515.    * requester. If the user obeyed, the handler will now exist. On the other
  516.    * hand, if the device really is a clone device, it will still be one (ie. have
  517.    * its task field zero), so check the device list again. */
  518.  
  519.   /* only for possible clone devices */
  520.   if (han == HAN_CLONE_DEV)
  521.     *is_fs = find_handler (f) != HAN_CLONE_DEV;
  522.   
  523.   return dp;
  524. }
  525.  
  526. static int
  527. unslashify (char *name)
  528. {
  529.   char *oname = name;
  530.  
  531.   if (index (name, ':'))
  532.     return 0;
  533.  
  534.   while (oname[0] == '/' &&
  535.          (oname[1] == '/' || !memcmp(oname + 1, "./", 2) || !memcmp(oname + 1, "../", 3)))
  536.     while (*++oname == '.') ;
  537.  
  538.   if (!strcmp(oname, "/.") || !strcmp(oname, "/.."))
  539.     oname[1] = '\0';
  540.  
  541.   /* don't (!) use strcpy () here, this is an overlapping copy ! */
  542.   if (oname > name)
  543.     bcopy (oname, name, strlen (oname) + 1);
  544.     
  545.   /* root directory */
  546.   if (name[0] == '/' && name[1] == 0)
  547.     {
  548.       name[0] = ':';
  549.       return 0;
  550.     }
  551.  
  552.   if (name[0] == '/')
  553.     {
  554.       /* get the delimiter */
  555.       char *cp = index (name + 1, '/');
  556.       int shift = 0;
  557.  
  558.       /* if there is a separating (and not terminating) slash, shift a bit ;-) */
  559.       if (cp)
  560.     while (*cp == '/')
  561.           {
  562.             shift ++;
  563.         cp ++;
  564.       }
  565.  
  566.       /* is it a terminator (then discard it) or a separator ? */
  567.       if (! cp || !*cp)
  568.         {
  569.       /* terminator */
  570.           cp = name + strlen (name);
  571.           bcopy (name + 1, name, cp - name);
  572.           cp[-1-shift] = ':';
  573.           cp[-shift] = 0;
  574.     }
  575.       else
  576.     {
  577.       /* separator */
  578.       bcopy (name + 1, name, strlen (name) + 1);
  579.       cp --;
  580.       bcopy (cp, cp - (shift - 1), strlen (cp) + 1);
  581.       cp[-shift] = ':';
  582.     }
  583.     }
  584.   return 0;
  585. }
  586.