/* * This file is part of ixemul.library for the Amiga. * Copyright (C) 1991, 1992 Markus M. Wild * Portions Copyright (C) 1994 Rafael W. Luebbert * * 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. * * $Id: open.c,v 1.2 2006/02/09 09:56:49 piru Exp $ */ #define _KERNEL #include "ixemul.h" #include "kprintf.h" #include /* standard functions.. could get overridden in a network environment */ extern int __ioctl(), __fselect(), __close(); /* "normal" functions, means do half-async writes & sync reads */ extern int __write(), __read(), __open(); /* incore functions */ extern int __mread(), __mclose(), __mselect(); static struct ix_mutex open_sem; #define D(x) int open(char *name, int mode, int perms) { int fd; struct file *f; BPTR fh; int late_stat; int omask, error; char ptymask = 0; int ptyindex = 0; int amode = 0, i; usetup; D(dprintf("open: <%s> mode 0x%lx perms 0x%lx\n", name, mode, perms);) if (name == NULL) /* sanity check */ return EACCES; mode = FFLAGS(mode); /* inhibit signals */ omask = syscall (SYS_sigsetmask, ~0); error = falloc (&f, &fd); if (error) { syscall (SYS_sigsetmask, omask); errno = error; D(dprintf("open: falloc failed\n");) KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno)); return -1; } /* we now got the file, ie. since its count is > 0, no other process * will get it with falloc() */ if (u.u_parent_userdata){u.u_ofile[fd] = 0xdeadbeef;} late_stat = 0; // The code between the stat() and the actual open() is critical ix_mutex_lock(&open_sem); if (stat(name, &f->f_stb) < 0) { D(dprintf("open: stat failed\n");) /* there can mainly be two reasons for stat() to fail. Either the * file really doesn't exist (ENOENT), or then the filesystem/handler * doesn't support file locks. */ /* if we should get out of here without an error, init the stat * buffer after having opened the file with __fstat, this sets some * reasonable parameters (see end of function). */ late_stat = 1; if ((errno == ENOENT) && (mode & O_CREAT)) { /* can't set permissions on an open file, so this has to be done * by 'close' */ f->f_stb.st_mode = (perms & ~u.u_cmask); f->f_stb_dirty |= FSDF_MODE; if (!muBase) { f->f_stb.st_uid = geteuid(); f->f_stb.st_gid = getegid(); if (f->f_stb.st_uid != (uid_t)(-2) || f->f_stb.st_gid != (gid_t)(-2)) f->f_stb_dirty |= FSDF_OWNER; } } } f->f_flags = mode & FMASK; f->f_ttyflags = IXTTY_ICRNL | IXTTY_OPOST | IXTTY_ONLCR; /* initialise the packet. The only thing needed at this time is its * header, filling in of port, action & args will be done when it's * used */ __init_std_packet (&f->f_sp); __init_std_packet ((void *)&f->f_select_sp); /* check for case-sensitive filename */ if ((mode & O_CASE) && !late_stat && !strchr(name, ':') && filenamecmp(name)) { D(dprintf("open: case sensitive failed\n");) error = ENOENT; goto error; } /* ok, so lets try to open the file... */ /* do this *only* if the stat() above was successful !! */ if (!late_stat && S_ISDIR (f->f_stb.st_mode) && !(mode & FWRITE)) { if (convert_dir (f, name, omask)) { ix_mutex_unlock(&open_sem); goto ret_ok; } else { D(dprintf("open: convert dir failed\n");) goto error; } } /* filter invalid modes */ switch (mode & (O_CREAT|O_TRUNC|O_EXCL)) { case O_EXCL: case O_EXCL|O_TRUNC: /* can never succeed ! */ D(dprintf("open: O_EXCL failed\n");) error = EINVAL; goto error; case O_CREAT|O_EXCL: case O_CREAT|O_EXCL|O_TRUNC: if (! late_stat) { D(dprintf("open: O_CREAT|O_EXCL|O_TRUNC: late_stat failed\n");) error = EEXIST; goto error; } break; } amode = (mode & O_CREAT) ? MODE_READWRITE : MODE_OLDFILE; if (!strcmp(name, "/dev/tty")) name = "*"; else if ((i = is_pseudoterminal(name))) { char *orig_name = name; char mask; name = "/fifo/ptyXX/rweksm"; memcpy(name + 7, orig_name + i + 1, 4); name[17] = (orig_name[i] == 'p' ? 'm' : 'c'); mask = (name[17] == 'm' ? IX_PTY_MASTER : IX_PTY_SLAVE) | IX_PTY_CLOSE; ptyindex = (name[9] - 'p') * 16 + name[10] - (name[10] >= 'a' ? 'a' - 10 : '0'); ix_lock_base(); if (ix.ix_ptys[ptyindex] & mask) { ix_unlock_base(); error = EIO; D(dprintf("open: pty mask EIO failed\n");) goto error; } ptymask = mask; ix.ix_ptys[ptyindex] |= (mask & IX_PTY_OPEN); /* mark pty in use */ ix_unlock_base(); } do { #if 0 if (!strcmp(name, "*") || !strcasecmp(name, "console:")) /* Temporary patch for KingCON 1.3, which seems to have problems with ACTION_FINDINPUT of "*"/"console:" when "dp_Port" of the packet is not set to sender's "pr_MsgPort" - that's what IXEmul makes on clients' startup during initialization of "stderr". */ fh = Open(name, amode); else #endif fh = __open (name, amode); if (! fh) { int err = IoErr(); D(dprintf("open: __open failed ioerr %ld\n", err);) /* For those handlers that do not understand MODE_READWRITE (e.g. PAR: ) */ if (err == ERROR_ACTION_NOT_KNOWN && amode == MODE_READWRITE) { amode = MODE_NEWFILE; } else { error = __ioerr_to_errno (err); goto error; } } } while (!fh); /* 19-Jul-2003 bugfix: Seek to end of the file for O_APPEND - Piru */ #if 1 if (mode & O_APPEND) { (void) Seek(fh, 0, OFFSET_END); } #endif // End of critical section ix_mutex_unlock(&open_sem); /* now.. we're lucky, we actually opened the file! */ f->f_fh = (struct FileHandle *) BTOCPTR(fh); if (mode & FWRITE) f->f_write = __write; if (mode & FREAD) f->f_read = __read; f->f_ioctl = __ioctl; f->f_select = __fselect; f->f_close = __close; f->f_type = DTYPE_FILE; /* * have to use kmalloc() instead of malloc(), because this is no task-private * data, it could (in the future) be shared by other tasks */ f->f_name = (void *) kmalloc (strlen (name) + 1); if (f->f_name) strcpy (f->f_name, name); ret_ok: /* ok, we're almost done. If desired, init the stat buffer to the * information we can get from an open file descriptor */ if (late_stat) __fstat (f); /* if the file qualifies, try to change it into a DTYPE_MEM file */ #if 0 /* XXX: DTYPE_MEM is broken. Disabled for now */ if (!late_stat && f->f_type == DTYPE_FILE && f->f_stb.st_size < ix.ix_membuf_limit && mode == FREAD) { void *buf; /* try to obtain the needed memory */ buf = (void *) kmalloc (f->f_stb.st_size); if (buf) if (syscall (SYS_read, fd, buf, f->f_stb.st_size) == f->f_stb.st_size) { #if 1 Close (CTOBPTR (f->f_fh)); #else __Close (CTOBPTR (f->f_fh)); #endif f->f_type = DTYPE_MEM; f->f_mf.mf_offset = 0; f->f_mf.mf_buffer = buf; f->f_read = __mread; f->f_close = __mclose; f->f_ioctl = 0; f->f_select = __mselect; } else kfree (buf); } #endif syscall (SYS_sigsetmask, omask); if (error) { D(dprintf("open: error %ld\n", error);) errno = error; KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno)); } if (!error && (mode & O_TRUNC) && (amode != MODE_NEWFILE)) { syscall(SYS_ftruncate, fd, 0); f->f_stb_dirty |= FSDF_UTIME; } /* return the descriptor */ return fd; error: // End of critical section ix_mutex_unlock(&open_sem); if (ptymask) { ix_lock_base(); ix.ix_ptys[ptyindex] &= ~(ptymask & IX_PTY_OPEN); ix_unlock_base(); } f->f_count--; if (f->f_count == 0) ffree(f); syscall (SYS_sigsetmask, omask); errno = error; KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno)); if (u.u_parent_userdata){u_ptr = u.u_parent_userdata;} /* free the file */ u.u_ofile[fd] = 0; return -1; }