//+++2004-08-31 // Copyright (C) 2004 Mike Rieker, Beverly, MA USA // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; version 2 of the License. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //---2004-08-31 /************************************************************************/ /* */ /* This is the general disk filesystem driver, fs independent layer */ /* */ /* It processes file system I/O requestes and generates the necessary */ /* disk I/O requests. */ /* */ /************************************************************************/ #define OZ_VDFS_VERSION 5 #include "ozone.h" #include "oz_dev_timer.h" #include "oz_dev_vdfs.h" #include "oz_hw_bootblock.h" #include "oz_io_console.h" #include "oz_io_disk.h" #include "oz_io_fs.h" #include "oz_knl_dcache.h" #include "oz_knl_devio.h" #include "oz_knl_event.h" #include "oz_knl_hw.h" #include "oz_knl_kmalloc.h" #include "oz_knl_procmode.h" #include "oz_knl_sdata.h" #include "oz_knl_section.h" #include "oz_knl_security.h" #include "oz_knl_shuthand.h" #include "oz_knl_spte.h" #include "oz_knl_status.h" #include "oz_knl_thread.h" #include "oz_knl_userjob.h" #include "oz_sys_dateconv.h" #include "oz_sys_xprintf.h" #define THREAD_PRIORITY oz_knl_user_getmaxbasepri (NULL) typedef struct OZ_VDFS_Chnex Chnex; typedef struct OZ_VDFS_Devex Devex; typedef struct OZ_VDFS_File File; typedef struct OZ_VDFS_Fileid Fileid; typedef struct OZ_VDFS_Iopex Iopex; typedef struct OZ_VDFS_Vector Vector; typedef struct OZ_VDFS_Volume Volume; typedef struct OZ_VDFS_Wildscan Wildscan; /* Test the directory bit */ #define IS_DIRECTORY(__file) ((*(__file -> volume -> devex -> vector -> is_directory)) (__file)) /* File-id structure */ struct OZ_VDFS_Fileid { uByte opaque[OZ_FS_MAXFIDLN]; }; /* Function table */ static uLong oz_disk_fs_clonecre (OZ_Devunit *template_devunit, void *template_devexv, int template_cloned, OZ_Procmode procmode, OZ_Devunit **cloned_devunit); static int oz_disk_fs_clonedel (OZ_Devunit *cloned_devunit, void *devexv, int cloned); static uLong oz_disk_fs_assign (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Procmode procmode); static int oz_disk_fs_deassign (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv); static void oz_disk_fs_abort (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Ioop *ioop, void *iopexv, OZ_Procmode procmode); static uLong oz_disk_fs_start (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Procmode procmode, OZ_Ioop *ioop, void *iopexv, uLong funcode, uLong as, void *ap); static const OZ_Devfunc oz_disk_fs_functable = { sizeof (Devex), sizeof (Chnex), sizeof (Iopex), 0, NULL, oz_disk_fs_clonecre, oz_disk_fs_clonedel, oz_disk_fs_assign, oz_disk_fs_deassign, oz_disk_fs_abort, oz_disk_fs_start, NULL }; /* Driver static data */ static File *crash_file = NULL; static OZ_IO_disk_crash crash_disk; /* I/O request processing routine table */ /* - these routines are their own kernel threads */ static uLong kt_initvol (void *iopexv); static uLong kt_mountvol (void *iopexv); static void shuthand (void *devexv); /* - the be_ routines are called by the kt_mountvol thread to process requests queued to devex->iopexqh/iopexqt */ /* the sc_ routines are called directly by the startup routines */ static uLong be_shutdown (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_dismount (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_verifyvol (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_create (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_open (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_close (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_enter (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_remove (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_rename (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_extend (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong sc_writeblocks (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong sc_readblocks (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong sc_ixdeb (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong sc_writerec (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong sc_readrec (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong sc_getinfo1 (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong sc_readdir (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_getsecattr (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_writeboot (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_setcurpos (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong sc_wildscan (Iopex *iopex, Chnex *chnex, Devex *devex); static void wildscan_topdiropen (void *chnexv, uLong status); static void wildscan_subdiropen (void *chnexv, uLong status); static void wildscan_startdir (Chnex *chnex); static void wildscan_lowipl (void *chnexv, OZ_Lowipl *lowipl); static void wildscan_dirread (void *chnexv, uLong status); static int wildscan_match (char const *wildcard, char const *filename, int tilde); static void wildscan_unlink (Wildscan *wildscan); static void wildscan_lockdir (void (*entry) (Chnex *chnex), Chnex *chnex); static void wildscan_trytolock (void *chnexv, OZ_Event *event); static void wildscan_unlkdir (Chnex *chnex); static uLong be_getinfo2 (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_getinfo3 (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_crash (Iopex *iopex, Chnex *chnex, Devex *devex); static uLong be_parse (Iopex *iopex, Chnex *chnex, Devex *devex); /* Internal routines */ static uLong queuerecio (Iopex *iopex, Chnex *chnex, Devex *devex); static void finishrecio (Iopex *iopex, uLong status, void (*finentry) (void *iopexv, uLong *status_r)); static uLong startshortcut (Chnex *chnex, Devex *devex, Iopex *iopex); static void finishortcut (Chnex *chnex, Devex *devex, Iopex *iopex); static void validate_shortcuts (Chnex *chnex); static void iodonex (Iopex *iopex, uLong status, void (*finentry) (void *finparam, uLong *status_r), void *finparam); static uLong assign_by_name (char const *devname, OZ_Lockmode lockmode, Iopex *iopex, OZ_Iochan **iochan_r); static uLong init_volume (int volnamelen, char const *volname, uLong clusterfactor, uLong secattrsize, const void *secattrbuff, uLong initflags, Iopex *iopex); static uLong write_init_header (Volume *volume, uByte *rootdirbuff, uLong filenum, char *name, uLong secattrsize, const void *secattrbuff, uLong filattrflags, OZ_Dbn efblk, OZ_Dbn count, OZ_Dbn start, OZ_Dbn index_header_start, Iopex *iopex); static void check_init_alloc (Iopex *iopex, OZ_Dbn cluster, uLong *clusterbuff, Volume *volume, OZ_Dbn count, OZ_Dbn start); static uLong mount_volume (Volume **volume_r, uLong mountflags, Iopex *iopex); static void calc_home_block (Volume *volume); static uLong dismount_volume (Volume *volume, int unload, int shutdown, Iopex *iopex); static uLong getdirid (Volume *volume, int fspeclen, char const *fspec, Fileid *dirid_r, int *fnamelen_r, char const **fname_r, char *dname_r, Iopex *iopex); static uLong lookup_file (File *dirfile, int namelen, char const *name, Fileid *fileid_r, char *name_r, Iopex *iopex); static uLong rename_file (File *olddirfile, char const *oldname, File *newdirfile, char const *newdirname, int newnamelen, char const *newname, int newversion, File *file, const Fileid *fileid, char *newname_r, Iopex *iopex); static uLong enter_file (File *dirfile, char const *dirname, int namelen, char const *name, int newversion, File *file, const Fileid *fileid, char *name_r, Iopex *iopex); static uLong remove_file (File *dirfile, char const *name, char *name_r, Iopex *iopex); static int dirisnotempty (File *dirfile, Iopex *iopex); static uLong create_file (Volume *volume, int namelen, char const *name, uLong filattrflags, OZ_Secattr *secattr, Fileid *dirid, File **file_r, Fileid **fileid_r, Iopex *iopex); static uLong getcresecattr (Iopex *iopex, uLong secattrsize, const void *secattrbuff, OZ_Secattr **secattr_r); static uLong set_file_attrs (File *file, uLong numitems, const OZ_Itmlst2 *itemlist, Iopex *iopex); static uLong extend_file (File *file, OZ_Dbn nblocks, uLong extflags, Iopex *iopex); static void write_dirty_homeboy (Volume *volume, Iopex *iopex); static uLong disk_fs_crash (void *dummy, OZ_Dbn vbn, uLong size, OZ_Mempage phypage, uLong offset); static uLong vdfs_knlpfmap (OZ_Iochan *iochan, OZ_Dbn vbn, OZ_Mempage *phypage_r); static uLong vdfs_knlpfupd (OZ_Iochan *iochan, OZ_Dbn vbn, OZ_Mempage phypage); static void vdfs_knlpfrel (OZ_Iochan *iochan, OZ_Mempage phypage); static uLong diskio (uLong funcode, uLong as, void *ap, Iopex *iopex, OZ_Iochan *iochan); static uLong revalidate (void *devexv, OZ_Dcache *dcache); static void revaltimer (void *kthreadv, OZ_Timer *timer); static uLong map_vbn_to_lbn (File *file, OZ_Dbn virtblock, OZ_Dbn *nblocks_r, OZ_Dbn *logblock_r); static void printe (Iopex *iopex, char const *format, ...); static uLong startprintk (void *iochanv, uLong *size, char **buff); /************************************************************************/ /* */ /* Boot-time initialization routine */ /* */ /************************************************************************/ void oz_dev_vdfs_init (int version, char const *drivername, const OZ_VDFS_Vector *vector) { Devex *devex; OZ_Devclass *devclass; OZ_Devdriver *devdriver; OZ_Devunit *devunit; if (version != OZ_VDFS_VERSION) { oz_knl_printk ("oz_dev_vdfs_init: caller '%s' is version %d, but I am version %d\n", drivername, version, OZ_VDFS_VERSION); return; } if (vector -> fileid_size > sizeof (Fileid)) { oz_knl_printk ("oz_dev_vdfs_init: caller '%s' fileid size %d, max allowed %d\n", drivername, vector -> fileid_size, sizeof (Fileid)); return; } /* Set up template device data structures */ devunit = oz_knl_devunit_lookup (drivername); if (devunit != NULL) oz_knl_devunit_increfc (devunit, -1); else { oz_knl_printk ("oz_dev_vdfs_init (%s)\n", drivername); devclass = oz_knl_devclass_create (OZ_IO_FS_CLASSNAME, OZ_IO_FS_BASE, OZ_IO_FS_MASK, drivername); devdriver = oz_knl_devdriver_create (devclass, drivername); devunit = oz_knl_devunit_create (devdriver, drivername, "init and mount template", &oz_disk_fs_functable, 0, oz_s_secattr_tempdev); if (devunit != NULL) { devex = oz_knl_devunit_ex (devunit); memset (devex, 0, sizeof *devex); devex -> devdriver = devdriver; devex -> drivername = drivername; devex -> vector = vector; } } } /************************************************************************/ /* */ /* An I/O channel is being assigned and the devio routines want to */ /* know if this device is to be cloned. */ /* */ /* In this driver, we only clone the original template device. */ /* */ /* Input: */ /* */ /* template_devunit = pointer to existing device unit */ /* template_devex = device extension data */ /* template_cloned = template's cloned flag */ /* procmode = processor mode doing the assign */ /* */ /* Output: */ /* */ /* oz_disk_fs_clonecre = OZ_SUCCESS : ok to assign channel */ /* else : error status */ /* **cloned_devunit = cloned device unit */ /* */ /************************************************************************/ static uLong oz_disk_fs_clonecre (OZ_Devunit *template_devunit, void *template_devexv, int template_cloned, OZ_Procmode procmode, OZ_Devunit **cloned_devunit) { char *unitname; Devex *devex, *template_devex; int i; OZ_Event *ioevent, *shortcutev; OZ_Secattr *secattr; uLong sts; /* If this is an already cloned devunit, don't clone anymore, just use the original device */ if (template_cloned) { *cloned_devunit = template_devunit; oz_knl_devunit_increfc (template_devunit, 1); } /* This is the original template device, clone a unit. The next thing the caller should do is an OZ_IO_FS_INITVOL or OZ_IO_FS_MOUNTVOL call. */ else { sts = oz_knl_event_create (14, "shortcut close", NULL, &shortcutev); if (sts != OZ_SUCCESS) return (sts); sts = oz_knl_event_create (8, "disk i/o", NULL, &ioevent); if (sts != OZ_SUCCESS) { oz_knl_event_increfc (shortcutev, -1); return (sts); } template_devex = template_devexv; i = strlen (template_devex -> drivername); unitname = OZ_KNL_NPPMALLOC (i + 12); memcpy (unitname, template_devex -> drivername, i); unitname[i++] = '_'; oz_hw_itoa (oz_hw_atomic_inc_long (&(template_devex -> clonumber), 1), 11, unitname + i); secattr = oz_knl_thread_getdefcresecattr (NULL); *cloned_devunit = oz_knl_devunit_create (template_devex -> devdriver, unitname, "Not yet mounted", &oz_disk_fs_functable, 1, secattr); OZ_KNL_NPPFREE (unitname); oz_knl_secattr_increfc (secattr, -1); devex = oz_knl_devunit_ex (*cloned_devunit); memset (devex, 0, sizeof *devex); oz_hw_smplock_init (sizeof devex -> smplock_vl, &(devex -> smplock_vl), OZ_SMPLOCK_LEVEL_VL); devex -> devdriver = template_devex -> devdriver; devex -> drivername = template_devex -> drivername; devex -> vector = template_devex -> vector; devex -> devunit = *cloned_devunit; devex -> ioevent = ioevent; devex -> shortcutev = shortcutev; devex -> iopexqt = &(devex -> iopexqh); } return (OZ_SUCCESS); } /************************************************************************/ /* */ /* The last channel was deassigned from a devunit. This routine is */ /* called to see if the unit should be deleted. */ /* */ /* In this driver, we only delete devices that are not mounted. We */ /* also never delete the template device (duh!). */ /* */ /* Input: */ /* */ /* cloned_devunit = cloned device's devunit struct */ /* devex = the devex of cloned_devunit */ /* cloned = the cloned_devunit's cloned flag */ /* */ /* Output: */ /* */ /* oz_disk_fs_clonedel = 0 : keep device in device table */ /* 1 : delete device from table */ /* */ /************************************************************************/ static int oz_disk_fs_clonedel (OZ_Devunit *cloned_devunit, void *devexv, int cloned) { Devex *devex; int ok_to_del; devex = devexv; ok_to_del = (cloned && (devex -> volume == NULL) && (devex -> event == NULL) && (devex -> iopexqh == NULL)); if (ok_to_del) { if (devex -> shortcutev != NULL) { oz_knl_event_increfc (devex -> shortcutev, -1); devex -> shortcutev = NULL; } if (devex -> ioevent != NULL) { oz_knl_event_increfc (devex -> ioevent, -1); devex -> ioevent = NULL; } } return (ok_to_del); } /************************************************************************/ /* */ /* An I/O channel was just assigned to the unit */ /* */ /* Clear out the channel extension block */ /* */ /************************************************************************/ static uLong oz_disk_fs_assign (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Procmode procmode) { Chnex *chnex; chnex = chnexv; memset (chnex, 0, sizeof *chnex); chnex -> iochan = iochan; return (OZ_SUCCESS); } /************************************************************************/ /* */ /* An I/O channel was just deassigned from a unit */ /* */ /* Input: */ /* */ /* devunit = device unit that is being deassigned from */ /* devexv = corresponding devex pointer */ /* iochan = i/o channel being deassigned */ /* chnexv = corresponding chnex pointer */ /* */ /************************************************************************/ static void dummydone (void *paramv, uLong status); static int oz_disk_fs_deassign (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv) { Chnex *chnex; uLong sts; chnex = chnexv; /* If a file or wildcard scan is open or shortcuts busy, close it */ if ((chnex -> file != NULL) || (chnex -> wildscan != NULL) || (chnex -> shortcuts > 0)) { chnex -> ignclose = 0; /* don't ignore closes anymore */ sts = oz_knl_iostart2 (1, iochan, OZ_PROCMODE_KNL, dummydone, NULL, NULL, NULL, NULL, NULL, OZ_IO_FS_CLOSE, 0, NULL); /* close */ if (sts == OZ_STARTED) return (1); /* if it completes asynchronously, come back when it calls oz_knl_iodone */ if (sts != OZ_SYSHUTDOWN) { if (chnex -> file != NULL) oz_crash ("oz_dev_vdfs deassign: file left open did not close, status %u", sts); /* make sure it closed */ if (chnex -> wildscan != NULL) oz_crash ("oz_dev_vdfs deassign: wild left open did not close, status %u", sts); /* make sure it closed */ if (chnex -> shortcuts > 0) oz_crash ("oz_dev_vdfs deassign: shortcuts did not close, status %u", sts); /* make sure it closed */ } } /* The scassochnex should be NULL. If not it means the associated shortcut routine is still running, */ /* as the shortcut routine that set this link should also clear it before releasing the channel. */ if (chnex -> scassochnex != NULL) oz_crash ("oz_dev_vdfs deassign: chnex %p -> scassochnex %p", chnex, chnex -> scassochnex); /* Free off the wildscan lowipl struct, if any present */ if (chnex -> wild_lowipl != NULL) { oz_knl_lowipl_free (chnex -> wild_lowipl); chnex -> wild_lowipl = NULL; } return (0); } /* Dummy I/O done routine needed so iostart will know we want async completion */ static void dummydone (void *paramv, uLong status) {} /************************************************************************/ /* */ /* Abort I/O request in progress */ /* */ /************************************************************************/ static void oz_disk_fs_abort (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Ioop *ioop, void *iopexv, OZ_Procmode procmode) { Chnex *chnex; Devex *devex; File *file; Iopex *iopex, **liopex, temp_iopex, *xiopex; uLong vl; devex = devexv; chnex = chnexv; xiopex = NULL; /* Remove any matching requests from kt_mountvol thread queue */ vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); // lock iopexqh/iopexqt queue for (liopex = &(devex -> iopexqh); (iopex = *liopex) != NULL;) { // scan the queue if (oz_knl_ioabortok (iopex -> ioop, iochan, procmode, ioop)) { // see if this is something we should abort *liopex = iopex -> next; // if so, unlink from queue if (devex -> iopexqh == NULL) devex -> iopexqt = &(devex -> iopexqh); iopex -> next = xiopex; xiopex = iopex; } else { liopex = &(iopex -> next); } } devex -> iopexqt = liopex; // set up possibly new tail pointer /* If wildcard scan in progress, tell it is aborted */ iopex = chnex -> wild_iopex; if ((iopex != NULL) && oz_knl_ioabortok (iopex -> ioop, iochan, procmode, ioop)) iopex -> aborted = 1; oz_hw_smplock_clr (&(devex -> smplock_vl), vl); // queue is clean, release lock /* Remove any matching requests from the corresponding file's recio request queue */ memset (&temp_iopex, 0xF0, sizeof temp_iopex); temp_iopex.shortcut_prev = NULL; if (startshortcut (chnex, devex, &temp_iopex) == OZ_SUCCESS) { // prevent file from closing on us file = chnex -> file; // see if any file is open on channel if (file != NULL) { vl = oz_hw_smplock_wait (&(file -> recio_vl)); // ok, lock its recio request queue for (liopex = &(file -> recio_qh); (iopex = *liopex) != NULL;) { // scan the queue if (oz_knl_ioabortok (iopex -> ioop, iochan, procmode, ioop)) { // see if it should be aborted *liopex = iopex -> next; // if so, unlink it if (devex -> iopexqh == NULL) devex -> iopexqt = &(devex -> iopexqh); iopex -> next = xiopex; // link to abort list xiopex = iopex; } else { liopex = &(iopex -> next); // if not, skip over it } } file -> recio_qt = liopex; // fix queue tail pointer oz_hw_smplock_clr (&(file -> recio_vl), vl); // unlock queue } finishortcut (chnex, devex, &temp_iopex); // let file be closed } /* Now that we're back at softint level, abort all the requests we found */ while ((iopex = xiopex) != NULL) { xiopex = iopex -> next; iodonex (iopex, OZ_ABORTED, NULL, NULL); } } typedef struct { uLong size; OZ_Dbn svbn; OZ_Handle hout; } Ixdeb; #define IXDEB_FC 0x99 void oz_ixdeb_user (OZ_Handle h_iochan, uLong size, OZ_Dbn svbn, OZ_Handle h_output) { Ixdeb ixdeb; uLong sts; oz_sys_io_fs_printf (h_output, "oz_ixdeb_user (%X, %u, %u)\n", h_iochan, size, svbn); ixdeb.size = size; ixdeb.svbn = svbn; ixdeb.hout = h_output; sts = oz_sys_io (OZ_PROCMODE_KNL, h_iochan, 0, IXDEB_FC, sizeof ixdeb, &ixdeb); oz_sys_io_fs_printf (h_output, "oz_ixdeb_user status %u\n", sts); } /************************************************************************/ /* */ /* This routine is called as a result of an oz_knl_iostart call to */ /* start performing an i/o request */ /* */ /************************************************************************/ static uLong oz_disk_fs_start (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Procmode procmode, OZ_Ioop *ioop, void *iopexv, uLong funcode, uLong as, void *ap) { char threadname[OZ_THREAD_NAMESIZE]; Chnex *chnex; Devex *devex; Iopex *iopex; OZ_Thread *thread; uLong sts, vl; chnex = chnexv; devex = devexv; iopex = iopexv; /* Maybe device is being shut down */ if (devex -> shutdown) return (OZ_SYSHUTDOWN); /* Set up stuff in iopex that is common to just about all functions */ iopex -> ioop = ioop; iopex -> funcode = funcode; iopex -> chnex = chnex; iopex -> devex = devex; iopex -> procmode = procmode; iopex -> as = as; iopex -> ap = ap; iopex -> aborted = 0; #if 111 iopex -> shortcut_next = iopex; // it's not linked to chnex -> shortcut_iopexs yet iopex -> shortcut_prev = NULL; #endif movc4 (as, ap, sizeof iopex -> u, &(iopex -> u)); sts = OZ_SUCCESS; switch (funcode) { /* Initialize volume - start a kernel thread - the thread stays around just long enough to init the volume */ case OZ_IO_FS_INITVOL: { sts = oz_knl_ioop_lockz (iopex -> ioop, -1, iopex -> u.initvol.p.devname, NULL, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockz (iopex -> ioop, -1, iopex -> u.initvol.p.volname, NULL, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockr (iopex -> ioop, iopex -> u.initvol.p.secattrsize, iopex -> u.initvol.p.secattrbuff, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); oz_sys_sprintf (sizeof threadname, threadname, "disk_fs init %s", iopex -> u.initvol.p.devname); sts = oz_knl_thread_create (oz_s_systemproc, THREAD_PRIORITY, NULL, NULL, NULL, 0, kt_initvol, iopex, OZ_ASTMODE_INHIBIT, sizeof threadname, threadname, NULL, &thread); if (sts == OZ_SUCCESS) { oz_knl_thread_orphan (thread); oz_knl_thread_increfc (thread, -1); sts = OZ_STARTED; } return (sts); } /* Mount volume - start a kernel thread - the thread stays around until the volume is dismounted */ case OZ_IO_FS_MOUNTVOL: { sts = oz_knl_ioop_lockz (iopex -> ioop, -1, iopex -> u.mountvol.p.devname, NULL, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); oz_sys_sprintf (sizeof threadname, threadname, "disk_fs %s", iopex -> u.mountvol.p.devname); sts = oz_knl_thread_create (oz_s_systemproc, THREAD_PRIORITY, NULL, NULL, NULL, 0, kt_mountvol, iopex, OZ_ASTMODE_INHIBIT, sizeof threadname, threadname, NULL, &thread); if (sts == OZ_SUCCESS) { oz_knl_thread_orphan (thread); oz_knl_thread_increfc (thread, -1); sts = OZ_STARTED; } return (sts); } /* These requests are primarily processed in the calling thread's context. This */ /* is ok because they do not interact with anything else in the filesystem, */ /* other than to require that a file have already been opened on the channel. */ case OZ_IO_FS_WRITEBLOCKS: { sts = oz_knl_ioop_lockr (iopex -> ioop, iopex -> u.writeblocks.p.size, iopex -> u.writeblocks.p.buff, &(iopex -> u.writeblocks.phypages), NULL, &(iopex -> u.writeblocks.phyoffs)); if (sts != OZ_SUCCESS) oz_knl_printk ("oz_dev_vdfs io_start*: writeblocks sts %u, size %X, buff %X\n", sts, iopex -> u.writeblocks.p.size, iopex -> u.writeblocks.p.buff); if (sts == OZ_SUCCESS) sts = sc_writeblocks (iopex, chnex, devex); return (sts); } case OZ_IO_FS_READBLOCKS: { sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.readblocks.p.size, iopex -> u.readblocks.p.buff, &(iopex -> u.readblocks.phypages), NULL, &(iopex -> u.readblocks.phyoffs)); if (sts == OZ_SUCCESS) sts = sc_readblocks (iopex, chnex, devex); return (sts); } case IXDEB_FC: { sts = sc_ixdeb (iopex, chnex, devex); return (sts); } case OZ_IO_FS_PAGEWRITE: { const OZ_Mempage *pagearray; OZ_Dbn blockcount, virtblock; if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); virtblock = iopex -> u.pagewrite.p.startblock; blockcount = iopex -> u.pagewrite.p.blockcount; pagearray = iopex -> u.pagewrite.p.pagearray; memset (&(iopex -> u.writeblocks.p), 0, sizeof iopex -> u.writeblocks.p); iopex -> u.writeblocks.p.size = blockcount * devex -> blocksize; iopex -> u.writeblocks.p.svbn = virtblock; iopex -> u.writeblocks.phypages = pagearray; sts = sc_writeblocks (iopex, chnex, devex); return (sts); } case OZ_IO_FS_PAGEREAD: { const OZ_Mempage *pagearray; OZ_Dbn blockcount, virtblock; if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); virtblock = iopex -> u.pageread.p.startblock; blockcount = iopex -> u.pageread.p.blockcount; pagearray = iopex -> u.pageread.p.pagearray; memset (&(iopex -> u.readblocks.p), 0, sizeof iopex -> u.readblocks.p); iopex -> u.readblocks.p.size = blockcount * devex -> blocksize; iopex -> u.readblocks.p.svbn = virtblock; iopex -> u.readblocks.phypages = pagearray; sts = sc_readblocks (iopex, chnex, devex); return (sts); } /* These requests are processed primarily as 'lowipl' routines not in any particular thread's context. */ /* There is one of these active per file at a time because they share the end-of-file pointer. */ case OZ_IO_FS_WRITEREC: { // Lock the data buffer in memory sts = oz_knl_ioop_lockr (iopex -> ioop, iopex -> u.writerec.p.size, iopex -> u.writerec.p.buff, &(iopex -> u.writerec.phypages), NULL, &(iopex -> u.writerec.byteoffs)); // Lock return length in memory and clear it if ((sts == OZ_SUCCESS) && (iopex -> u.writerec.p.wlen != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.writerec.p.wlen), iopex -> u.writerec.p.wlen, &(iopex -> u.writerec.wlen_phypages), NULL, &(iopex -> u.writerec.wlen_byteoffs)); if (sts == OZ_SUCCESS) *(iopex -> u.writerec.p.wlen) = 0; } // Lock the terminator buffer in memory (or copy small ones to trmdata) if (sts == OZ_SUCCESS) { if (iopex -> u.writerec.p.trmsize > sizeof iopex -> u.writerec.trmdata) { sts = oz_knl_ioop_lockr (iopex -> ioop, iopex -> u.writerec.p.trmsize, iopex -> u.writerec.p.trmbuff, &(iopex -> u.writerec.trmphypages), NULL, &(iopex -> u.writerec.trmbyteoffs)); } else { sts = oz_knl_section_uget (procmode, iopex -> u.writerec.p.trmsize, iopex -> u.writerec.p.trmbuff, iopex -> u.writerec.trmdata); } } // Queue the request for processing if (sts == OZ_SUCCESS) { iopex -> backend = sc_writerec; sts = queuerecio (iopex, chnex, devex); } return (sts); } case OZ_IO_FS_READREC: { // Lock the data buffer in memory sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.readrec.p.size, iopex -> u.readrec.p.buff, &(iopex -> u.readrec.phypages), NULL, &(iopex -> u.readrec.byteoffs)); // Lock return length in memory and clear it if ((sts == OZ_SUCCESS) && (iopex -> u.readrec.p.rlen != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.readrec.p.rlen), iopex -> u.readrec.p.rlen, &(iopex -> u.readrec.rlen_phypages), NULL, &(iopex -> u.readrec.rlen_byteoffs)); if (sts == OZ_SUCCESS) *(iopex -> u.readrec.p.rlen) = 0; } // Copy the terminator buffer in to temp buffer iopex -> u.readrec.trmbuff = NULL; if ((sts == OZ_SUCCESS) && (iopex -> u.readrec.p.trmsize != 0)) { iopex -> u.readrec.trmbuff = iopex -> u.readrec.trmdata; if (iopex -> u.readrec.p.trmsize > sizeof iopex -> u.readrec.trmdata) { iopex -> u.readrec.trmbuff = OZ_KNL_PGPMALLOQ (iopex -> u.readrec.p.trmsize); if (iopex -> u.readrec.trmbuff != NULL) return (OZ_EXQUOTAPGP); } sts = oz_knl_section_uget (procmode, iopex -> u.readrec.p.trmsize, iopex -> u.readrec.p.trmbuff, iopex -> u.readrec.trmbuff); if ((sts != OZ_SUCCESS) && (iopex -> u.readrec.p.trmsize > sizeof iopex -> u.readrec.trmdata)) OZ_KNL_PGPFREE (iopex -> u.readrec.trmbuff); } // Queue the request for processing if (sts == OZ_SUCCESS) { iopex -> backend = sc_readrec; sts = queuerecio (iopex, chnex, devex); } return (sts); } /* All others get queued to the existing kernel thread that was created with a previous mount request. */ /* If there isn't such a thread, it means the volume is not mounted, so reject the request. */ /* First, though, we lock all the buffers in memory. Do this now */ /* just in case the pagefaulting requires I/O on this disk drive. */ case OZ_IO_FS_DISMOUNT: { iopex -> backend = be_dismount; break; } case OZ_IO_FS_VERIFYVOL: { iopex -> backend = be_verifyvol; break; } case OZ_IO_FS_CREATE: { sts = oz_knl_ioop_lockz (iopex -> ioop, -1, iopex -> u.create.p.name, NULL, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockr (iopex -> ioop, iopex -> u.create.p.secattrsize, iopex -> u.create.p.secattrbuff, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.create.p.rnamesize, iopex -> u.create.p.rnamebuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.create.p.rnamesubs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.create.p.rnamesubs), iopex -> u.create.p.rnamesubs, NULL, NULL, NULL); } if ((sts == OZ_SUCCESS) && (iopex -> u.create.p.fileidsize != 0)) { if (iopex -> u.create.p.fileidsize != devex -> vector -> fileid_size) return (OZ_BADBUFFERSIZE); sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.create.p.fileidsize, iopex -> u.create.p.fileidbuff, NULL, NULL, NULL); } iopex -> backend = be_create; break; } case OZ_IO_FS_OPEN: { if ((iopex -> u.open.p.fileidsize != 0) && (iopex -> u.open.p.fileidsize != devex -> vector -> fileid_size)) return (OZ_BADBUFFERSIZE); if (iopex -> u.open.p.name != NULL) { sts = oz_knl_ioop_lockz (iopex -> ioop, -1, iopex -> u.open.p.name, NULL, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.open.p.fileidsize != 0)) { sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.open.p.fileidsize, iopex -> u.open.p.fileidbuff, NULL, NULL, NULL); } } else if (iopex -> u.open.p.fileidsize == 0) return (OZ_MISSINGPARAM); else sts = oz_knl_ioop_lockr (iopex -> ioop, iopex -> u.open.p.fileidsize, iopex -> u.open.p.fileidbuff, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.open.p.rnamesize, iopex -> u.open.p.rnamebuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.open.p.rnamesubs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.open.p.rnamesubs), iopex -> u.open.p.rnamesubs, NULL, NULL, NULL); } iopex -> backend = be_open; break; } case OZ_IO_FS_CLOSE: { if (chnex -> ignclose) return (OZ_SUCCESS); sts = oz_knl_ioop_lockr (iopex -> ioop, iopex -> u.close.p.numitems * sizeof *(iopex -> u.close.p.itemlist), iopex -> u.close.p.itemlist, NULL, NULL, NULL); iopex -> backend = be_close; break; } case OZ_IO_FS_ENTER: { sts = oz_knl_ioop_lockz (iopex -> ioop, -1, iopex -> u.enter.p.name, NULL, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.enter.p.rnamesize, iopex -> u.enter.p.rnamebuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.enter.p.rnamesubs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.enter.p.rnamesubs), iopex -> u.enter.p.rnamesubs, NULL, NULL, NULL); } iopex -> backend = be_enter; break; } case OZ_IO_FS_REMOVE: { sts = oz_knl_ioop_lockz (iopex -> ioop, -1, iopex -> u.remove.p.name, NULL, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.remove.p.rnamesize, iopex -> u.remove.p.rnamebuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.remove.p.rnamesubs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.remove.p.rnamesubs), iopex -> u.remove.p.rnamesubs, NULL, NULL, NULL); } iopex -> backend = be_remove; break; } case OZ_IO_FS_RENAME: { sts = oz_knl_ioop_lockz (iopex -> ioop, -1, iopex -> u.rename.p.oldname, NULL, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockz (iopex -> ioop, -1, iopex -> u.rename.p.newname, NULL, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.rename.p.oldrnamesize, iopex -> u.rename.p.oldrnamebuff, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.rename.p.newrnamesize, iopex -> u.rename.p.newrnamebuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.rename.p.oldrnamesubs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.rename.p.oldrnamesubs), iopex -> u.rename.p.oldrnamesubs, NULL, NULL, NULL); } if ((sts == OZ_SUCCESS) && (iopex -> u.rename.p.newrnamesubs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.rename.p.newrnamesubs), iopex -> u.rename.p.newrnamesubs, NULL, NULL, NULL); } iopex -> backend = be_rename; break; } case OZ_IO_FS_EXTEND: { iopex -> backend = be_extend; break; } case OZ_IO_FS_GETINFO1: { sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> as, iopex -> ap, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.getinfo1.p.fileidsize, iopex -> u.getinfo1.p.fileidbuff, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = sc_getinfo1 (iopex, chnex, devex); return (sts); } case OZ_IO_FS_READDIR: { sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.readdir.p.filenamsize, iopex -> u.readdir.p.filenambuff, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.readdir.p.fileidsize, iopex -> u.readdir.p.fileidbuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.readdir.p.filenamsubs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.readdir.p.filenamsubs), iopex -> u.readdir.p.filenamsubs, NULL, NULL, NULL); } if (sts == OZ_SUCCESS) sts = sc_readdir (iopex, chnex, devex); return (sts); } case OZ_IO_FS_GETSECATTR: { sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.getsecattr.p.size, iopex -> u.getsecattr.p.buff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.getsecattr.p.rlen != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.getsecattr.p.rlen), iopex -> u.getsecattr.p.rlen, NULL, NULL, NULL); } iopex -> backend = be_getsecattr; break; } case OZ_IO_FS_WRITEBOOT: { iopex -> backend = be_writeboot; break; } case OZ_IO_FS_SETCURPOS: { iopex -> backend = be_setcurpos; break; } case OZ_IO_FS_WILDSCAN: { sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.wildscan.p.size, iopex -> u.wildscan.p.buff, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.wildscan.p.fileidsize, iopex -> u.wildscan.p.fileidbuff, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.wildscan.p.wildsize, iopex -> u.wildscan.p.wildbuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.wildscan.p.subs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.wildscan.p.subs), iopex -> u.wildscan.p.subs, NULL, NULL, NULL); } if ((sts == OZ_SUCCESS) && (iopex -> u.wildscan.p.wildsubs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.wildscan.p.wildsubs), iopex -> u.wildscan.p.wildsubs, NULL, NULL, NULL); } if (sts == OZ_SUCCESS) sts = sc_wildscan (iopex, chnex, devex); return (sts); } case OZ_IO_FS_GETINFO2: { sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.getinfo2.p.filnamsize, iopex -> u.getinfo2.p.filnambuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.getinfo2.p.filnamsubs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.getinfo2.p.filnamsubs), iopex -> u.getinfo2.p.filnamsubs, NULL, NULL, NULL); } iopex -> backend = be_getinfo2; break; } case OZ_IO_FS_GETINFO3: { sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> as, iopex -> ap, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.getinfo3.p.underbuff != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.getinfo3.p.undersize, iopex -> u.getinfo3.p.underbuff, NULL, NULL, NULL); } iopex -> backend = be_getinfo3; break; } case OZ_IO_FS_SHUTDOWN: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); /* can only be called from kernel mode */ devex -> shutdown = 1; /* don't ever let any more requests queue */ iopex -> backend = be_shutdown; /* queue to mount thread */ break; } case OZ_IO_FS_CRASH: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); iopex -> backend = be_crash; break; } case OZ_IO_FS_PARSE: { sts = oz_knl_ioop_lockz (iopex -> ioop, -1, iopex -> u.parse.p.name, NULL, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (iopex -> ioop, iopex -> u.parse.p.rnamesize, iopex -> u.parse.p.rnamebuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.parse.p.rnamesubs != NULL)) { sts = oz_knl_ioop_lockw (iopex -> ioop, sizeof *(iopex -> u.parse.p.rnamesubs), iopex -> u.parse.p.rnamesubs, NULL, NULL, NULL); } iopex -> backend = be_parse; break; } /* Don't know what the function is */ default: { sts = OZ_BADIOFUNC; } } /* Now that everything checks out, queue it to the thread */ if (sts == OZ_SUCCESS) { vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); /* lock the volume data */ if (devex -> event == NULL) sts = OZ_NOTMOUNTED; /* error status if no thread (ie, it's not mounted) */ else if ((chnex -> shortcuts < 0) && (iopex -> backend != be_close)) sts = OZ_CLOSEINPROG; /* maybe the channel is being closed, ... */ /* don't let anyone (especially shortcuts) queue anything */ /* but allow redundant closes in case of deassign routine */ else if ((chnex -> scassochnex != NULL) && (chnex -> scassochnex -> shortcuts < 0) && (iopex -> backend != be_close)) sts = OZ_CLOSEINPROG; /* also don't allow shortcut routines to queue new */ /* requests on any channel they are using for I/O */ else { iopex -> next = NULL; /* it's mounted (or is being mounted), queue iopex to thread */ *(devex -> iopexqt) = iopex; devex -> iopexqt = &(iopex -> next); if (devex -> iopexqh == iopex) { /* if queue was empty, wake the thread */ oz_knl_event_set (devex -> event, 1); } sts = OZ_STARTED; /* request will complete asynchronously */ } oz_hw_smplock_clr (&(devex -> smplock_vl), vl); /* unlock database */ } return (sts); } /************************************************************************/ /* */ /* Initialize volume */ /* */ /************************************************************************/ static uLong kt_initvol (void *iopexv) { Chnex *chnex; Devex *devex; int si, volnamelen; Iopex *iopex, *iopexx; OZ_Iochan *iochan; OZ_Secattr *secattr; uLong sts, vl; iopex = iopexv; chnex = iopex -> chnex; devex = iopex -> devex; secattr = NULL; si = oz_hw_cpu_setsoftint (0); /* Attach to requestor's process to address its parameters */ oz_knl_process_setcur (oz_knl_ioop_getprocess (iopex -> ioop)); /* Assign an I/O channel to the disk drive - it will be deassigned when the deassign routine is called */ /* Allow others to read but no other writing, allow myself to write */ /* If it is already mounted, this will return an access conflict status */ sts = assign_by_name (iopex -> u.initvol.p.devname, OZ_LOCKMODE_PW, iopex, &iochan); if (sts != OZ_SUCCESS) goto rtn; if (!oz_hw_atomic_setif_ptr (&(devex -> master_iochan), iochan, NULL)) { oz_knl_iochan_increfc (iochan, -1); sts = OZ_ALREADYMOUNTED; goto rtn; } oz_knl_event_rename (devex -> ioevent, OZ_EVENT_NAMESIZE, oz_knl_devunit_devname (oz_knl_iochan_getdevunit (iochan))); /* Parse the given secattrs */ sts = getcresecattr (iopex, iopex -> u.initvol.p.secattrsize, iopex -> u.initvol.p.secattrbuff, &secattr); if (sts != OZ_SUCCESS) goto rtn; sts = oz_knl_secattr_fromname (strlen (iopex -> u.initvol.p.volname) + 1, iopex -> u.initvol.p.volname, &volnamelen, NULL, &secattr); if (sts != OZ_SUCCESS) goto rtn; /* Write the disk */ sts = init_volume (volnamelen, iopex -> u.initvol.p.volname, iopex -> u.initvol.p.clusterfactor, oz_knl_secattr_getsize (secattr), oz_knl_secattr_getbuff (secattr), iopex -> u.initvol.p.initflags, iopex); /* Reject any i/o requests that were queued while writing disk */ vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); while ((iopexx = devex -> iopexqh) != NULL) { devex -> iopexqh = iopexx -> next; if (devex -> iopexqh == NULL) devex -> iopexqt = &(devex -> iopexqh); oz_hw_smplock_clr (&(devex -> smplock_vl), vl); iodonex (iopexx, OZ_NOTMOUNTED, NULL, NULL); vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); } /* Close the I/O channel to the disk drive */ oz_knl_iochan_increfc (devex -> master_iochan, -1); devex -> master_iochan = NULL; oz_hw_smplock_clr (&(devex -> smplock_vl), vl); rtn: oz_knl_secattr_increfc (secattr, -1); iodonex (iopex, sts, NULL, NULL); oz_knl_process_setcur (oz_s_systemproc); oz_hw_cpu_setsoftint (si); return (OZ_SUCCESS); } /************************************************************************/ /* */ /* Mount volume */ /* */ /************************************************************************/ static uLong kt_mountvol (void *iopexv) { char unitdesc[OZ_DEVUNIT_DESCSIZE], unitname[OZ_DEVUNIT_NAMESIZE]; Chnex *chnex; Devex *devex; int si; Iopex *iopex, *iopexx; OZ_Devunit *devunit; OZ_Event *event; OZ_Iochan *iochan; OZ_Lockmode lockmode; OZ_Secattr *secattr; uLong sts, vl; iopex = iopexv; chnex = iopex -> chnex; devex = iopex -> devex; si = oz_hw_cpu_setsoftint (0); /* Attach to requestor's process to address its parameters */ oz_knl_process_setcur (oz_knl_ioop_getprocess (iopex -> ioop)); /* Assign an I/O channel to the disk drive - */ /* Allow others to read disk but no-one else can write */ /* If already mounted, this will return access confilct status */ lockmode = OZ_LOCKMODE_PW; if (iopex -> u.mountvol.p.mountflags & OZ_FS_MOUNTFLAG_READONLY) lockmode = OZ_LOCKMODE_PR; sts = assign_by_name (iopex -> u.mountvol.p.devname, lockmode, iopex, &iochan); if (sts != OZ_SUCCESS) goto done; if (!oz_hw_atomic_setif_ptr (&(devex -> master_iochan), iochan, NULL)) { oz_knl_iochan_increfc (iochan, -1); sts = OZ_ALREADYMOUNTED; goto done; } oz_knl_event_rename (devex -> ioevent, OZ_EVENT_NAMESIZE, oz_knl_devunit_devname (oz_knl_iochan_getdevunit (iochan))); /* Declare shutdown handler so it will get dismounted on shutdown */ devex -> shuthand = oz_knl_shuthand_create (shuthand, devex); /* Open the sacred files */ sts = mount_volume (&(devex -> volume), iopex -> u.mountvol.p.mountflags, iopex); /* open the sacred files and set up the volume struct */ if (sts != OZ_SUCCESS) goto done; /* abort mount request if any error */ /* Mount successful, rename the unit to something predictable */ /* Unit name is . */ /* Unit description is Volume */ devunit = oz_knl_iochan_getdevunit (chnex -> iochan); oz_sys_sprintf (sizeof unitname, unitname, "%s.%s", oz_knl_devunit_devname (oz_knl_iochan_getdevunit (devex -> master_iochan)), devex -> drivername); oz_sys_sprintf (sizeof unitdesc, unitdesc, "Volume %s", (*(devex -> vector -> get_volname)) (devex -> volume)); oz_knl_devunit_rename (devunit, unitname, unitdesc); /* If the caller supplied any security attributes, apply them to the device now */ if (iopex -> u.mountvol.p.secattrsize != 0) { sts = oz_knl_secattr_create (iopex -> u.mountvol.p.secattrsize, iopex -> u.mountvol.p.secattrbuff, NULL, &secattr); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "error %u creating mount security attributes\n", sts); dismount_volume (devex -> volume, 0, 0, iopex); devex -> volume = NULL; goto done; } oz_knl_devunit_setsecattr (devunit, secattr); oz_knl_secattr_increfc (secattr, -1); } /* Process I/O requests for the volume. Eventually we get a dismount request which clears devex -> volume. */ sts = oz_knl_event_create (sizeof unitname, unitname, NULL, &(devex -> event)); /* create event flag */ if (sts != OZ_SUCCESS) oz_crash ("oz_dev_ip kt_mountvol: error %u creating event flag", sts); /* so requests will queue */ while (devex -> volume != NULL) { /* keep looping while volume is mounted */ oz_dev_vdfs_write_dirty_headers (devex -> volume, iopex); /* write dirty headers to disk */ write_dirty_homeboy (devex -> volume, iopex); /* write dirty homeblock to disk */ iodonex (iopex, sts, NULL, NULL); /* post the last request for completion */ oz_knl_process_setcur (oz_s_systemproc); /* detach from reqeusting process address space */ dequeue: oz_knl_event_set (devex -> event, 0); /* clear event flag in case queue is empty */ vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); /* lock the i/o request queue */ iopex = devex -> iopexqh; /* see if there is anything in the queue */ if (iopex == NULL) { /* if the queue is empty, */ oz_hw_smplock_clr (&(devex -> smplock_vl), vl); /* release the lock */ oz_knl_event_waitone (devex -> event); /* wait for something in the queue */ goto dequeue; /* go back an check queue again */ } devex -> iopexqh = iopex -> next; /* queue not empty, unlink the request */ if (devex -> iopexqh == NULL) devex -> iopexqt = &(devex -> iopexqh); oz_hw_smplock_clr (&(devex -> smplock_vl), vl); /* release the lock */ oz_knl_process_setcur (oz_knl_ioop_getprocess (iopex -> ioop)); /* attach to requestor process to address parameters */ sts = (*(iopex -> backend)) (iopex, iopex -> chnex, devex); /* call the backend (be_...) routine */ } /* Clear any shutdown handler we had set up */ done: if (devex -> shuthand != NULL) { oz_knl_shuthand_delete (devex -> shuthand); devex -> shuthand = NULL; } /* Don't let any new requeusts queue */ event = devex -> event; devex -> event = NULL; if (event != NULL) oz_knl_event_increfc (event, 1); /* Reject any I/O requests that were queued while mounting or dismounting disk */ reject: vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); if ((iopexx = devex -> iopexqh) != NULL) { devex -> iopexqh = iopexx -> next; if (devex -> iopexqh == NULL) devex -> iopexqt = &(devex -> iopexqh); oz_hw_smplock_clr (&(devex -> smplock_vl), vl); iodonex (iopexx, OZ_NOTMOUNTED, NULL, NULL); goto reject; } oz_hw_smplock_clr (&(devex -> smplock_vl), vl); /* Complete the mount/dismount request */ rtn: if (devex -> master_iochan != NULL) { oz_knl_iochan_increfc (devex -> master_iochan, -1); devex -> master_iochan = NULL; } iodonex (iopex, sts, NULL, NULL); oz_knl_process_setcur (oz_s_systemproc); oz_hw_cpu_setsoftint (si); return (OZ_SUCCESS); } /************************************************************************/ /* */ /* The system is being shut down - terminate all activity now */ /* */ /* Input: */ /* */ /* devex = device to shut down */ /* smplevel = softint */ /* */ /************************************************************************/ static void shuthand (void *devexv) { Devex *devex; uLong sts; OZ_Iochan *iochan; devex = devexv; /* The shutdown handler is no longer queued */ devex -> shuthand = NULL; /* Use an I/O function code so it gets synchronized with the thread */ sts = oz_knl_iochan_create (devex -> devunit, OZ_LOCKMODE_NL, OZ_PROCMODE_KNL, NULL, &iochan); if (sts != OZ_SUCCESS) oz_crash ("oz_disk_fs_shutdown: error %u assigning I/O channel", sts); sts = oz_knl_io (iochan, OZ_IO_FS_SHUTDOWN, 0, NULL); if ((sts != OZ_SUCCESS) && (sts != OZ_NOTMOUNTED)) { oz_crash ("oz_disk_fs_shutdown: error %u shutting down", sts); } oz_knl_iochan_increfc (iochan, -1); } /************************************************************************/ /* */ /* System is being shutdown - close all files and dismount volume */ /* */ /************************************************************************/ static uLong be_shutdown (Iopex *iopex, Chnex *chnex, Devex *devex) { uLong sts; oz_knl_printk ("oz_dev_vdfs: shutting down %s\n", oz_knl_devunit_devname (devex -> devunit)); sts = dismount_volume (devex -> volume, 0, 1, iopex); if (sts == OZ_SUCCESS) devex -> volume = NULL; return (sts); } /************************************************************************/ /* */ /* Dismount volume */ /* */ /************************************************************************/ static uLong be_dismount (Iopex *iopex, Chnex *chnex, Devex *devex) { uLong sts; sts = dismount_volume (devex -> volume, iopex -> u.dismount.p.unload, 0, iopex); if (sts == OZ_SUCCESS) devex -> volume = NULL; return (sts); } /************************************************************************/ /* */ /* Verify volume */ /* */ /************************************************************************/ static uLong be_verifyvol (Iopex *iopex, Chnex *chnex, Devex *devex) { return ((*(devex -> vector -> verifyvol)) (iopex, devex)); } /************************************************************************/ /* */ /* Create file */ /* */ /************************************************************************/ static uLong be_create (Iopex *iopex, Chnex *chnex, Devex *devex) { char rname[2*OZ_FS_MAXFNLEN]; char const *fname; File *dirfile, *file; Fileid dirid, *fileid; int fnamelen, rnamelen; OZ_Lockmode lockmode; OZ_Secattr *secattr; uLong sts; dirfile = NULL; file = NULL; secattr = NULL; if (chnex -> file != NULL) sts = OZ_FILEALREADYOPEN; /* error if something already open on channel */ else sts = getcresecattr (iopex, iopex -> u.create.p.secattrsize, iopex -> u.create.p.secattrbuff, &secattr); /* get secattrs to create it with */ if (sts == OZ_SUCCESS) sts = oz_knl_secattr_fromname (strlen (iopex -> u.create.p.name) + 1, iopex -> u.create.p.name, &fnamelen, NULL, &secattr); /* maybe there are secattrs in the name itself */ if (sts == OZ_SUCCESS) sts = getdirid (devex -> volume, fnamelen, iopex -> u.create.p.name, &dirid, &fnamelen, &fname, rname, iopex); /* get directory id the file is in */ if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_open_file (devex -> volume, &dirid, OZ_SECACCMSK_WRITE, &dirfile, iopex); /* open the directory (must be able to write it) */ if (sts == OZ_SUCCESS) { sts = create_file (devex -> volume, fnamelen, fname, iopex -> u.create.p.filattrflags, secattr, &dirid, &file, &fileid, iopex); /* create */ if (sts != OZ_SUCCESS) oz_dev_vdfs_printk (iopex, "oz_dev_vdfs be_create: error %u creating file\n", sts); } if (sts == OZ_SUCCESS) { rnamelen = strlen (rname); sts = enter_file (dirfile, rname, fnamelen, fname, iopex -> u.create.p.newversion, file, fileid, rname + rnamelen, iopex); /* enter in directory */ if ((sts != OZ_SUCCESS) && (sts != OZ_FILEALREADYEXISTS)) { rname[rnamelen] = 0; oz_dev_vdfs_printk (iopex, "oz_dev_vdfs be_create: error %u entering file %*.*S in %s\n", sts, fnamelen, fnamelen, fname, rname); } } if (dirfile != NULL) oz_dev_vdfs_close_file (dirfile, iopex); /* close the directory */ if (sts == OZ_SUCCESS) { lockmode = iopex -> u.create.p.lockmode; /* get channel lock mode */ chnex -> file = file; /* set up file pointer */ chnex -> lockmode = lockmode; /* set up channel file open lock mode */ chnex -> ignclose = iopex -> u.create.p.ignclose; /* maybe ignore closes */ if (OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_SELF_READ)) file -> refc_read ++; /* increment read and/or write counts */ if (OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_SELF_WRITE)) file -> refc_write ++; if (!OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_OTHERS_READ)) file -> deny_read ++; if (!OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_OTHERS_WRITE)) file -> deny_write ++; chnex -> cur_blk = 1; /* set record I/O context to beg of file */ chnex -> cur_byt = 0; memcpy (iopex -> u.create.p.fileidbuff, (*(devex -> vector -> get_fileid)) (file), iopex -> u.create.p.fileidsize); (*(devex -> vector -> returnspec)) (rname, iopex -> u.create.p.rnamesize, iopex -> u.create.p.rnamebuff, iopex -> u.create.p.rnamesubs); } else if (file != NULL) oz_dev_vdfs_close_file (file, iopex); /* failed to enter, delete file (its dircount is zero) */ oz_knl_secattr_increfc (secattr, -1); /* anyway, we're done with secattrs */ return (sts); } /************************************************************************/ /* */ /* Open file */ /* */ /************************************************************************/ static uLong be_open (Iopex *iopex, Chnex *chnex, Devex *devex) { char rname[2*OZ_FS_MAXFNLEN]; char const *fname; File *dirfile, *file; Fileid dirid, fileid; int fnamelen; OZ_Lockmode lockmode; OZ_Secaccmsk secaccmsk; uLong sts; dirfile = NULL; file = NULL; lockmode = iopex -> u.create.p.lockmode; /* get file lock mode */ secaccmsk = OZ_SECACCMSK_LOOK; /* determine security access mask */ if (OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_SELF_READ)) secaccmsk |= OZ_SECACCMSK_READ; if (OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_SELF_WRITE)) secaccmsk |= OZ_SECACCMSK_WRITE; if (chnex -> file != NULL) return (OZ_FILEALREADYOPEN); /* error if something already open on channel */ if (iopex -> u.open.p.name != NULL) { /* if name supplied, open it by name */ sts = getdirid (devex -> volume, strlen (iopex -> u.open.p.name), iopex -> u.open.p.name, &dirid, &fnamelen, &fname, rname, iopex); /* get directory id the file is in */ if (sts == OZ_SUCCESS) { sts = oz_dev_vdfs_open_file (devex -> volume, &dirid, OZ_SECACCMSK_LOOK, &dirfile, iopex); /* open the directory (must be able to look in it for a specific file) */ } if (sts == OZ_SUCCESS) { sts = lookup_file (dirfile, strlen (fname), fname, &fileid, rname + strlen (rname), iopex); /* lookup the file in the directory */ } if (dirfile != NULL) oz_dev_vdfs_close_file (dirfile, iopex); /* close the directory */ if (sts == OZ_SUCCESS) { sts = oz_dev_vdfs_open_file (devex -> volume, &fileid, secaccmsk, &file, iopex); /* open the file */ } } else { sts = oz_dev_vdfs_open_file (devex -> volume, iopex -> u.open.p.fileidbuff, secaccmsk, &file, iopex); /* open the file by the fileid */ } if (sts == OZ_SUCCESS) { if ((OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_SELF_READ) && (file -> deny_read != 0)) || (OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_SELF_WRITE) && (file -> deny_write != 0)) || (!OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_OTHERS_READ) && (file -> refc_read != 0)) || (!OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_OTHERS_WRITE) && (file -> refc_write != 0))) { oz_dev_vdfs_close_file (file, iopex); sts = OZ_ACCONFLICT; } else { chnex -> file = file; /* set up file pointer */ chnex -> lockmode = lockmode; /* set up channel file open lock mode */ chnex -> ignclose = iopex -> u.open.p.ignclose; /* maybe ignore closes */ if (OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_SELF_READ)) file -> refc_read ++; /* increment read and/or write counts */ if (OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_SELF_WRITE)) file -> refc_write ++; if (!OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_OTHERS_READ)) file -> deny_read ++; if (!OZ_LOCK_ALLOW_TEST (lockmode, OZ_LOCK_ALLOWS_OTHERS_WRITE)) file -> deny_write ++; chnex -> cur_blk = 1; /* set record I/O context to beg of file */ chnex -> cur_byt = 0; } if (iopex -> u.open.p.name != NULL) { memcpy (iopex -> u.open.p.fileidbuff, &fileid, iopex -> u.open.p.fileidsize); (*(devex -> vector -> returnspec)) (rname, iopex -> u.open.p.rnamesize, iopex -> u.open.p.rnamebuff, iopex -> u.open.p.rnamesubs); } } return (sts); } /************************************************************************/ /* */ /* Close file, delete it if marked for delete */ /* */ /************************************************************************/ static Iopex *abort_requests (Iopex **iopex_qh, Iopex ***iopex_qt, Chnex *chnex); static uLong be_close (Iopex *iopex, Chnex *chnex, Devex *devex) { File *file; int waited; Iopex *niopex, *xiopex; Long shortcuts; OZ_Dbn efblk; uLong efbyt, sts, vl; Wildscan *wildscan; sts = OZ_FILENOTOPEN; // error status to return if nothing open /* See how many shortcut requests are in progress and block anything more (shortcut and normal) from queuing */ xiopex = NULL; vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); validate_shortcuts (chnex); chnex -> closeshorts = shortcuts = oz_hw_atomic_set_long (&(chnex -> shortcuts), -1); /* Abort any requests in the thread's queue for this channel. This is */ /* in case some shortcut routine queued a request before we blocked them. */ xiopex = abort_requests (&(devex -> iopexqh), &(devex -> iopexqt), chnex); oz_hw_smplock_clr (&(devex -> smplock_vl), vl); // get back to softint level while ((niopex = xiopex) != NULL) { // abort the requests xiopex = niopex -> next; iodonex (niopex, OZ_CLOSEINPROG, NULL, NULL); } /* Remove all requests from the queuerecio queue for this channel. Just in case there is some request ahead of them */ /* on a different channel that will queue a normal request (like an extend) and thus our requests will never complete. */ file = chnex -> file; if (file != NULL) { vl = oz_hw_smplock_wait (&(file -> recio_vl)); // lock the file's record-io request queue xiopex = abort_requests (&(file -> recio_qh), &(file -> recio_qt), chnex); // find requests to abort oz_hw_smplock_clr (&(file -> recio_vl), vl); // unlock the request queue while ((niopex = xiopex) != NULL) { // abort the requests xiopex = niopex -> next; iodonex (niopex, OZ_CLOSEINPROG, NULL, NULL); finishortcut (chnex, devex, niopex); } } /* Make sure all shortcut requests have finished. We already blocked any more from starting. */ /* Note that queuerecio requests count as shortcuts, so this will wait for them, too. */ waited = 0; vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); while (1) { // see if any shortcuts are still going #if 111 for (xiopex = chnex -> shortcut_iopexs; xiopex != NULL; xiopex = xiopex -> shortcut_next) { oz_knl_printk ("oz_dev_vdfs be_close*: chnex %p -> shortcut_iopex %p -> backend %p\n", chnex, xiopex, xiopex -> backend); } #endif if (chnex -> shortcuts + shortcuts == -1) break; oz_knl_printk ("oz_dev_vdfs be_close*: chnex %p -> shortcuts %d + shortcuts %d > -1\n", chnex, chnex -> shortcuts, shortcuts); oz_hw_smplock_clr (&(devex -> smplock_vl), vl); oz_knl_event_waitone (devex -> shortcutev); // if so, wait for them to finish oz_knl_event_set (devex -> shortcutev, 0); vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); waited = 1; } if (waited) oz_knl_printk ("oz_dev_vdfs be_close*: resuming\n"); oz_hw_smplock_clr (&(devex -> smplock_vl), vl); // if shortcuts was 3, chnex->shortcuts is now -4 // ... indicating that all 3 have completed /* Now close any file that might be open on channel */ if (file != NULL) { // see if anything open if (OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_SELF_WRITE)) { // maybe set some file attributes if (iopex -> u.close.p.numitems != 0) { sts = set_file_attrs (file, iopex -> u.close.p.numitems, iopex -> u.close.p.itemlist, iopex); } } if (OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_SELF_READ)) file -> refc_read --; if (OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_SELF_WRITE)) file -> refc_write --; if (!OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_OTHERS_READ)) file -> deny_read --; if (!OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_OTHERS_WRITE)) file -> deny_write --; sts = oz_dev_vdfs_close_file (file, iopex); // close file chnex -> file = NULL; // mark channel closed } /* Close any wildcard search context that might be open on channel */ /* We know wildcard scanning is not in progress because we have locked out all shortcut routines */ while ((wildscan = chnex -> wildscan) != NULL) { (*(devex -> vector -> wildscan_terminate)) (chnex); // close out fs-dependent context wildscan_unlink (wildscan); // make sure wildscan is unlinked from dirfile->wildscans list chnex -> wildscan = wildscan -> nextouter; // save pointer to next outer level ((Chnex *)oz_knl_iochan_ex (wildscan -> iochan)) -> scassochnex = NULL; // we won't be queuing any more I/O's to the channel oz_knl_iochan_increfc (wildscan -> iochan, -1); // close the directory file OZ_KNL_PGPFREE (wildscan); // free off the struct if (sts == OZ_FILENOTOPEN) sts = OZ_SUCCESS; } /* Close is complete. Set shortcuts back to zero to allow new requests to start on channel. */ OZ_HW_MB; // make sure chnex->file=NULL, etc, seen first chnex -> shortcuts = 0; // let stuff go wild again /* Also clear wild_iopex so wildcard scans can be done */ vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); chnex -> wild_iopex = NULL; oz_hw_smplock_clr (&(devex -> smplock_vl), vl); return (sts); } /* Find requests in the queue that match the given channel and remove them for aborting */ /* Do likewise for any requests sitting in the queue that are for shortcut processing on the channel */ static Iopex *abort_requests (Iopex **iopex_qh, Iopex ***iopex_qt, Chnex *chnex) { Iopex **liopex, *niopex, *xiopex; xiopex = NULL; for (liopex = iopex_qh; (niopex = *liopex) != NULL;) { // scan the request queue if ((niopex -> chnex != chnex) && (niopex -> chnex -> scassochnex != chnex)) liopex = &(niopex -> next); // leave request in queue if different channel else { *liopex = niopex -> next; // same channel, unlink request from queue niopex -> next = xiopex; // link it to list of requests to be aborted xiopex = niopex; } } *iopex_qt = liopex; // the tail of the queue might be different now return (xiopex); // return the list of requests to abort } /************************************************************************/ /* */ /* Enter file alias name */ /* */ /************************************************************************/ static uLong be_enter (Iopex *iopex, Chnex *chnex, Devex *devex) { char rname[2*OZ_FS_MAXFNLEN]; char const *fname; File *dirfile; Fileid dirid, fileid; int fnamelen; uLong sts; dirfile = NULL; if (chnex -> file == NULL) sts = OZ_FILENOTOPEN; /* make sure we have an open file */ else sts = getdirid (devex -> volume, strlen (iopex -> u.enter.p.name), iopex -> u.enter.p.name, &dirid, &fnamelen, &fname, rname, iopex); /* get directory id the new filename is in */ if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_open_file (devex -> volume, &dirid, OZ_SECACCMSK_WRITE, &dirfile, iopex); /* open the directory (must be able to write it) */ if (sts == OZ_SUCCESS) { sts = lookup_file (dirfile, fnamelen, fname, &fileid, NULL, iopex); /* lookup the new filename in the directory */ if (sts == OZ_SUCCESS) sts = OZ_FILEALREADYEXISTS; else if (sts == OZ_NOSUCHFILE) sts = OZ_SUCCESS; } if (sts == OZ_SUCCESS) sts = enter_file (dirfile, rname, fnamelen, fname, iopex -> u.enter.p.newversion, chnex -> file, (*(devex -> vector -> get_fileid)) (chnex -> file), rname + strlen (rname), iopex); /* enter in directory */ if (sts == OZ_SUCCESS) (*(devex -> vector -> returnspec)) (rname, iopex -> u.enter.p.rnamesize, iopex -> u.enter.p.rnamebuff, iopex -> u.enter.p.rnamesubs); if (dirfile != NULL) oz_dev_vdfs_close_file (dirfile, iopex); /* close the directory */ return (sts); } /************************************************************************/ /* */ /* Remove file name from directory. If last entry, delete file. */ /* */ /************************************************************************/ static uLong be_remove (Iopex *iopex, Chnex *chnex, Devex *devex) { char rname[2*OZ_FS_MAXFNLEN]; char const *fname; File *dirfile; Fileid dirid; int fnamelen; uLong sts; dirfile = NULL; sts = getdirid (devex -> volume, strlen (iopex -> u.remove.p.name), iopex -> u.remove.p.name, &dirid, &fnamelen, &fname, rname, iopex); /* get directory id the filename is in */ if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_open_file (devex -> volume, &dirid, OZ_SECACCMSK_WRITE, &dirfile, iopex); /* open the directory (must be able to write it) */ if (sts == OZ_SUCCESS) sts = remove_file (dirfile, fname, rname + strlen (rname), iopex); /* remove the filename from the directory */ if (sts == OZ_SUCCESS) (*(devex -> vector -> returnspec)) (rname, iopex -> u.remove.p.rnamesize, iopex -> u.remove.p.rnamebuff, iopex -> u.remove.p.rnamesubs); if (dirfile != NULL) oz_dev_vdfs_close_file (dirfile, iopex); /* close the directory */ return (sts); } /************************************************************************/ /* */ /* Rename file */ /* */ /************************************************************************/ static uLong be_rename (Iopex *iopex, Chnex *chnex, Devex *devex) { char newrname[2*OZ_FS_MAXFNLEN], oldrname[2*OZ_FS_MAXFNLEN]; char const *newfname, *oldfname; File *file, *newdirfile, *olddirfile; Fileid fileid, newdirid, olddirid; int newfnamelen, oldfnamelen; uLong sts; file = NULL; newdirfile = NULL; olddirfile = NULL; sts = getdirid (devex -> volume, strlen (iopex -> u.rename.p.oldname), iopex -> u.rename.p.oldname, &olddirid, &oldfnamelen, &oldfname, oldrname, iopex); /* get directory the old filename is in */ if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_open_file (devex -> volume, &olddirid, OZ_SECACCMSK_WRITE, &olddirfile, iopex); /* open the directory the old filename is in (must be able to write it) */ if (sts == OZ_SUCCESS) sts = lookup_file (olddirfile, oldfnamelen, oldfname, &fileid, oldrname + strlen (oldrname), iopex); /* lookup the old filename to get the fileid */ if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_open_file (devex -> volume, &fileid, 0, &file, iopex); /* open the file being renamed */ if (sts == OZ_SUCCESS) sts = getdirid (devex -> volume, strlen (iopex -> u.rename.p.newname), iopex -> u.rename.p.newname, &newdirid, &newfnamelen, &newfname, newrname, iopex); /* get directory id the new filename goes in */ if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_open_file (devex -> volume, &newdirid, OZ_SECACCMSK_WRITE, &newdirfile, iopex); /* open the directory the new filename goes in (must be able to write it) */ if (sts == OZ_SUCCESS) sts = rename_file (olddirfile, oldfname, newdirfile, newrname, newfnamelen, newfname, iopex -> u.rename.p.newversion, NULL, &fileid, newrname + strlen (newrname), iopex); /* rename it */ if (sts == OZ_SUCCESS) { (*(devex -> vector -> returnspec)) (oldrname, iopex -> u.rename.p.oldrnamesize, iopex -> u.rename.p.oldrnamebuff, iopex -> u.rename.p.oldrnamesubs); (*(devex -> vector -> returnspec)) (newrname, iopex -> u.rename.p.newrnamesize, iopex -> u.rename.p.newrnamebuff, iopex -> u.rename.p.newrnamesubs); } if (olddirfile != NULL) oz_dev_vdfs_close_file (olddirfile, iopex); /* close the old directory */ if (newdirfile != NULL) oz_dev_vdfs_close_file (newdirfile, iopex); /* close the new directory */ if (file != NULL) oz_dev_vdfs_close_file (file, iopex); /* close the file */ return (sts); /* return composite status */ } /************************************************************************/ /* */ /* Extend file */ /* */ /************************************************************************/ static uLong be_extend (Iopex *iopex, Chnex *chnex, Devex *devex) { File *file; uLong efbyt, sts, vl; OZ_Dbn efblk; file = chnex -> file; sts = OZ_FILENOTOPEN; if (file != NULL) { if (IS_DIRECTORY (file)) sts = OZ_FILEISADIR; else if (!OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_SELF_WRITE)) sts = OZ_NOWRITEACCESS; else if (iopex -> u.extend.p.nblocks < file -> allocblocks) { if (!(iopex -> u.extend.p.extflags & OZ_FS_EXTFLAG_NOTRUNC)) { file -> truncpend = 1; file -> truncblocks = iopex -> u.extend.p.nblocks; } return (OZ_SUCCESS); } else sts = extend_file (file, iopex -> u.extend.p.nblocks, iopex -> u.extend.p.extflags, iopex); if ((sts == OZ_SUCCESS) && ((efblk = iopex -> u.extend.p.eofblock) != 0)) { efbyt = iopex -> u.extend.p.eofbyte; efblk += efbyt / devex -> blocksize; efbyt %= devex -> blocksize; vl = oz_hw_smplock_wait (&(file -> attrlock_vl)); file -> attrlock_efblk = efblk; file -> attrlock_efbyt = efbyt; file -> attrlock_flags |= OZ_VDFS_ALF_M_EOF; oz_hw_smplock_clr (&(file -> attrlock_vl), vl); oz_dev_vdfs_mark_header_dirty (file); } } return (sts); } /************************************************************************/ /* */ /* Write blocks */ /* */ /* This request takes a shortcut around the queue and is processed */ /* immediately upon issue. Ideally, it will just need to copy the */ /* user's buffer to cache blocks and return synchronous completion. */ /* Otherwise, it may have to start a disk read then complete later. */ /* */ /* Input: */ /* */ /* iopex = I/O request extension */ /* chnex = corresponding I/O channel extension */ /* devex = corresponding device extension */ /* smplevel = softint */ /* */ /* Output: */ /* */ /* sc_writeblocks = OZ_STARTED : request queued, will complete */ /* asynchronously */ /* else : completion status */ /* */ /************************************************************************/ static uLong dc_writeblocks (OZ_Dcmpb *dcmpb, uLong status); static uLong wt_writeblocks (OZ_Dcmpb *dcmpb, uLong status); static uLong sc_writeblocks (Iopex *iopex, Chnex *chnex, Devex *devex) { File *file; uLong sts; // Increment channel's shortcut count, to prevent a close from being queued. // But if the count is already negative, that means a close has already been queued on the channel. sts = startshortcut (chnex, devex, iopex); if (sts != OZ_SUCCESS) return (sts); // Ok, closes are blocked, make sure the write can proceed sts = OZ_FILENOTOPEN; // the channel has to have a file open on it file = chnex -> file; if (file == NULL) goto rtnsts; sts = OZ_FILEISADIR; // don't allow writing to a directory if (IS_DIRECTORY (file)) goto rtnsts; sts = OZ_NOWRITEACCESS; // and we have to have write access to file if (!OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_SELF_WRITE)) goto rtnsts; // Maybe volume or file attributes force write-thru mode if ((*(iopex -> devex -> vector -> vis_writethru)) (file -> volume) || (*(iopex -> devex -> vector -> fis_writethru)) (file, iopex -> u.writeblocks.p.svbn, iopex -> u.writeblocks.p.offs, iopex -> u.writeblocks.p.size, NULL)) iopex -> u.writeblocks.p.writethru = 1; // Start processing via cache iopex -> u.writeblocks.dcmpb.writing = 1; // disk write operation iopex -> u.writeblocks.dcmpb.virtblock = iopex -> u.writeblocks.p.svbn; // starting virtual block number iopex -> u.writeblocks.dcmpb.nbytes = iopex -> u.writeblocks.p.size; // number of bytes to be written to disk iopex -> u.writeblocks.dcmpb.blockoffs = iopex -> u.writeblocks.p.offs; // byte offset in starting virtual block iopex -> u.writeblocks.dcmpb.entry = dc_writeblocks; // call this routine when cache blocks ready iopex -> u.writeblocks.dcmpb.param = iopex; iopex -> u.writeblocks.dcmpb.writethru = iopex -> u.writeblocks.p.writethru; // set up writethru mode flag iopex -> u.writeblocks.dcmpb.ix4kbuk = 0; iopex -> u.writeblocks.status = OZ_SUCCESS; // in case nbytes is zero sts = oz_dev_vdfs_dcache_map_vbn_to_lbn (&(iopex -> u.writeblocks.dcmpb), chnex -> file); // map virtual-to-logical block number if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_dcache_map_blocks (&(iopex -> u.writeblocks.dcmpb)); // process the request if (sts != OZ_STARTED) dc_writeblocks (&(iopex -> u.writeblocks.dcmpb), sts); return (OZ_STARTED); rtnsts: finishortcut (iopex -> chnex, iopex -> devex, iopex); return (sts); } /************************************************************************/ /* */ /* The disk cache system calls this routine when it has access to the */ /* cache blocks for the disk */ /* */ /************************************************************************/ static uLong dc_writeblocks (OZ_Dcmpb *dcmpb, uLong status) { Chnex *chnex; const OZ_Mempage *phypages; File *file; Iopex *iopex; uLong size, skip, vl; iopex = dcmpb -> param; chnex = iopex -> chnex; file = chnex -> file; size = 0; /* Maybe the write request is complete now */ if (status != OZ_PENDING) { if (status == OZ_SUCCESS) status = iopex -> u.writeblocks.status; vl = oz_hw_smplock_wait (&(file -> attrlock_vl)); file -> attrlock_date = oz_hw_tod_getnow (); file -> attrlock_flags |= OZ_VDFS_ALF_M_ADT | OZ_VDFS_ALF_M_CDT | OZ_VDFS_ALF_M_MDT; oz_hw_smplock_clr (&(file -> attrlock_vl), vl); finishortcut (chnex, iopex -> devex, iopex); iodonex (iopex, status, NULL, NULL); } /* Copy to cache page from user buffer and increment parameters for next transfer */ /* If new nbytes is zero, means we're done, and cache system will call us back when it's ok to free dcmpb */ else { skip = (dcmpb -> virtblock - iopex -> u.writeblocks.p.svbn) * iopex -> devex -> blocksize + (dcmpb -> blockoffs - iopex -> u.writeblocks.p.offs); size = iopex -> u.writeblocks.p.size - skip; if (size > dcmpb -> nbytes) size = dcmpb -> nbytes; oz_hw_phys_movephys (size, iopex -> u.writeblocks.phypages, skip + iopex -> u.writeblocks.phyoffs, &(dcmpb -> phypage), dcmpb -> pageoffs); skip += size; dcmpb -> virtblock = iopex -> u.writeblocks.p.svbn; dcmpb -> nbytes = iopex -> u.writeblocks.p.size - skip; dcmpb -> blockoffs = iopex -> u.writeblocks.p.offs + skip; iopex -> u.writeblocks.status = oz_dev_vdfs_dcache_map_vbn_to_lbn (dcmpb, file); if (iopex -> u.writeblocks.status != OZ_SUCCESS) dcmpb -> nbytes = 0; } /* Return number of bytes we modified */ return (size); } /************************************************************************/ /* */ /* Read blocks */ /* */ /* This request takes a shortcut around the queue and is processed */ /* immediately upon issue. Ideally, it will just need to copy the */ /* user's buffer from cache blocks and return synchronous completion. */ /* Otherwise, it may have to start a disk read then complete later. */ /* */ /* Input: */ /* */ /* iopex = I/O request extension */ /* chnex = corresponding I/O channel extension */ /* devex = corresponding device extension */ /* smplevel = softint */ /* */ /* Output: */ /* */ /* sc_readblocks = OZ_STARTED : request queued, will complete */ /* asynchronously */ /* else : completion status */ /* */ /************************************************************************/ static uLong dc_readblocks (OZ_Dcmpb *dcmpb, uLong status); static uLong nc_readblocks_start (Iopex *iopex); static void nc_readblocks_done (void *iopexv, uLong status); static uLong sc_readblocks (Iopex *iopex, Chnex *chnex, Devex *devex) { uLong sts; // Increment channel's shortcut count, to prevent a close from being queued. // But if the count is already negative, that means a close has already been queued on the channel. sts = startshortcut (chnex, devex, iopex); if (sts != OZ_SUCCESS) return (sts); // Ok, closes are blocked, make sure the read can proceed sts = OZ_FILENOTOPEN; // the channel has to have a file open on it if (chnex -> file == NULL) goto rtnsts; sts = OZ_NOREADACCESS; // and we have to have read access to file if (!OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_SELF_READ)) goto rtnsts; // Start processing via cache if (devex -> dcache != NULL) { iopex -> u.readblocks.dcmpb.writing = 0; // disk read operation iopex -> u.readblocks.dcmpb.virtblock = iopex -> u.readblocks.p.svbn; // starting virtual block number iopex -> u.readblocks.dcmpb.nbytes = iopex -> u.readblocks.p.size; // number of bytes to be written to disk iopex -> u.readblocks.dcmpb.blockoffs = iopex -> u.readblocks.p.offs; // byte offset in starting virtual block iopex -> u.readblocks.dcmpb.entry = dc_readblocks; // call this routine when cache blocks ready iopex -> u.readblocks.dcmpb.param = iopex; iopex -> u.readblocks.dcmpb.ix4kbuk = iopex -> u.readblocks.p.ix4kbuk; iopex -> u.readblocks.status = OZ_SUCCESS; // in case nbytes is zero sts = oz_dev_vdfs_dcache_map_vbn_to_lbn (&(iopex -> u.readblocks.dcmpb), chnex -> file); // map virtual-to-logical block number if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_dcache_map_blocks (&(iopex -> u.readblocks.dcmpb)); // process request if (sts != OZ_STARTED) dc_readblocks (&(iopex -> u.readblocks.dcmpb), sts); return (OZ_STARTED); } // No cache, start processing 'manually' iopex -> u.readblocks.temp = NULL; // haven't allocated a temp buffer yet sts = nc_readblocks_start (iopex); // start reading directly from disk into caller's buffer if (sts != OZ_STARTED) nc_readblocks_done (iopex, sts); // if sync compl, call compl routine so it can clean up return (OZ_STARTED); rtnsts: finishortcut (chnex, devex, iopex); return (sts); } /************************************************************************/ /* */ /* The disk cache system calls this routine when it has access to the */ /* cache blocks for the disk */ /* */ /************************************************************************/ static uLong dc_readblocks (OZ_Dcmpb *dcmpb, uLong status) { Chnex *chnex; const OZ_Mempage *phypages; Devex *devex; File *file; Iopex *iopex; OZ_Dbn prefetch; uLong size, skip, vl; iopex = dcmpb -> param; chnex = iopex -> chnex; devex = iopex -> devex; file = chnex -> file; /* Maybe the read request is complete now */ if (status != OZ_PENDING) { int ix4kbuk; if (status == OZ_SUCCESS) status = iopex -> u.readblocks.status; if (!(file -> volume -> mountflags & OZ_FS_MOUNTFLAG_READONLY)) { vl = oz_hw_smplock_wait (&(file -> attrlock_vl)); file -> attrlock_date = oz_hw_tod_getnow (); file -> attrlock_flags |= OZ_VDFS_ALF_M_ADT; oz_hw_smplock_clr (&(file -> attrlock_vl), vl); } #if 000 prefetch = 0; if (status == OZ_SUCCESS) { dcmpb -> nbytes = 1; if ((ix4kbuk = dcmpb -> ix4kbuk) != 0) dcmpb -> nbytes = 4096; if (oz_dev_vdfs_dcache_map_vbn_to_lbn (dcmpb, chnex -> file) == OZ_SUCCESS) prefetch = dcmpb -> logblock; } #endif finishortcut (chnex, devex, iopex); iodonex (iopex, status, NULL, NULL); #if 000 if (prefetch != 0) oz_knl_dcache_prefetch (devex -> dcache, prefetch, ix4kbuk); #endif } /* Copy from cache page to user buffer and increment parameters for next transfer */ /* If new nbytes is zero, means we're done, and cache system will call us back when it's ok to free dcmpb */ else { skip = (dcmpb -> virtblock - iopex -> u.readblocks.p.svbn) * devex -> blocksize + (dcmpb -> blockoffs - iopex -> u.readblocks.p.offs); size = iopex -> u.readblocks.p.size - skip; if (size > dcmpb -> nbytes) size = dcmpb -> nbytes; if (iopex -> u.readblocks.p.ix4kbuk) { if (size != 4096) oz_knl_printk ("oz_dev_vdfs dc_readblocks %d: size %u\n", __LINE__, size); else ix4kbuk_validate_phypage (&(dcmpb -> phypage), dcmpb -> pageoffs, __FILE__, __LINE__); } oz_hw_phys_movephys (size, &(dcmpb -> phypage), dcmpb -> pageoffs, iopex -> u.readblocks.phypages, skip + iopex -> u.readblocks.phyoffs); if (iopex -> u.readblocks.p.ix4kbuk) { if (size != 4096) oz_knl_printk ("oz_dev_vdfs dc_readblocks %d: size %u\n", __LINE__, size); else ix4kbuk_validate_phypage (iopex -> u.readblocks.phypages, skip + iopex -> u.readblocks.phyoffs, __FILE__, __LINE__); } skip += size; dcmpb -> virtblock = iopex -> u.readblocks.p.svbn; dcmpb -> nbytes = iopex -> u.readblocks.p.size - skip; dcmpb -> blockoffs = iopex -> u.readblocks.p.offs + skip; iopex -> u.readblocks.status = oz_dev_vdfs_dcache_map_vbn_to_lbn (dcmpb, file); if (iopex -> u.readblocks.status != OZ_SUCCESS) dcmpb -> nbytes = 0; } /* Return number of bytes we modified */ return (0); } /************************************************************************/ /* */ /* No cache, start reading blocks directly from disk */ /* */ /* Input: */ /* */ /* iopex -> u.readblocks.p.size = size of transfer */ /* svbn = starting virt block number */ /* offs = offset in svbn block */ /* .phypages = physical page array pointer */ /* .phyoffs = offset in first physical page */ /* */ /* Output: */ /* */ /* nc_readblocks_start = OZ_STARTED : will complete asyncly */ /* else : error status */ /* */ /************************************************************************/ static uLong nc_readblocks_start (Iopex *iopex) { Chnex *chnex; Devex *devex; OZ_Dbn logblock, nblocks; OZ_IO_disk_readblocks disk_readblocks; OZ_IO_disk_readpages disk_readpages; uLong sts; chnex = iopex -> chnex; devex = iopex -> devex; /* Normalize starting virtual block number */ iopex -> u.readblocks.p.svbn += iopex -> u.readblocks.p.offs / devex -> blocksize; iopex -> u.readblocks.p.offs %= devex -> blocksize; /* Normalize starting physical page number */ iopex -> u.readblocks.phypages += iopex -> u.readblocks.phyoffs >> OZ_HW_L2PAGESIZE; iopex -> u.readblocks.phyoffs %= 1 << OZ_HW_L2PAGESIZE; /* Convert starting virtual block number to logical block number */ sts = (*(devex -> vector -> map_vbn_to_lbn)) (chnex -> file, iopex -> u.readblocks.p.svbn, &nblocks, &logblock); if (sts != OZ_SUCCESS) return (sts); /* Disk can only do block sized transfers to properly aligned memory buffers */ /* If request doesn't meet those criteria, use a temp buffer */ if ((iopex -> u.readblocks.p.offs != 0) // must start at beginning of disk block || (iopex -> u.readblocks.phyoffs & devex -> bufalign) // must be long/quad/whatever aligned || ((iopex -> u.readblocks.p.size % devex -> blocksize) != 0)) { // must be an exact number of blocks if (iopex -> u.readblocks.temp == NULL) { // no, malloc a block-sized buffer iopex -> u.readblocks.temp = OZ_KNL_NPPMALLOQ (devex -> blocksize); if (iopex -> u.readblocks.temp == NULL) return (OZ_EXQUOTANPP); } iopex -> u.readblocks.nblocks = 1; // we just read one block memset (&disk_readblocks, 0, sizeof disk_readblocks); disk_readblocks.size = devex -> blocksize; disk_readblocks.buff = iopex -> u.readblocks.temp; disk_readblocks.slbn = logblock; sts = oz_knl_iostart3 (1, NULL, devex -> master_iochan, OZ_PROCMODE_KNL, // start reading into temp buffer nc_readblocks_done, iopex, NULL, NULL, NULL, NULL, OZ_IO_DISK_READBLOCKS, sizeof disk_readblocks, &disk_readblocks); } else { if (iopex -> u.readblocks.temp != NULL) { // ok, free temp buffer so nc_readblocks_done won't try to copy from it OZ_KNL_NPPFREE (iopex -> u.readblocks.temp); iopex -> u.readblocks.temp = NULL; } if (nblocks > iopex -> u.readblocks.p.size / devex -> blocksize) { // don't read more than caller wants nblocks = iopex -> u.readblocks.p.size / devex -> blocksize; } iopex -> u.readblocks.nblocks = nblocks; // this is now much we're reading memset (&disk_readpages, 0, sizeof disk_readpages); disk_readpages.size = nblocks * devex -> blocksize; disk_readpages.pages = iopex -> u.readblocks.phypages; disk_readpages.offset = iopex -> u.readblocks.phyoffs; disk_readpages.slbn = logblock; sts = oz_knl_iostart3 (1, NULL, devex -> master_iochan, OZ_PROCMODE_KNL, // start reading directly into caller's buffer nc_readblocks_done, iopex, NULL, NULL, NULL, NULL, OZ_IO_DISK_READPAGES, sizeof disk_readpages, &disk_readpages); } return (sts); } /************************************************************************/ /* */ /* A direct read of the disk has completed (there is no cache active */ /* on the disk) */ /* */ /* Input: */ /* */ /* iopex -> u.readblocks.p.size = size remaining (incl this one) */ /* iopex -> u.readblocks.p.svbn = vbn just read in */ /* iopex -> u.readblocks.nblocks = number of blocks just read */ /* iopex -> u.readblocks.temp = NULL : read into phypages */ /* else : read into this temp buff */ /* iopex -> u.readblocks.phypages = where data was read into */ /* iopex -> u.readblocks.phyoffs = where data was read into */ /* status = disk read status */ /* */ /* Output: */ /* */ /* I/O completion posted or another disk read started */ /* */ /************************************************************************/ static void nc_readblocks_done (void *iopexv, uLong status) { Devex *devex; Iopex *iopex; uLong size, sts; iopex = iopexv; devex = iopex -> devex; /* Abort if read error */ sts = status; while (sts == OZ_SUCCESS) { /* Get length read. If using temp buffer, it is the size of the temp buffer. Else, it shouldn't be more than caller wants. */ size = iopex -> u.readblocks.nblocks * devex -> blocksize; /* If read into temp buffer, copy to caller's buffer */ if (iopex -> u.readblocks.temp != NULL) { size -= iopex -> u.readblocks.p.offs; // we ignore this much at beg of temp buffer if (size > iopex -> u.readblocks.p.size) size = iopex -> u.readblocks.p.size; // don't copy back more than caller wants oz_hw_phys_movefromvirt (size, iopex -> u.readblocks.temp + iopex -> u.readblocks.p.offs, // copy from temp buffer to caller's buffer iopex -> u.readblocks.phypages, iopex -> u.readblocks.phyoffs); } /* Maybe we're all done */ iopex -> u.readblocks.p.size -= size; if (iopex -> u.readblocks.p.size == 0) break; /* If not, increment and start another read */ iopex -> u.readblocks.p.offs += size; iopex -> u.readblocks.phyoffs += size; sts = nc_readblocks_start (iopex); /* Repeat if successful synchronous completion */ } /* If we're not waiting for another read, request is complete */ if (sts != OZ_STARTED) { if (iopex -> u.readblocks.temp != NULL) OZ_KNL_NPPFREE (iopex -> u.readblocks.temp); finishortcut (iopex -> chnex, devex, iopex); iodonex (iopex, sts, NULL, NULL); } } /************************************************************************/ /* */ /* IX DEBUG routine */ /* */ /* Dump out stats about cache blocks pertaining to the given size and */ /* starting virtual block number */ /* */ /* Input: */ /* */ /* iopex = I/O request extension */ /* chnex = corresponding I/O channel extension */ /* devex = corresponding device extension */ /* smplevel = softint */ /* */ /* Output: */ /* */ /* sc_ixdeb = completion status */ /* */ /************************************************************************/ static uLong sc_ixdeb (Iopex *iopex, Chnex *chnex, Devex *devex) { File *file; Ixdeb *ixdeb; OZ_Dbn logblock, nblocks, svbn; OZ_Handle h_output; OZ_IO_disk_readblocks disk_readblocks; uByte *blockbuff; uLong blocksize, size, sts; // Increment channel's shortcut count, to prevent a close from being queued. // But if the count is already negative, that means a close has already been queued on the channel. sts = startshortcut (chnex, devex, iopex); if (sts != OZ_SUCCESS) return (sts); // Ok, closes are blocked, make sure the read can proceed sts = OZ_FILENOTOPEN; // the channel has to have a file open on it file = chnex -> file; if (file == NULL) goto rtnsts; sts = OZ_NOREADACCESS; // and we have to have read access to file if (!OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_SELF_READ)) goto rtnsts; // Make sure cache is active sts = 999; if (devex -> dcache == NULL) goto rtnsts; // Get parameters of part of file to dump stats for ixdeb = (Ixdeb *)&(iopex -> u); size = ixdeb -> size; svbn = ixdeb -> svbn; h_output = ixdeb -> hout; // Dump cache stats for those blocks blocksize = devex -> blocksize; while (size > 0) { sts = (*(devex -> vector -> map_vbn_to_lbn)) (file, svbn, &nblocks, &logblock); if (sts != OZ_SUCCESS) { oz_sys_io_fs_printf (h_output, "oz_dev_vdfs sc_ixdeb*: error %u mapping vbn %u\n", sts, svbn); goto rtnsts; } if (nblocks > (size + blocksize - 1) / blocksize) nblocks = (size + blocksize - 1) / blocksize; oz_sys_io_fs_printf (h_output, "oz_dev_vdfs sc_ixdeb*: vbn %u -> %u @ %u\n", svbn, nblocks, logblock); nblocks = oz_knl_dcache_ixdeb (devex -> dcache, nblocks, logblock, h_output); if (nblocks == 0) { oz_sys_io_fs_printf (h_output, "oz_dev_vdfs sc_ixdeb*: oz_knl_dcache_ixdeb returned 0\n"); break; } size -= nblocks * blocksize; svbn += nblocks; } // Dump out the logical blocks right from the hard drive size = ixdeb -> size; svbn = ixdeb -> svbn; blockbuff = OZ_KNL_PGPMALLOC (blocksize); memset (&disk_readblocks, 0, sizeof disk_readblocks); disk_readblocks.size = blocksize; disk_readblocks.buff = blockbuff; while (size > 0) { sts = (*(devex -> vector -> map_vbn_to_lbn)) (file, svbn, &nblocks, &logblock); if (sts != OZ_SUCCESS) { oz_sys_io_fs_printf (h_output, "oz_dev_vdfs sc_ixdeb*: error %u mapping vbn %u\n", sts, svbn); goto rtnsts; } oz_sys_io_fs_printf (h_output, "oz_dev_vdfs sc_ixdeb*: vbn %u -> lbn %u:\n", svbn, logblock); disk_readblocks.slbn = logblock; sts = oz_knl_io (devex -> master_iochan, OZ_IO_DISK_READBLOCKS, sizeof disk_readblocks, &disk_readblocks); if (sts != OZ_SUCCESS) { oz_sys_io_fs_printf (h_output, "oz_dev_vdfs sc_ixdeb*: error %u reading lbn %u\n", sts, logblock); goto rtnsts; } oz_sys_io_fs_dumpmem (h_output, blocksize, blockbuff); size -= blocksize; svbn ++; } OZ_KNL_PGPFREE (blockbuff); sts = OZ_SUCCESS; rtnsts: finishortcut (chnex, devex, iopex); return (sts); } /************************************************************************/ /* */ /* Validate an IX database 4K bucket */ /* */ /************************************************************************/ int ix4kbuk_validate_phypage (const OZ_Mempage *phypages, uLong phyoffs, char const *file, int line) { int i; OZ_Pagentry savepte; uLong cksm, cksm0, cksmx, *vaddr; // Normalize page offset phypages += phyoffs >> 12; phyoffs &= 0xFFF; // Map first part of buffer to virtual memory vaddr = oz_hw_phys_mappage (*phypages, &savepte); (OZ_Pointer)vaddr += phyoffs; // Faster for normal case of longword alignment if ((phyoffs & 3) == 0) { cksm0 = *vaddr; cksm = 0; for (i = 1; i < 1024; i ++) { ++ vaddr; if ((((OZ_Pointer)vaddr) & 0xFFF) == 0) { vaddr = oz_hw_phys_mappage (*(++ phypages), NULL); } cksm += *vaddr; } } // Slower for unaligned buffers else { for (i = 0; i < 1024; i ++) { if ((((OZ_Pointer)vaddr) & 0xFFF) > 0xFFC) { memcpy (&cksmx, vaddr, 4 - (phyoffs & 3)); vaddr = oz_hw_phys_mappage (*(++ phypages), NULL); memcpy (((uByte *)&cksmx) + 4 - (phyoffs & 3), vaddr, phyoffs & 3); (OZ_Pointer)vaddr += phyoffs & 3; } else { cksmx = *(vaddr ++); } if (i == 0) { cksm0 = cksmx; cksm = 0; } else { cksm += cksmx; } } } oz_hw_phys_unmappage (savepte); if (cksm != cksm0) oz_knl_printk ("%s %d: checksum %8.8X calculated %8.8X\n", file, line, cksm0, cksm); return (cksm == cksm0); } /************************************************************************/ /* */ /* Write record */ /* */ /* This is a shortcut routine, but there is only one request active */ /* per file at a time, as they go through file -> recio_q. */ /* */ /************************************************************************/ static void sc_writerec_extended (void *iopexv, uLong status); static uLong dc_writerec (OZ_Dcmpb *dcmpb, uLong status); static uLong sc_writerec (Iopex *iopex, Chnex *chnex, Devex *devex) { const OZ_VDFS_Vector *vector; File *file; OZ_Dbn efblk, ewblk; OZ_IO_fs_extend fs_extend; uLong blocksize, efbyt, ewbyt, sts, vl; blocksize = devex -> blocksize; vector = devex -> vector; /* Make sure we have write access to the file */ file = chnex -> file; sts = OZ_NOWRITEACCESS; if (!OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_SELF_WRITE)) goto rtnsts; /* If atblock specified, position there */ restart: if (iopex -> u.writerec.p.atblock != 0) { chnex -> cur_blk = iopex -> u.writerec.p.atblock; chnex -> cur_byt = iopex -> u.writerec.p.atbyte; } chnex -> cur_blk += chnex -> cur_byt / blocksize; chnex -> cur_byt %= blocksize; /* Maybe position to or set the end-of-file marker for the file */ iopex -> u.writerec.updateof = 0; // so far, we don't update the eof position if (iopex -> u.writerec.p.append) { // if we're appending, position to the end-of-file vl = oz_hw_smplock_wait (&(file -> attrlock_vl)); chnex -> cur_blk = file -> attrlock_efblk; chnex -> cur_byt = file -> attrlock_efbyt; oz_hw_smplock_clr (&(file -> attrlock_vl), vl); } ewblk = chnex -> cur_blk; // calculate where our write will end ewbyt = chnex -> cur_byt; ewbyt += iopex -> u.writerec.p.size + iopex -> u.writerec.p.trmsize; ewblk += ewbyt / blocksize; ewbyt %= blocksize; if (ewbyt == 0) ewblk --; if (ewblk > file -> allocblocks) { // see if write will go beyond end of allocated space memset (&fs_extend, 0, sizeof fs_extend); // if so, extend file fs_extend.nblocks = ewblk; sts = oz_knl_iostart3 (1, NULL, chnex -> iochan, OZ_PROCMODE_KNL, sc_writerec_extended, iopex, NULL, NULL, NULL, NULL, OZ_IO_FS_EXTEND, sizeof fs_extend, &fs_extend); if (sts == OZ_SUCCESS) goto restart; // ... then start over return (sts); } // Maybe volume or file attributes force write-thru mode if ((*(vector -> vis_writethru)) (file -> volume) || (*(vector -> fis_writethru)) (file, chnex -> cur_blk, chnex -> cur_byt, iopex -> u.writerec.p.size + iopex -> u.writerec.p.trmsize, NULL)) iopex -> u.writerec.p.writethru = 1; /* Write as much as we can to current position from caller's buffer */ iopex -> u.writerec.wlen = 0; // nothing written from user's buffer yet iopex -> u.writerec.trmwlen = 0; // nothing written from terminator yet iopex -> u.writerec.dcmpb.writing = 1; // this is a disk write operation iopex -> u.writerec.dcmpb.nbytes = iopex -> u.writerec.p.size + iopex -> u.writerec.p.trmsize; iopex -> u.writerec.dcmpb.virtblock = chnex -> cur_blk; // virtual block we want to start writing at iopex -> u.writerec.dcmpb.blockoffs = chnex -> cur_byt; // byte we want to start writing at iopex -> u.writerec.dcmpb.entry = dc_writerec; // write routine entrypoint iopex -> u.writerec.dcmpb.param = iopex; // write routine parameter iopex -> u.writerec.dcmpb.writethru = iopex -> u.writerec.p.writethru; // set up writethru mode flag iopex -> u.writerec.dcmpb.ix4kbuk = 0; iopex -> u.writerec.status = OZ_SUCCESS; // in case of zero bytes sts = oz_dev_vdfs_dcache_map_vbn_to_lbn (&(iopex -> u.writerec.dcmpb), file); // map virtual-to-logical block if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_dcache_map_blocks (&(iopex -> u.writerec.dcmpb)); // process the request if (sts != OZ_STARTED) dc_writerec (&(iopex -> u.writerec.dcmpb), sts); return (OZ_STARTED); rtnsts: return (sts); } /* The file has been exteded sufficiently to permit the data to be written. */ /* If the extend failed, terminate the request. Else, restart the request. */ static void sc_writerec_extended (void *iopexv, uLong status) { Iopex *iopex; iopex = iopexv; if (status != OZ_SUCCESS) finishrecio (iopex, status, NULL); else { status = sc_writerec (iopex, iopex -> chnex, iopex -> devex); if (status != OZ_STARTED) finishrecio (iopex, status, NULL); } } /************************************************************************/ /* */ /* The disk cache system calls this routine when it has access to the */ /* cache blocks for the disk */ /* */ /************************************************************************/ static uLong dc_writerec (OZ_Dcmpb *dcmpb, uLong status) { Chnex *chnex; const OZ_Mempage *phypages; File *file; Iopex *iopex; Long alf; uLong blocksize, modified, skip, size, vl; iopex = dcmpb -> param; chnex = iopex -> chnex; file = chnex -> file; modified = 0; /* Maybe we are all finished up */ if (status != OZ_PENDING) { if (status == OZ_SUCCESS) status = iopex -> u.writerec.status; /* Ok, update the current and end-of-file pointers */ if (status == OZ_SUCCESS) { blocksize = iopex -> devex -> blocksize; chnex -> cur_byt += iopex -> u.writerec.p.size + iopex -> u.writerec.p.trmsize; chnex -> cur_blk += chnex -> cur_byt / blocksize; chnex -> cur_byt %= blocksize; alf = OZ_VDFS_ALF_M_MDT | OZ_VDFS_ALF_M_CDT | OZ_VDFS_ALF_M_ADT; vl = oz_hw_smplock_wait (&(file -> attrlock_vl)); if (iopex -> u.writerec.p.truncate || (chnex -> cur_blk > file -> attrlock_efblk) || ((chnex -> cur_blk == file -> attrlock_efblk) && (chnex -> cur_byt > file -> attrlock_efbyt))) { file -> attrlock_efblk = chnex -> cur_blk; file -> attrlock_efbyt = chnex -> cur_byt; alf |= OZ_VDFS_ALF_M_EOF; } file -> attrlock_date = oz_hw_tod_getnow (); file -> attrlock_flags |= alf; oz_hw_smplock_clr (&(file -> attrlock_vl), vl); if (alf & OZ_VDFS_ALF_M_EOF) oz_dev_vdfs_mark_header_dirty (file); } /* Copy the written length back to user's buffer */ if (iopex -> u.writerec.p.wlen != NULL) oz_hw_phys_movefromvirt (sizeof *(iopex -> u.writerec.p.wlen), &(iopex -> u.writerec.wlen), iopex -> u.writerec.wlen_phypages, iopex -> u.writerec.wlen_byteoffs); /* Post I/O request's completion and maybe start another recio on the file */ finishrecio (iopex, status, NULL); return (0); } /* Copy unwritten data bytes from user's buffer to cache page */ size = iopex -> u.writerec.p.size - iopex -> u.writerec.wlen; // this is how much of the user buffer we have yet to put if (size > dcmpb -> nbytes) size = dcmpb -> nbytes; // can't put more than cache is making available if (size > 0) { oz_hw_phys_movephys (size, iopex -> u.writerec.phypages, iopex -> u.writerec.byteoffs + iopex -> u.writerec.wlen, &(dcmpb -> phypage), dcmpb -> pageoffs); iopex -> u.writerec.wlen += size; // that much more has been written to cache dcmpb -> nbytes -= size; // remove from what cache has made available to us dcmpb -> pageoffs += size; modified = size; } /* Copy unwritten terminator bytes to cache page */ size = iopex -> u.writerec.p.trmsize - iopex -> u.writerec.trmwlen; // this is how much of the terminator buffer we have yet to put if (size > dcmpb -> nbytes) size = dcmpb -> nbytes; // can't put more than cache is making available if (size > 0) { if (iopex -> u.writerec.p.trmsize > sizeof iopex -> u.writerec.trmdata) { oz_hw_phys_movephys (size, iopex -> u.writerec.trmphypages, iopex -> u.writerec.trmbyteoffs + iopex -> u.writerec.trmwlen, &(dcmpb -> phypage), dcmpb -> pageoffs); } else { oz_hw_phys_movefromvirt (size, iopex -> u.writerec.trmdata + iopex -> u.writerec.trmwlen, &(dcmpb -> phypage), dcmpb -> pageoffs); } iopex -> u.writerec.trmwlen += size; // that much more has been written to cache dcmpb -> nbytes -= size; // remove from what cache has made available to us dcmpb -> pageoffs += size; modified += size; } /* Get more from cache if we haven't written it all yet. Else set nbytes=0 so oz_knl_dcache_map will know we're all done. */ size = iopex -> u.writerec.p.size + iopex -> u.writerec.p.trmsize; skip = iopex -> u.writerec.wlen + iopex -> u.writerec.trmwlen; iopex -> u.writerec.dcmpb.nbytes = size - skip; iopex -> u.writerec.dcmpb.virtblock = chnex -> cur_blk; iopex -> u.writerec.dcmpb.blockoffs = chnex -> cur_byt + skip; iopex -> u.writerec.status = oz_dev_vdfs_dcache_map_vbn_to_lbn (dcmpb, file); if (iopex -> u.writerec.status != OZ_SUCCESS) dcmpb -> nbytes = 0; /* Return number of bytes we modified in the cache page */ return (modified); } /************************************************************************/ /* */ /* Read record */ /* */ /* This is a shortcut routine, but there is only one request active */ /* per file at a time, as they go through file -> recio_q. */ /* */ /************************************************************************/ static uLong dc_readrec (OZ_Dcmpb *dcmpb, uLong status); static uLong sc_readrec (Iopex *iopex, Chnex *chnex, Devex *devex) { File *file; uLong blocksize, dcsize, sts, vl; /* Make sure we have read access to the file */ file = chnex -> file; sts = OZ_NOREADACCESS; if (!OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_SELF_READ)) goto rtnerr; /* If atblock specified, position there */ blocksize = devex -> blocksize; if (iopex -> u.readrec.p.atblock != 0) { chnex -> cur_blk = iopex -> u.readrec.p.atblock; chnex -> cur_byt = iopex -> u.readrec.p.atbyte; } chnex -> cur_blk += chnex -> cur_byt / blocksize; chnex -> cur_byt %= blocksize; /* If we're at or past the eof, return eof status */ vl = oz_hw_smplock_wait (&(file -> attrlock_vl)); iopex -> u.readrec.efblk = file -> attrlock_efblk; iopex -> u.readrec.efbyt = file -> attrlock_efbyt; oz_hw_smplock_clr (&(file -> attrlock_vl), vl); sts = OZ_ENDOFFILE; if (chnex -> cur_blk > iopex -> u.readrec.efblk) goto rtnerr; if ((chnex -> cur_blk == iopex -> u.readrec.efblk) && (chnex -> cur_byt >= iopex -> u.readrec.efbyt)) goto rtnerr; /* Read as much as we can from current position into caller's buffer */ iopex -> u.readrec.rlen = 0; // nothing read into user's buffer yet iopex -> u.readrec.trmseen = 0; // haven't seen any of the terminator yet iopex -> u.readrec.dcmpb.writing = 0; // this is a disk read operation iopex -> u.readrec.dcmpb.nbytes = iopex -> u.readrec.p.size + iopex -> u.readrec.p.trmsize; iopex -> u.readrec.dcmpb.virtblock = chnex -> cur_blk; // virtual block we want to start reading at iopex -> u.readrec.dcmpb.blockoffs = chnex -> cur_byt; // byte we want to start reading at iopex -> u.readrec.dcmpb.entry = dc_readrec; // read routine entrypoint iopex -> u.readrec.dcmpb.param = iopex; // read routine parameter iopex -> u.readrec.dcmpb.ix4kbuk = 0; iopex -> u.readrec.status = OZ_SUCCESS; // in case nbytes is zero sts = oz_dev_vdfs_dcache_map_vbn_to_lbn (&(iopex -> u.readrec.dcmpb), file); // map virtual-to-logical block if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_dcache_map_blocks (&(iopex -> u.readrec.dcmpb)); // process the request if (sts != OZ_STARTED) dc_readrec (&(iopex -> u.readrec.dcmpb), sts); return (OZ_STARTED); rtnerr: if (iopex -> u.readrec.p.trmsize > sizeof iopex -> u.readrec.trmdata) OZ_KNL_PGPFREE (iopex -> u.readrec.trmbuff); return (sts); } /************************************************************************/ /* */ /* The disk cache system calls this routine when it has access to the */ /* cache blocks for the disk */ /* */ /************************************************************************/ static uLong dc_readrec (OZ_Dcmpb *dcmpb, uLong status) { Chnex *chnex; const OZ_Mempage *phypages; File *file; Iopex *iopex; OZ_Dbn efblk, epblk, prefetch; OZ_Pagentry savepte; uByte *dcbuff, *p; uLong blocksize, efbyt, epbyt, skip, size, sts, vl; iopex = dcmpb -> param; chnex = iopex -> chnex; file = chnex -> file; blocksize = iopex -> devex -> blocksize; /* Maybe oz_knl_dcache_map is all done with dcmpb. If so, post request completion. */ if (status != OZ_PENDING) { if (status == OZ_SUCCESS) status = iopex -> u.readrec.status; chnex -> cur_byt += iopex -> u.readrec.rlen + iopex -> u.readrec.trmseen; chnex -> cur_blk += chnex -> cur_byt / blocksize; chnex -> cur_byt %= blocksize; if (iopex -> u.readrec.p.trmsize > sizeof iopex -> u.readrec.trmdata) OZ_KNL_PGPFREE (iopex -> u.readrec.trmbuff); if (!(file -> volume -> mountflags & OZ_FS_MOUNTFLAG_READONLY)) { vl = oz_hw_smplock_wait (&(file -> attrlock_vl)); file -> attrlock_date = oz_hw_tod_getnow (); file -> attrlock_flags |= OZ_VDFS_ALF_M_ADT; oz_hw_smplock_clr (&(file -> attrlock_vl), vl); } if (iopex -> u.readrec.p.rlen != NULL) oz_hw_phys_movefromvirt (sizeof *(iopex -> u.readrec.p.rlen), &(iopex -> u.readrec.rlen), iopex -> u.readrec.rlen_phypages, iopex -> u.readrec.rlen_byteoffs); #if 000 prefetch = 0; if (status == OZ_SUCCESS) { dcmpb -> nbytes = 1; if (oz_dev_vdfs_dcache_map_vbn_to_lbn (dcmpb, chnex -> file) == OZ_SUCCESS) prefetch = dcmpb -> logblock; } #endif finishrecio (iopex, status, NULL); #if 000 if (prefetch != 0) oz_knl_dcache_prefetch (iopex -> devex -> dcache, prefetch, 0); #endif return (0); } /* Chop nbytes off at the end-of-file */ skip = iopex -> u.readrec.rlen - iopex -> u.readrec.trmseen; // calculate where nbytes of the cache would put us epblk = chnex -> cur_blk; epbyt = chnex -> cur_byt + skip + dcmpb -> nbytes; epblk += epbyt / blocksize; epbyt %= blocksize; efblk = iopex -> u.readrec.efblk; // get end-of-file pointer efbyt = iopex -> u.readrec.efbyt; if ((epblk > efblk) || ((epblk == efblk) && (epbyt > efbyt))) { // if nbytes goes beyond eof ... dcmpb -> nbytes = (efblk - chnex -> cur_blk) * blocksize + efbyt - chnex -> cur_byt - skip; // just read up to the eof if (dcmpb -> nbytes == 0) { skip = iopex -> u.readrec.rlen + iopex -> u.readrec.byteoffs; phypages = iopex -> u.readrec.phypages; if (iopex -> u.readrec.rlen + iopex -> u.readrec.trmseen <= iopex -> u.readrec.p.size) { iopex -> u.readrec.rlen += iopex -> u.readrec.trmseen; iopex -> u.readrec.status = OZ_ENDOFFILE; // return end-of-file as there is nothing more to read } else { iopex -> u.readrec.trmseen = iopex -> u.readrec.p.size - iopex -> u.readrec.rlen; iopex -> u.readrec.rlen = iopex -> u.readrec.p.size; // return success so they come back and // get the rest of partial terminator } oz_hw_phys_movefromvirt (iopex -> u.readrec.trmseen, iopex -> u.readrec.trmbuff, phypages, skip); iopex -> u.readrec.trmseen = 0; // partial terminator was put in data buffer goto rtn; } } /* Maybe this is a continuation of a multi-byte terminator sequence */ /* iopex -> u.readrec.trmseen has how many bytes have been matched so far */ if (iopex -> u.readrec.trmseen != 0) { // see if part of terminator seen last time size = iopex -> u.readrec.p.trmsize - iopex -> u.readrec.trmseen; // get how much of the terminator we have yet to find if (size > dcmpb -> nbytes) size = dcmpb -> nbytes; // ... but we won't find more than what cache has given us dcbuff = oz_hw_phys_mappage (dcmpb -> phypage, &savepte); // point to cache buffer data dcbuff += dcmpb -> pageoffs; // offset to the byte we want to start with p = iopex -> u.readrec.trmbuff + iopex -> u.readrec.trmseen; // point to what's left of terminator to match for (sts = 0; sts < size; sts ++) if (*(dcbuff ++) != *(p ++)) break; // see how much of it matches oz_hw_phys_unmappage (savepte); iopex -> u.readrec.trmseen += sts; // this much more of the terminator has been matched up if (iopex -> u.readrec.trmseen == iopex -> u.readrec.p.trmsize) { // read is complete if whole terminator found iopex -> u.readrec.status = OZ_SUCCESS; goto readdone; } if (sts < size) { iopex -> u.readrec.trmseen = 0; // terminator broken, forget about it skip = iopex -> u.readrec.rlen + iopex -> u.readrec.byteoffs; phypages = iopex -> u.readrec.phypages; oz_hw_phys_movefromvirt (1, iopex -> u.readrec.trmbuff, phypages, skip); // but copy first byte to data buffer iopex -> u.readrec.rlen ++; } goto continuereading; } /* See how much of what it gave will fit in user data buffer */ size = iopex -> u.readrec.p.size - iopex -> u.readrec.rlen; if (size > dcmpb -> nbytes) size = dcmpb -> nbytes; /* If no terminator specified, copy all of it to user buffer */ if (iopex -> u.readrec.p.trmsize == 0) { oz_hw_phys_movephys (size, &(dcmpb -> phypage), dcmpb -> pageoffs, iopex -> u.readrec.phypages, iopex -> u.readrec.byteoffs + iopex -> u.readrec.rlen); iopex -> u.readrec.rlen += size; // accumulate whatever was read in if (iopex -> u.readrec.rlen < iopex -> u.readrec.p.size) goto continuereading; // continue reading if we haven't filled buffer } /* Terminator processing */ else { dcbuff = oz_hw_phys_mappage (dcmpb -> phypage, &savepte); // point to cache buffer data we just copied from dcbuff += dcmpb -> pageoffs; p = memchr (dcbuff, iopex -> u.readrec.trmbuff[0], size); // search for the first byte of the terminator while (p != NULL) { sts = dcbuff + dcmpb -> nbytes - p; // this is how much remains at first terminator byte if (sts > iopex -> u.readrec.p.trmsize) sts = iopex -> u.readrec.p.trmsize; // only match up to whole terminator if (memcmp (p, iopex -> u.readrec.trmbuff, sts) == 0) break; // stop if successful comparison p = memchr (p + 1, iopex -> u.readrec.trmbuff[0], sts - 1); // mismatch, look for first byte again } oz_hw_phys_unmappage (savepte); // unmap cache page if (p == NULL) { oz_hw_phys_movephys (size, &(dcmpb -> phypage), dcmpb -> pageoffs, iopex -> u.readrec.phypages, iopex -> u.readrec.byteoffs + iopex -> u.readrec.rlen); iopex -> u.readrec.rlen += size; // no match found, so it is all data if (iopex -> u.readrec.rlen < iopex -> u.readrec.p.size) goto continuereading; // continue reading iopex -> u.readrec.status = OZ_NOTERMINATOR; // user buffer filled with no terminator, return semi-error status goto readdone; } size = p - dcbuff; // length up to but not including start of terminator oz_hw_phys_movephys (size, &(dcmpb -> phypage), dcmpb -> pageoffs, iopex -> u.readrec.phypages, iopex -> u.readrec.byteoffs + iopex -> u.readrec.rlen); iopex -> u.readrec.rlen += size; iopex -> u.readrec.trmseen = sts; // set the size matched so far if (sts < iopex -> u.readrec.p.trmsize) goto continuereading; // haven't found the whole thing, continue reading } iopex -> u.readrec.status = OZ_SUCCESS; goto readdone; /* Request requires more data to complete */ continuereading: size = iopex -> u.readrec.p.size + iopex -> u.readrec.p.trmsize; skip = iopex -> u.readrec.rlen + iopex -> u.readrec.trmseen; dcmpb -> nbytes = size - skip; dcmpb -> virtblock = chnex -> cur_blk; dcmpb -> blockoffs = chnex -> cur_byt + skip; iopex -> u.readrec.status = oz_dev_vdfs_dcache_map_vbn_to_lbn (dcmpb, file); if (iopex -> u.readrec.status == OZ_SUCCESS) goto rtn; /* Read request is complete */ readdone: dcmpb -> nbytes = 0; rtn: return (0); } /************************************************************************/ /* */ /* Get information part 1 */ /* */ /************************************************************************/ static uLong sc_getinfo1 (Iopex *iopex, Chnex *chnex, Devex *devex) { OZ_VDFS_File *file; uLong clusterfactor, sts; sts = startshortcut (chnex, devex, iopex); // block file from being closed if (sts == OZ_SUCCESS) { file = chnex -> file; if (file == NULL) sts = OZ_FILENOTOPEN; // make sure a file is open else { // ok, get info about the file /* Get fs independent info */ iopex -> u.getinfo1.p.blocksize = devex -> blocksize; iopex -> u.getinfo1.p.eofblock = file -> attrlock_efblk; iopex -> u.getinfo1.p.eofbyte = file -> attrlock_efbyt; iopex -> u.getinfo1.p.hiblock = file -> allocblocks; iopex -> u.getinfo1.p.curblock = chnex -> cur_blk; iopex -> u.getinfo1.p.curbyte = chnex -> cur_byt; iopex -> u.getinfo1.p.secattrsize = oz_knl_secattr_getsize (file -> secattr); /* If caching enabled and cluster factor is a multiple of page size (so file */ /* pages align with cache pages), we can give direct access to cache pages. */ if ((devex -> dcache != NULL) && ((clusterfactor = devex -> volume -> clusterfactor) != 0) && (((clusterfactor * devex -> blocksize) & ((1 << OZ_HW_L2PAGESIZE) - 1)) == 0)) { iopex -> u.getinfo1.p.knlpfmap = vdfs_knlpfmap; iopex -> u.getinfo1.p.knlpfupd = vdfs_knlpfupd; iopex -> u.getinfo1.p.knlpfrel = vdfs_knlpfrel; } /* Fill in fs-dependent info */ (*(devex -> vector -> getinfo1)) (iopex); /* Store block back to caller */ movc4 (sizeof iopex -> u.getinfo1.p, &(iopex -> u.getinfo1.p), iopex -> as, iopex -> ap); } finishortcut (chnex, devex, iopex); // allow a close to proceed } return (sts); } /************************************************************************/ /* */ /* Read next entry from directory that is open on channel */ /* */ /************************************************************************/ static uLong sc_readdir (Iopex *iopex, Chnex *chnex, Devex *devex) { OZ_IO_fs_readdir fs_readdir; fs_readdir = iopex -> u.readdir.p; // copy the input parameter block memset (&(iopex -> u.wildscan.p), 0, sizeof iopex -> u.wildscan.p); // clear out the wildscan param block iopex -> u.wildscan.p.init = (chnex -> wildscan == NULL); // init if there is no scanning going on there iopex -> u.wildscan.p.size = fs_readdir.filenamsize; // where to return the filename found iopex -> u.wildscan.p.buff = fs_readdir.filenambuff; iopex -> u.wildscan.p.fileidsize = fs_readdir.fileidsize; // where to return the fileid found iopex -> u.wildscan.p.fileidbuff = fs_readdir.fileidbuff; iopex -> u.wildscan.p.subs = fs_readdir.filenamsubs; // where to return substring sizes return (sc_wildscan (iopex, chnex, devex)); // process as a wildscan function } /************************************************************************/ /* */ /* Get security attributes */ /* */ /************************************************************************/ static uLong be_getsecattr (Iopex *iopex, Chnex *chnex, Devex *devex) { File *file; uLong size; file = chnex -> file; if (file == NULL) return (OZ_FILENOTOPEN); size = oz_knl_secattr_getsize (file -> secattr); if (size > iopex -> u.getsecattr.p.size) return (OZ_BUFFEROVF); memcpy (iopex -> u.getsecattr.p.buff, oz_knl_secattr_getbuff (file -> secattr), size); if (iopex -> u.getsecattr.p.rlen != NULL) *(iopex -> u.getsecattr.p.rlen) = size; return (OZ_SUCCESS); } /************************************************************************/ /* */ /* Write boot block */ /* */ /************************************************************************/ static uLong be_writeboot (Iopex *iopex, Chnex *chnex, Devex *devex) { uByte *bootblock; File *file; uLong sts; OZ_Dbn bb_nblocks, bb_logblock, eofblock, logblock, nblocks, part_logblock; OZ_IO_disk_getinfo1 disk_getinfo1, host_getinfo1; OZ_Iochan *partiochan; file = chnex -> file; if (file == NULL) return (OZ_FILENOTOPEN); if (IS_DIRECTORY (file)) return (OZ_FILEISADIR); if (!OZ_LOCK_ALLOW_TEST (chnex -> lockmode, OZ_LOCK_ALLOWS_SELF_WRITE)) return (OZ_NOWRITEACCESS); /* Get info about disk drive */ memset (&disk_getinfo1, 0, sizeof disk_getinfo1); sts = diskio (OZ_IO_DISK_GETINFO1, sizeof disk_getinfo1, &disk_getinfo1, iopex, devex -> master_iochan); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_vdfs: error %u getting disk info\n", sts); return (sts); } /* Get number of blocks in the hardware boot block */ sts = oz_hw_bootblock_nblocks (&disk_getinfo1, devex -> master_iochan, &bb_nblocks, &bb_logblock); if (sts != OZ_SUCCESS) return (sts); /* Make sure loader image file has just one pointer past its copy of the bootblock(s) (ie, it is contiguous), and get starting logical block number */ eofblock = file -> attrlock_efblk; if (file -> attrlock_efbyt == 0) eofblock --; sts = (*(devex -> vector -> map_vbn_to_lbn)) (file, 1 + bb_nblocks, &nblocks, &logblock); if ((sts == OZ_SUCCESS) && (nblocks < eofblock - bb_nblocks)) sts = OZ_FILENOTCONTIG; if (sts != OZ_SUCCESS) return (sts); /* If disk is partition of another disk, relocate lbn by the starting block of the partition */ /* 'logblock' must be an absolute value for the physical disk drive */ /* Also get sec/trk/cyl of physical drive */ part_logblock = disk_getinfo1.parthoststartblock; host_getinfo1 = disk_getinfo1; while (host_getinfo1.parthostdevname[0] != 0) { /* if no partition host, we're done scanning */ /* note: linux_dev_disk driver returns a parthoststartblock */ /* without a parthostdevname implying that the contents */ /* are at sometime in the future going to be copied to */ /* a partitioned disk */ sts = oz_knl_iochan_crbynm (host_getinfo1.parthostdevname, OZ_LOCKMODE_NL, OZ_PROCMODE_KNL, NULL, &partiochan); /* assign channel to host disk device */ if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_vdfs: error %u assigning channel to partition host disk %s\n", sts, disk_getinfo1.parthostdevname); return (sts); } memset (&host_getinfo1, 0, sizeof host_getinfo1); /* find out about it */ sts = diskio (OZ_IO_DISK_GETINFO1, sizeof host_getinfo1, &host_getinfo1, iopex, partiochan); if (sts != OZ_SUCCESS) { /* abort if error getting info */ oz_dev_vdfs_printk (iopex, "oz_dev_vdfs: error %u getting info for disk %s\n", sts, oz_knl_iochan_getdevunit (partiochan)); oz_knl_iochan_increfc (partiochan, -1); return (sts); } oz_knl_iochan_increfc (partiochan, -1); /* deassign channel to host disk */ } /* repeat in case of nested partitioning */ /* A copy of the boot block is the first block of the image file. So read it from loader image file, modify it then write it to actual bootblock. */ bootblock = OZ_KNL_PGPMALLOQ (bb_nblocks * disk_getinfo1.blocksize); /* allocate a temp buffer for bootblock(s) */ if (bootblock == NULL) return (OZ_EXQUOTAPGP); sts = oz_dev_vdfs_readvirtblock (file, 1, 0, bb_nblocks * disk_getinfo1.blocksize, bootblock, iopex, 1); /* read boot block image */ if (sts == OZ_SUCCESS) { sts = oz_hw_bootblock_modify (bootblock, nblocks, logblock, part_logblock, &(iopex -> u.writeboot.p), &disk_getinfo1, &host_getinfo1, devex -> master_iochan); if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_writelogblock (bb_logblock, 0, bb_nblocks * disk_getinfo1.blocksize, bootblock, 0, iopex); /* write to volume's actual boot block */ } OZ_KNL_PGPFREE (bootblock); /* free off temp buffer */ return (sts); } /************************************************************************/ /* */ /* Set current file position */ /* */ /************************************************************************/ static uLong be_setcurpos (Iopex *iopex, Chnex *chnex, Devex *devex) { File *file; uLong blocksize; file = chnex -> file; if (file == NULL) return (OZ_FILENOTOPEN); if (IS_DIRECTORY (file)) return (OZ_FILEISADIR); blocksize = devex -> blocksize; chnex -> cur_blk = iopex -> u.setcurpos.p.atblock; chnex -> cur_byt = iopex -> u.setcurpos.p.atbyte; chnex -> cur_blk += chnex -> cur_byt / blocksize; chnex -> cur_byt %= blocksize; return (OZ_SUCCESS); } /************************************************************************/ /* */ /* Wildcard scanner */ /* */ /* This is a shortcut routine that operates outside the thread so it */ /* must provide its own synchronization */ /* */ /* Some examples: */ /* */ /* "/~" means the whole disk, files and directories */ /* "/~;*" means all files on the whole disk */ /* "/~/" means all directories on the whole disk */ /* "/*" means all files and directories in the root directory */ /* "/*;*" means all files in the root directory */ /* */ /* The version number can be: */ /* */ /* 1) A positive integer meaning exactly that number */ /* 3) A ';0' or just a ';', meaning the latest version */ /* 4) A ';*' meaning all versions (same as no ';' at all, except */ /* it excludes directories) */ /* 5) A negative integer meaning that many back from latest */ /* */ /************************************************************************/ static uLong sc_wildscan (Iopex *iopex, Chnex *chnex, Devex *devex) { char c, *p, *q, *r; File *dirfile; int usedup; OZ_IO_fs_open fs_open; uLong sts, vl; Volume *volume; Wildscan *wildscan; volume = devex -> volume; /* Declare this as a shortcut-in-progress on the channel. This will block the be_close routine from closing this channel */ /* up. But be_close won't let us queue any I/O's to open or read directories, so it shouldn't be blocked for very long. */ sts = startshortcut (chnex, devex, iopex); if (sts != OZ_SUCCESS) return (sts); /* Store iopex in channel as current wildcard operation */ /* There can be only one going on a channel so we don't have to make a queue */ vl = oz_hw_smplock_wait (&(devex -> smplock_vl)); // lock wild_iopex if (chnex -> wild_iopex != NULL) { // see if there is an I/O going on it oz_hw_smplock_clr (&(devex -> smplock_vl), vl); // if so, release lock finishortcut (chnex, devex, iopex); return (OZ_CHANNELBUSY); // ... and return error status } chnex -> wild_iopex = iopex; // none going, say the scan is in progress oz_hw_smplock_clr (&(devex -> smplock_vl), vl); // release lock /* Maybe initialize wildcard search context */ wildscan = chnex -> wildscan; chnex -> wild_nested = 0; if (iopex -> u.wildscan.p.init) { /* If there is an existing wildscan search context, tell them to close it */ sts = OZ_FILEALREADYOPEN; if (wildscan != NULL) goto rtnerrnf; /* Create a new wildcard context */ sts = OZ_EXQUOTAPGP; wildscan = OZ_KNL_PGPMALLOQ (sizeof *wildscan + volume -> dirblocksize); if (wildscan == NULL) goto rtnerrnf; memset (wildscan, 0, sizeof *wildscan); /* If a wild spec is given, use it to open the directory and start scanning from there */ if (iopex -> u.wildscan.p.wild != NULL) { strncpyz (wildscan -> lastname, iopex -> u.wildscan.p.wild, si