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 >
Wrap
C/C++ Source or Header
|
1996-09-28
|
16KB
|
586 lines
/*
* This file is part of ixemul.library for the Amiga.
* Copyright (C) 1991, 1992 Markus M. Wild
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Lock() and LLock() emulation. Takes care of expanding paths that contain
* symlinks.
* Call __plock() if you need a lock to the parent directory as used in
* other packets, that way you always get the "right" thing
*/
#define _KERNEL
#include "ixemul.h"
#include "kprintf.h"
#include <stdlib.h>
#include <string.h>
static struct DevProc *get_device_proc (char *, struct DevProc *, int *);
static int unslashify (char *);
static void lock(struct MsgPort *handler, struct StandardPacket *sp, BPTR lock, char *s)
{
sp->sp_Pkt.dp_Type = ACTION_LOCATE_OBJECT;
sp->sp_Pkt.dp_Arg1 = lock;
sp->sp_Pkt.dp_Arg2 = CTOBPTR(s);
sp->sp_Pkt.dp_Arg3 = ACCESS_READ;
PutPacket(handler, sp);
__wait_sync_packet(sp);
}
static void unlock(struct MsgPort *handler, struct StandardPacket *sp, BPTR lock)
{
sp->sp_Pkt.dp_Port = __srwport;
sp->sp_Pkt.dp_Type = ACTION_FREE_LOCK;
sp->sp_Pkt.dp_Arg1 = lock;
PutPacket(handler, sp);
__wait_sync_packet(sp);
}
static void readlink(struct MsgPort *handler, struct StandardPacket *sp, BPTR lock, char *s)
{
sp->sp_Pkt.dp_Port = __srwport;
sp->sp_Pkt.dp_Type = ACTION_READ_LINK;
sp->sp_Pkt.dp_Arg1 = lock;
sp->sp_Pkt.dp_Arg2 = (long)s; /* read as cstr */
sp->sp_Pkt.dp_Arg3 = (long)s; /* write as cstr, same place */
sp->sp_Pkt.dp_Arg4 = 255; /* what a BSTR can address */
PutPacket(handler, sp);
__wait_sync_packet(sp);
}
int is_pseudoterminal(char *name)
{
int i = 1;
if (!memcmp(name, "/dev/", 5) || !(i = memcmp(name, "dev:", 4)))
{
if (i)
name++;
if ((name[4] == 'p' || name[4] == 't') && name[5] == 't'
&& name[6] == 'y' && name[7] >= 'p' && name[7] <= 'u'
&& strchr("0123456789abcdef", name[8]) && !name[9])
return i + 4;
}
return 0;
}
BPTR
__plock (const char *file_name, int (*last_func)(), void *last_arg)
{
BPTR parent_lock;
struct MsgPort *handler;
struct StandardPacket *sp;
unsigned char *bstr;
char *sep, *next = NULL, *cp;
int len;
/* true when we're processing the last element of name */
int is_last;
/* true after first pass, we should unlock all locks except the one
* we get as a sideeffect of DeviceProc */
int unlock_parent;
int link_levels;
int no_error;
BPTR result;
int res_res2;
int omask;
int is_fs;
short is_root = u.u_is_root;
struct DevProc *dp;
char *orig_name, *name;
KPRINTF (("__plock: file_name = %s, last_func = $%lx\n",
file_name ? file_name : "(none)", last_func));
/* ``fix'' until I find the real problem in pdksh.. */
if (! file_name)
return 0;
/* need to operate on a backup of the passed name, so I can do
* /sys -> sys: conversion in place */
name = alloca (strlen (file_name) + 2);
strcpy (name + 1, file_name);
if (is_root)
name[0] = '/';
else
name++;
orig_name = name;
/* now get a LONG aligned packet */
sp = alloca (sizeof(*sp)+2);
sp = LONG_ALIGN (sp);
__init_std_packet(sp);
/* allocate one BSTR-buffer. A length of 255 is enough, since a bstr
* can't address any more ;-) */
bstr = alloca (256 + 2);
bstr = LONG_ALIGN (bstr);
/* NOTE: although we don't use any DOS calls here, we have to block
* any signals, since the locks we obtain in this function have to
* be freed before anything else can be done. This function is
* *not* reentrant in the sense that it can be interrupted without
* being finished */
omask = syscall (SYS_sigsetmask, ~0);
dp = 0;
retry_multi_assign:
name = orig_name;
if (ix.ix_flags & ix_translate_slash)
unslashify (name);
if (!strcmp(name, ":"))
{
result = 0;
res_res2 = 6262; /* another special special (the root directory) */
dp = 0;
goto do_return;
}
if (!strcasecmp(name, "nil:") || !strcasecmp(name, "/nil") ||
!strcmp(name, "/dev/null") || !strcmp(name, "dev:null"))
{
result = 0;
res_res2 = 4242; /* special special ;-)))) */
dp = 0;
goto do_return;
}
if (is_pseudoterminal(name))
{
result = 0;
res_res2 = 5252; /* special special ;-)))) */
dp = 0;
goto do_return;
}
if (!strcmp(name, "*") || !strcasecmp(name, "console:") ||
!strcmp(name, "/dev/tty"))
{
handler = (struct MsgPort *)(((struct Process *)FindTask (0))->pr_ConsoleTask);
parent_lock = 0;
name = "*"; /* Apparently use of CONSOLE: gave problems with
Emacs, so we continue to use "*" instead. */
FreeDeviceProc (dp);
is_fs = 0;
dp = 0;
}
else
{
dp = get_device_proc (name, dp, &is_fs);
handler = dp ? dp->dvp_Port : 0;
parent_lock = dp ? dp->dvp_Lock : 0;
}
is_last = 0;
unlock_parent = 0;
link_levels = 0;
result = 0;
if (! handler)
{
res_res2 = ERROR_OBJECT_NOT_FOUND;
goto do_return;
}
link_levels = 0;
/* this seems logical, doesn't it? don't know ;-)) */
sep = index (name, ':');
if (sep) name = sep+1;
do
{
KPRINTF (("__plock: solving for %s.\n", name));
if (is_fs)
{
/* fetch the first part of "name", thus stopping at either a : or a /
* next points at the start of the next directory component to be
* processed in the next run
*/
sep = index (name, ':');
if (sep)
{
sep++; /* the : is part of the filename */
next = sep;
}
else
{
sep = index (name, '/');
/* map foo/bar/ into foo/bar, but keep foo/bar// */
if (sep && sep[1]==0)
{
is_last = 1;
next = sep;
}
else if (! sep)
{
sep = name + strlen (name);
next = sep;
is_last = 1;
}
else
{
if (ix.ix_flags & ix_translate_slash)
for (next = sep + 1; *next == '/'; next ++) ;
else
next = sep + 1;
/* if the slash is the first character, it means "parent",
* so we have to pass it literally to Lock() */
if (sep == name) sep = next;
}
}
}
else
{
sep = name + strlen (name);
is_last = 1;
}
len = sep - name;
if (len) bcopy (name, bstr + 1, len);
*bstr = len;
/* turn a ".." into a "/", and a "." into a "" */
if (bcmp (bstr, "\2..", 3) == 0)
{
bstr[0] = 1; bstr[1] = '/';
}
else if (bcmp (bstr, "\1.", 2) == 0)
bstr[0] = 0;
do
{
int res = 1;
sp->sp_Pkt.dp_Port = __srwport;
if (! is_last)
{
lock(handler, sp, parent_lock, bstr);
no_error = sp->sp_Pkt.dp_Res1 > 0;
}
else
res = (*last_func)(sp, handler, parent_lock, CTOBPTR (bstr),
last_arg, &no_error);
is_root = 0;
/* if no error, fine */
if (no_error)
break;
if (is_fs && sp->sp_Pkt.dp_Res2 == ERROR_OBJECT_NOT_FOUND && !bcmp(bstr, "\1/", 2))
{
is_root = 1;
orig_name = next - 1;
*orig_name = '/';
FreeDeviceProc (dp);
dp = 0;
if (unlock_parent)
unlock(handler, sp, parent_lock);
goto retry_multi_assign;
}
if (!res)
break;
/* else check whether ordinary error or really symlink */
if (sp->sp_Pkt.dp_Res2 != ERROR_IS_SOFT_LINK) break;
/* read the link. temporarily use our bstr as a cstr, thus setting
* a terminating zero byte and skipping the length byte */
bstr[*bstr + 1] = 0;
readlink(handler, sp, parent_lock, bstr + 1);
/* error (no matter which...) couldn't read the link */
if (sp->sp_Pkt.dp_Res1 <= 0)
{
/* this is our error-"lock", so make sure it is really zero */
sp->sp_Pkt.dp_Res1 = 0;
break;
}
/* okay, new name. Set up as bstr and retry to lock it */
*bstr = sp->sp_Pkt.dp_Res1;
/* if the read name is absolute, we may have to change the
* handler and the parent lock. Check for this */
bstr[*bstr + 1] = 0;
if ((cp = index ((char *)bstr + 1, ':')))
{
if (unlock_parent)
unlock(handler, sp, parent_lock);
/* if this is ":foobar", then the handler stays the same, and the
* parent_lock gets zero. Don't need get_device_proc() to find
* this out ;-) */
if (cp == (char *)bstr+1)
parent_lock = 0;
else
{
/*
* NOTE: Multiassigns are currently only supported as the first
* part of a path name. I don't like the idea of setting up
* another recursion level here just to parse them...
*
* This approach also makes symbolic links to non-fs devices
* limited, which is bad. I'll HAVE to think something up
* (so you can't for example have dev:tty -> con:0/0/640/100/tty)
*/
handler = DeviceProc (bstr + 1); /* XXX fix !!! */
parent_lock = IoErr ();
}
unlock_parent = 0;
if (! handler)
{
/* interesting bug.. long not noticed... */
sp->sp_Pkt.dp_Res1 = 0;
if (! strcasecmp (bstr + 1, "nil:"))
sp->sp_Pkt.dp_Res2 = 4242;
else
sp->sp_Pkt.dp_Res2 = ERROR_OBJECT_NOT_FOUND;
break;
}
}
++link_levels;
}
while (link_levels < MAXSYMLINKS);
if (link_levels == MAXSYMLINKS)
{
result = 0;
res_res2 = ERROR_TOO_MANY_LEVELS;
}
else
{
result = sp->sp_Pkt.dp_Res1;
res_res2 = sp->sp_Pkt.dp_Res2;
}
if (unlock_parent)
unlock(handler, sp, parent_lock);
else
unlock_parent = (result != 0);
parent_lock = result;
name = next;
}
while (no_error && ! is_last);
/* yes I know it's ugly ... */
if (!no_error && res_res2 == ERROR_OBJECT_NOT_FOUND
&& dp && (dp->dvp_Flags & DVPF_ASSIGN))
goto retry_multi_assign;
do_return:
FreeDeviceProc (dp);
/* set up Result2 so that the IoErr() works */
((struct Process *)FindTask (0))->pr_Result2 = res_res2;
syscall (SYS_sigsetmask, omask);
KPRINTF (("__plock: returning %ld, res2 = %ld.\n", result, res_res2));
return result;
}
/*
* this is somewhat similar to DeviceProc(), but with the difference that it's
* strictly passive, it won't start the handler, if it doesn't exist. This is
* vital to be able to deal with stubborn console handlers, that don't answer
* packets until they're really open..
* Returns:
* HAN_UNDEF device doesn't exist
* HAN_CLONE_DEV device exists, handler zero
* HAN_FS_DEV device exists, handler non-zero
* HAN_NOT_A_DEV exists, but is not a device
*/
#define HAN_UNDEF 0
#define HAN_CLONE_DEV 1
#define HAN_FS_DEV 2
#define HAN_NOT_A_DEV 3
static int
find_handler (char *bstr)
{
struct RootNode *rn;
struct DosInfo *di;
struct DevInfo *dv;
int res = HAN_UNDEF;
/* could probably use less drastic measures under 2.0... */
Forbid ();
rn = (struct RootNode *) DOSBase->dl_Root;
di = BTOCPTR (rn->rn_Info);
for (dv = BTOCPTR (di->di_DevInfo); dv; dv = BTOCPTR (dv->dvi_Next))
{
if (! strncasecmp (bstr, BTOCPTR (dv->dvi_Name), bstr[0]+1))
{
if (dv->dvi_Type == DLT_DEVICE)
res = dv->dvi_Task ? HAN_FS_DEV : HAN_CLONE_DEV;
else
res = HAN_NOT_A_DEV;
break;
}
}
Permit ();
return res;
}
/*
* feels very much like GetDeviceProc(), but works under 1.3 as well. Under
* 2.0, we're using the dos-library GetDeviceProc(), under 1.3 that is
* emulated with own structures.
* is_fs is filled out with a best guess approach, since we can't use the
* proper packet on a handler that isn't yet fully operating (as after just
* calling DevProc)
* if calling with prev!=0, is_fs is not touched.
*/
static struct DevProc *
get_device_proc (char *name, struct DevProc *prev, int *is_fs)
{
char *cp, *f = NULL;
int len;
struct DevProc *dp;
int han = HAN_UNDEF;
struct Process *this_proc = NULL;
APTR oldwin = NULL;
if (! prev)
{
/* have to prove the opposite */
*is_fs = 1;
cp = index (name, ':');
if (cp && cp != name) /* ":..." has to be a filesystem */
{
len = cp - name;
f = alloca (len + 1);
f[0] = len;
bcopy (name, f + 1, len);
/* try to find it */
han = find_handler (f);
KPRINTF ((" find_handler(%s) = %ld\n", name, han));
/* this might be wrong, we'll know more after GetDeviceProc() */
if (han == HAN_CLONE_DEV)
*is_fs = 0;
}
}
if (ix.ix_flags & ix_no_insert_disk_requester)
{
this_proc = (struct Process *)FindTask (0);
oldwin = this_proc->pr_WindowPtr;
this_proc->pr_WindowPtr = (APTR)-1;
}
dp = GetDeviceProc (name, prev);
if (ix.ix_flags & ix_no_insert_disk_requester)
this_proc->pr_WindowPtr = oldwin;
/* Second approach to verify, if a handler probably is a file system or not.
* If the device is a filesystem, but didn't contain a volume before, then
* the GetDeviceProc() call will have popped up the `please insert a disk'
* requester. If the user obeyed, the handler will now exist. On the other
* hand, if the device really is a clone device, it will still be one (ie. have
* its task field zero), so check the device list again. */
/* only for possible clone devices */
if (han == HAN_CLONE_DEV)
*is_fs = find_handler (f) != HAN_CLONE_DEV;
return dp;
}
static int
unslashify (char *name)
{
char *oname = name;
if (index (name, ':'))
return 0;
while (oname[0] == '/' &&
(oname[1] == '/' || !memcmp(oname + 1, "./", 2) || !memcmp(oname + 1, "../", 3)))
while (*++oname == '.') ;
if (!strcmp(oname, "/.") || !strcmp(oname, "/.."))
oname[1] = '\0';
/* don't (!) use strcpy () here, this is an overlapping copy ! */
if (oname > name)
bcopy (oname, name, strlen (oname) + 1);
/* root directory */
if (name[0] == '/' && name[1] == 0)
{
name[0] = ':';
return 0;
}
if (name[0] == '/')
{
/* get the delimiter */
char *cp = index (name + 1, '/');
int shift = 0;
/* if there is a separating (and not terminating) slash, shift a bit ;-) */
if (cp)
while (*cp == '/')
{
shift ++;
cp ++;
}
/* is it a terminator (then discard it) or a separator ? */
if (! cp || !*cp)
{
/* terminator */
cp = name + strlen (name);
bcopy (name + 1, name, cp - name);
cp[-1-shift] = ':';
cp[-shift] = 0;
}
else
{
/* separator */
bcopy (name + 1, name, strlen (name) + 1);
cp --;
bcopy (cp, cp - (shift - 1), strlen (cp) + 1);
cp[-shift] = ':';
}
}
return 0;
}