home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 354.lha / MSH_v1.5 / src / hanfile.c < prev    next >
C/C++ Source or Header  |  1990-03-12  |  20KB  |  867 lines

  1. /*-
  2.  * $Id: hanfile.c,v 1.6 90/02/10 21:38:26 Rhialto Exp $
  3.  * $Log:    hanfile.c,v $
  4.  * Revision 1.6  90/02/10  21:38:26  Rhialto
  5.  * Optimized 12-bit fat unpacking.
  6.  * 
  7.  * Revision 1.5  90/01/27  20:26:51  Rhialto
  8.  * Fixed ATTR_ARCHIVED bit in MSWrite()
  9.  *
  10.  * Revision 1.4  90/01/23  02:32:23  Rhialto
  11.  * Add 16-bit FAT support.
  12.  *
  13.  * Revision 1.3  90/01/23  00:39:04  Rhialto
  14.  * Always return -1 on MSWrite error.
  15.  *
  16.  * Revision 1.2  89/12/17  23:04:39  Rhialto
  17.  * Add ATTR_READONLY support
  18.  *
  19.  * Revision 1.1  89/12/17  20:03:11  Rhialto
  20.  * Initial revision
  21.  *
  22.  * HANFILE.C
  23.  *
  24.  * The code for the messydos file system handler.
  25.  *
  26.  * This parts handles files and the File Allocation Table.
  27.  *
  28.  * This code is (C) Copyright 1989,1990 by Olaf Seibert. All rights reserved.
  29.  * May not be used or copied without a licence.
  30. -*/
  31.  
  32. #include "han.h"
  33. #include "dos.h"
  34.  
  35. #ifdef DEBUG
  36. #   define    debug(x)  dbprintf x
  37. #else
  38. #   define    debug(x)
  39. #endif
  40.  
  41. extern char    DotDot[1 + 8 + 3];
  42.  
  43. /*
  44.  * Read the FAT from the disk, and count the free clusters.
  45.  */
  46.  
  47. int
  48. GetFat()
  49. {
  50.     int         i;
  51.     byte       *secptr;
  52.  
  53.     if (!Fat && !(Fat = AllocMem((long) Disk.bps * Disk.spf, BufMemType))) {
  54.     debug(("No memory for FAT\n"));
  55.     return 0;
  56.     }
  57.     FatDirty = FALSE;
  58.     for (i = 0; i < Disk.spf; i++) {
  59.     if (secptr = GetSec(Disk.res + i)) {
  60.         CopyMem(secptr, Fat + i * Disk.bps, (long) Disk.bps);
  61.         FreeSec(secptr);
  62.     } else {
  63.         /* q&d way to set the entire FAT to FAT_EOF */
  64.         setmem(Fat + i * Disk.bps, (int) Disk.bps, FAT_EOF);        /* 0xFF */
  65.     }
  66.     }
  67.  
  68.     debug(("counting free clusters\n"));
  69.  
  70.     Disk.nsectsfree = 0;
  71.     for (i = MS_FIRSTCLUST; i <= Disk.maxclst; i++) {
  72.     if (GetFatEntry((word) i) == FAT_UNUSED)
  73.         Disk.nsectsfree += Disk.spc;
  74.     }
  75.  
  76.     return 1;
  77. }
  78.  
  79. void
  80. FreeFat()
  81. {
  82.     if (Fat) {
  83.     FreeMem(Fat, (long) Disk.bps * Disk.spf);
  84.     Fat = NULL;
  85.     FatDirty = FALSE;
  86.     }
  87. }
  88.  
  89. /*-
  90.  *  The FAT consists of 12-bits entries for each cluster,
  91.  *  indicating the next cluster in the chain, or FFF for EOF.
  92.  *
  93.  *  Every two entries are packed in three bytes, like this:
  94.  *
  95.  *  Two entries     abc  123 (for one cluster and the next)
  96.  *  are packed as    bc 3a 12
  97. -*/
  98.  
  99. word
  100. GetFatEntry(cluster)
  101. word        cluster;
  102. {
  103.     if (!Fat && !GetFat())
  104.     return FAT_EOF;
  105.  
  106.     if (Disk.fat16bits) {
  107.     return OtherEndianWord(((word *)Fat)[cluster]);
  108.     } else {
  109.     register int    offset = 3 * (cluster / 2);
  110.     register word    twoentries;
  111.  
  112.     if (cluster & 1) {
  113.         twoentries = Fat[offset + 1] >> 4;
  114.         twoentries |= Fat[offset + 2] << 4;
  115.     } else {
  116.         twoentries = Fat[offset];
  117.         twoentries |= (Fat[offset + 1] & 0x0F) << 8;
  118.     }
  119.  
  120.     /*
  121.      * Convert the special values 0xFF0 .. 0xFFF to 16 bits so they
  122.      * can be checked consistently.
  123.      */
  124.     if (twoentries >= 0xFF0)
  125.         twoentries |= 0xF000;
  126.  
  127.     return twoentries;
  128.     }
  129. }
  130.  
  131. #ifndef READONLY
  132.  
  133. void
  134. SetFatEntry(cluster, value)
  135. word        cluster;
  136. word        value;
  137. {
  138.     if (!Fat && !GetFat())
  139.     return;
  140.  
  141.     if (Disk.fat16bits) {
  142.     ((word *)Fat)[cluster] = OtherEndianWord(value);
  143.     } else {
  144.     register int    offset = 3 * (cluster / 2);
  145.  
  146.     if (cluster & 1) {          /* 123 kind of entry */
  147.         Fat[offset + 2] = value >> 4;
  148.         Fat[offset + 1] &= 0x0F;
  149.         Fat[offset + 1] |= (value & 0x0F) << 4;
  150.     } else {            /* abc kind of entry */
  151.         Fat[offset + 0] = value;
  152.         Fat[offset + 1] &= 0xF0;
  153.         Fat[offset + 1] |= (value >> 8) & 0x0F;
  154.     }
  155.     }
  156.  
  157.     FatDirty = TRUE;
  158. }
  159.  
  160. /*
  161.  * Find a free cluster to install as the one following this one. Start
  162.  * looking for it right after the given one, so we allocate the cluster
  163.  * chain as contiguous as possible. If we run off the end of the disk, we
  164.  * start again at the beginning. The termination test should not be
  165.  * necessary (and won't work if we are given MSFIRSTCLUST - 1) but won't
  166.  * harm either.
  167.  */
  168.  
  169. word
  170. FindFreeCluster(prev)
  171. word        prev;
  172. {
  173.     register word   i;
  174.  
  175.     if (prev == 0 || prev == FAT_EOF)
  176.     prev = MS_FIRSTCLUST - 1;
  177.  
  178.     if (Disk.nsectsfree >= Disk.spc) {
  179.     for (i = prev + 1; i != prev; i++) {
  180.         if (i > Disk.maxclst)       /* Wrap around */
  181.         i = MS_FIRSTCLUST;
  182.         if (GetFatEntry(i) == FAT_UNUSED) {
  183.         SetFatEntry(i, FAT_EOF);
  184.         if (prev >= MS_FIRSTCLUST)
  185.             SetFatEntry(prev, i);
  186.         Disk.nsectsfree -= Disk.spc;
  187.         return i;
  188.         }
  189.     }
  190.     }
  191.     return FAT_EOF;
  192. }
  193.  
  194. /*
  195.  * Add a cluster to a cluster chain. For input, we get some cluster we
  196.  * know that is on the chain, even if it is the first one.
  197.  */
  198.  
  199. word
  200. ExtendClusterChain(cluster)
  201. register word    cluster;
  202. {
  203.     register word   nextcluster;
  204.  
  205.     /*
  206.      * Find the end of the cluster chain to tack the new cluster on to.
  207.      * Then FindFreeCluster will (or won't) extend the chain for us.
  208.      */
  209.     if (cluster != 0)
  210.     while ((nextcluster = NextCluster(cluster)) != FAT_EOF) {
  211.         cluster = nextcluster;
  212.     }
  213.  
  214.     return FindFreeCluster(cluster);
  215. }
  216.  
  217. /*
  218.  * Free a chain of clusters by setting their FAT entries to FAT_UNUSED.
  219.  */
  220.  
  221. void
  222. FreeClusterChain(cluster)
  223. register word    cluster;
  224. {
  225.     register word   nextcluster;
  226.  
  227.     while (cluster != FAT_EOF) {
  228.     nextcluster = NextCluster(cluster);
  229.     SetFatEntry(cluster, FAT_UNUSED);
  230.     Disk.nsectsfree += Disk.spc;
  231.     cluster = nextcluster;
  232.     }
  233. }
  234.  
  235. #endif                /* READONLY */
  236.  
  237. /*
  238.  * This routine opens a file.
  239.  */
  240.  
  241. struct MSFileHandle *
  242. MSOpen(parentdir, name, mode)
  243. struct MSFileLock *parentdir;
  244. char           *name;
  245. long        mode;
  246. {
  247.     struct MSFileLock *fl;
  248.     struct MSFileHandle *fh = NULL;
  249.     long        lockmode;
  250.  
  251.     switch (mode) {
  252.     case MODE_NEWFILE:
  253.     case MODE_READWRITE:
  254.     lockmode = EXCLUSIVE_LOCK ^ MODE_CREATEFILE;
  255.     break;
  256.     default:
  257.     mode = MODE_OLDFILE;
  258.     case MODE_OLDFILE:
  259.     lockmode = SHARED_LOCK;
  260.     }
  261.  
  262.     if (fl = MSLock(parentdir, name, lockmode)) {
  263. makefh:
  264.     if (fl->msfl_Msd.msd_Attributes & ATTR_DIR) {
  265.         error = ERROR_OBJECT_WRONG_TYPE;
  266.         MSUnLock(fl);
  267.     } else if (fh = AllocMem((long) sizeof (*fh), MEMF_PUBLIC)) {
  268. #ifndef READONLY
  269.         /* Do we need to truncate the file? */
  270.         if (mode == MODE_NEWFILE && fl->msfl_Msd.msd_Cluster) {
  271.         FreeClusterChain(fl->msfl_Msd.msd_Cluster);
  272.         fl->msfl_Msd.msd_Cluster = 0;
  273.         fl->msfl_Msd.msd_Filesize = 0;
  274.         UpdateFileLock(fl);
  275.         }
  276. #endif
  277.         fh->msfh_Cluster = fl->msfl_Msd.msd_Cluster;
  278.         fh->msfh_SeekPos = 0;
  279.         fh->msfh_FileLock = fl;
  280.     } else {
  281.         error = ERROR_NO_FREE_STORE;
  282.         MSUnLock(fl);
  283.     }
  284.     return fh;
  285.     }
  286. #ifndef READONLY
  287.     /*
  288.      * If the file was not found, see if we can make a new one. Therefore
  289.      * we need to have an empty spot in the desired directory, and create
  290.      * an MSFileLock for it.
  291.      */
  292.  
  293.     if (!(lockmode & MODE_CREATEFILE) && (fl = EmptyFileLock)) {
  294.     debug(("Creating new file\n"));
  295.     EmptyFileLock = NULL;
  296.     fl->msfl_Msd.msd_Attributes = ATTR_ARCHIVED;
  297.     UpdateFileLock(fl);
  298.  
  299.     goto makefh;
  300.     }
  301.     if (EmptyFileLock) {
  302.     MSUnLock(EmptyFileLock);
  303.     EmptyFileLock = NULL;
  304.     }
  305. #endif
  306.  
  307.     return NULL;
  308. }
  309.  
  310. void
  311. MSClose(fh)
  312. register struct MSFileHandle *fh;
  313. {
  314.     if (fh) {
  315.     MSUnLock(fh->msfh_FileLock);
  316.     FreeMem(fh, (long) sizeof (*fh));
  317.     }
  318. }
  319.  
  320. long
  321. MSSeek(fh, position, mode)
  322. struct MSFileHandle *fh;
  323. long        position;
  324. long        mode;
  325. {
  326.     long        oldpos = fh->msfh_SeekPos;
  327.     long        newpos = oldpos;
  328.     long        filesize = fh->msfh_FileLock->msfl_Msd.msd_Filesize;
  329.     word        cluster = fh->msfh_Cluster;
  330.     word        oldcluster;
  331.     word        newcluster;
  332.  
  333.     switch (mode) {
  334.     case OFFSET_BEGINNING:
  335.     newpos = position;
  336.     break;
  337.     case OFFSET_CURRENT:
  338.     newpos += position;
  339.     break;
  340.     case OFFSET_END:
  341.     newpos = filesize - position;
  342.     break;
  343.     }
  344.  
  345.     if (newpos < 0 || newpos > filesize) {
  346.     error = ERROR_SEEK_ERROR;
  347.     return -1;
  348.     }
  349.     newcluster = newpos / Disk.bpc;
  350.     oldcluster = oldpos / Disk.bpc;
  351.  
  352.     if (oldcluster > newcluster) {      /* Seek backwards */
  353.     cluster = fh->msfh_FileLock->msfl_Msd.msd_Cluster;
  354.     oldcluster = 0;
  355.     }
  356.     if (oldcluster < newcluster) {
  357.     if (CheckLock(fh->msfh_FileLock))
  358.         return -1L;
  359.     while (oldcluster < newcluster) {
  360.         cluster = NextCluster(cluster);
  361.         oldcluster++;
  362.     }
  363.     }
  364.     fh->msfh_Cluster = cluster;
  365.     fh->msfh_SeekPos = newpos;
  366.  
  367.     return oldpos;
  368. }
  369.  
  370. long
  371. MSRead(fh, userbuffer, size)
  372. register struct MSFileHandle *fh;
  373. register byte  *userbuffer;
  374. register long    size;
  375. {
  376.     long        oldsize;
  377.  
  378.     if (CheckLock(fh->msfh_FileLock))
  379.     return -1L;
  380.  
  381.     if (fh->msfh_SeekPos + size > fh->msfh_FileLock->msfl_Msd.msd_Filesize)
  382.     size = fh->msfh_FileLock->msfl_Msd.msd_Filesize - fh->msfh_SeekPos;
  383.  
  384.     oldsize = size;
  385.  
  386.     while (size > 0) {
  387.     word        offset;
  388.     word        sector;
  389.     byte           *diskbuffer;
  390.     long        insector;
  391.     long        tocopy;
  392.  
  393.     offset = fh->msfh_SeekPos % Disk.bpc;
  394.     sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset);
  395.     if (diskbuffer = GetSec(sector)) {
  396.         offset %= Disk.bps;
  397.         insector = Disk.bps - offset;
  398.         tocopy = lmin(size, insector);
  399.  
  400.         CopyMem(diskbuffer + offset, userbuffer, tocopy);
  401.         userbuffer += tocopy;
  402.         size -= tocopy;
  403.         FreeSec(diskbuffer);
  404.         /* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */
  405.         if ((fh->msfh_SeekPos += tocopy) % Disk.bpc == 0)
  406.         fh->msfh_Cluster = NextCluster(fh->msfh_Cluster);
  407.     } else {        /* Read error. Return amount successfully
  408.                  * read, if any. Else return -1 for error. */
  409.         if (size == oldsize) {
  410.         return -1L;
  411.         }
  412.         return oldsize - size;
  413.     }
  414.     }
  415.  
  416.     return oldsize;
  417. }
  418.  
  419. long
  420. MSWrite(fh, userbuffer, size)
  421. register struct MSFileHandle *fh;
  422. register byte  *userbuffer;
  423. register long    size;
  424. {
  425. #ifdef READONLY
  426.     return -1;
  427. #else
  428.     long        oldsize;
  429.     struct MSFileLock *fl = fh->msfh_FileLock;
  430.     word        prevclust = fl->msfl_Msd.msd_Cluster;
  431.     word        update = 0;
  432.  
  433.     if (CheckLock(fl))
  434.     return -1;
  435.  
  436.     if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) {
  437.     error = ERROR_WRITE_PROTECTED;
  438.     return -1;
  439.     }
  440.  
  441.     oldsize = size;
  442.  
  443.     while (size > 0) {
  444.     /*
  445.      * Do we need to extend the file?
  446.      */
  447.  
  448.     if (fh->msfh_Cluster == 0 || fh->msfh_Cluster == FAT_EOF) {
  449.         word        newclust;
  450.  
  451.         newclust = ExtendClusterChain(prevclust);
  452.         debug(("Extend with %d\n", newclust));
  453.         if (newclust != FAT_EOF) {
  454.         if (prevclust == 0) {   /* Record first cluster in dir */
  455.             fl->msfl_Msd.msd_Cluster = newclust;
  456.         }
  457.         fh->msfh_Cluster = newclust;
  458.         prevclust = newclust;
  459.         } else {
  460.         error = ERROR_DISK_FULL;
  461.         goto error;
  462.         }
  463.     }
  464.     {
  465.         word        offset;
  466.         word        sector;
  467.         byte       *diskbuffer;
  468.         long        insector;
  469.         long        tocopy;
  470.  
  471.         offset = fh->msfh_SeekPos % Disk.bpc;
  472.         sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset);
  473.         offset %= Disk.bps;
  474.         insector = Disk.bps - offset;
  475.         tocopy = lmin(size, insector);
  476.  
  477.         if (tocopy == Disk.bps)
  478.         diskbuffer = EmptySec(sector);
  479.         else
  480.         diskbuffer = GetSec(sector);
  481.  
  482.         if (diskbuffer != NULL) {
  483.         CopyMem(userbuffer, diskbuffer + offset, tocopy);
  484.         userbuffer += tocopy;
  485.         size -= tocopy;
  486.         MarkSecDirty(diskbuffer);
  487.         FreeSec(diskbuffer);
  488.         /* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */
  489.         if ((fh->msfh_SeekPos += tocopy) % Disk.bpc == 0)
  490.             fh->msfh_Cluster = NextCluster(fh->msfh_Cluster);
  491.         if (fh->msfh_SeekPos > fl->msfl_Msd.msd_Filesize)
  492.             fl->msfl_Msd.msd_Filesize = fh->msfh_SeekPos;
  493.         fl->msfl_Msd.msd_Attributes |= ATTR_ARCHIVED;
  494.         update = 1;
  495.         } else {        /* Write error. */
  496.     error:
  497.         if (update)
  498.             UpdateFileLock(fl);
  499. #if 1
  500.         return -1;    /* We loose the information about how much
  501.                  * data we wrote, but the standard file system
  502.                  * seems to do it this way. */
  503. #else
  504.         if (size == oldsize) {
  505.             return -1;
  506.         }
  507.         return oldsize - size;    /* Amount successfully written */
  508. #endif
  509.         }
  510.     }
  511.     }
  512.  
  513.     if (update)
  514.     UpdateFileLock(fl);
  515.  
  516.     return oldsize;
  517. #endif
  518. }
  519.  
  520. long
  521. MSDeleteFile(parentdir, name)
  522. struct MSFileLock *parentdir;
  523. byte           *name;
  524. {
  525. #ifdef READONLY
  526.     return DOSFALSE;
  527. #else
  528.     register struct MSFileLock *fl;
  529.  
  530.     fl = MSLock(parentdir, name, EXCLUSIVE_LOCK);
  531.     if (fl) {
  532.     if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) {
  533.         error = ERROR_DELETE_PROTECTED;
  534.         goto error;
  535.     }
  536.     if (fl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY) {
  537.         struct FileInfoBlock fib;
  538.  
  539.         /*
  540.          * We normally can't get REAL exclusive locks on directories,
  541.          * so we check here just to be sure. We don't want to delete
  542.          * anyone's current directory, do we?
  543.          */
  544.  
  545.         if (fl->msfl_Refcount != 1 || fl == RootLock) {
  546.         error = ERROR_OBJECT_IN_USE;
  547.         goto error;
  548.         }
  549.         if (MSExamine(fl, &fib) &&  /* directory itself */
  550.         MSExNext(fl, &fib)) {   /* should fail */
  551.         if (error == 0) {
  552.         not_empty:
  553.             error = ERROR_DIRECTORY_NOT_EMPTY;
  554.         error:
  555.             MSUnLock(fl);
  556.             return DOSFALSE;
  557.         }
  558.         }
  559.         if (error != ERROR_NO_MORE_ENTRIES)
  560.         goto error;
  561.  
  562.         error = 0;
  563.     }
  564.     if (fl->msfl_Msd.msd_Cluster)
  565.         FreeClusterChain(fl->msfl_Msd.msd_Cluster);
  566.     fl->msfl_Msd.msd_Name[0] = DIR_DELETED;
  567.     WriteFileLock(fl);
  568.     MSUnLock(fl);
  569.  
  570.     return DOSTRUE;
  571.     }
  572.     return DOSFALSE;
  573. #endif
  574. }
  575.  
  576. long
  577. MSSetDate(parentdir, name, datestamp)
  578. struct MSFileLock *parentdir;
  579. byte           *name;
  580. struct DateStamp *datestamp;
  581. {
  582. #ifdef READONLY
  583.     return DOSFALSE;
  584. #else
  585.     register struct MSFileLock *fl;
  586.  
  587.     fl = MSLock(parentdir, name, EXCLUSIVE_LOCK);
  588.     if (fl) {
  589.     ToMSDate(&fl->msfl_Msd.msd_Date, &fl->msfl_Msd.msd_Time, datestamp);
  590.     WriteFileLock(fl);
  591.     MSUnLock(fl);
  592.  
  593.     return DOSTRUE;
  594.     }
  595.     return DOSFALSE;
  596. #endif
  597. }
  598.  
  599. /*
  600.  * Create a new directory, with its own initial "." and ".." entries.
  601.  */
  602.  
  603. struct MSFileLock *
  604. MSCreateDir(parentdir, name)
  605. struct MSFileLock *parentdir;
  606. byte           *name;
  607. {
  608. #ifdef READONLY
  609.     return DOSFALSE;
  610. #else
  611.     register struct MSFileLock *fl;
  612.  
  613.     /*
  614.      * Go create a new file. If we fail later, we have an empty file that
  615.      * we delete again.
  616.      */
  617.  
  618.     fl = MSLock(parentdir, name, EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
  619.     if (fl || error == ERROR_OBJECT_IN_USE) {
  620.     error = ERROR_OBJECT_EXISTS;
  621.     goto error;
  622.     }
  623.     if (error != 0) {
  624.     goto error;
  625.     }
  626.     if (fl = EmptyFileLock) {
  627.     debug(("Creating new dir\n"));
  628.     EmptyFileLock = NULL;
  629.     if ((fl->msfl_Msd.msd_Cluster = FindFreeCluster(FAT_EOF)) != FAT_EOF) {
  630.         struct MsDirEntry direntry;
  631.         byte       *sec;
  632.         word        sector;
  633.  
  634.         sector = ClusterToSector(fl->msfl_Msd.msd_Cluster);
  635.         sec = EmptySec(sector);
  636.         if (sec == NULL)
  637.         goto error_no_free_store;
  638.         setmem(sec, (int) Disk.bps, 0);
  639.  
  640.         /*
  641.          * Turn the file into a directory.
  642.          */
  643.         fl->msfl_Msd.msd_Attributes = ATTR_DIRECTORY;
  644.         UpdateFileLock(fl);
  645.  
  646.         /*
  647.          * Create the "." entry.
  648.          */
  649.         direntry = fl->msfl_Msd;
  650.         strncpy(direntry.msd_Name, DotDot + 1, 8 + 3);
  651.         OtherEndianMsd(&direntry);
  652.         ((struct MsDirEntry *) sec)[0] = direntry;
  653.  
  654.         /*
  655.          * Get the real parent directory because we will duplicate the
  656.          * directory entry in the subdirectory.
  657.          */
  658.  
  659.         parentdir = MSParentDir(fl);
  660.         if (parentdir == NULL)      /* Cannot happen */
  661.         parentdir = MSDupLock(RootLock);
  662.  
  663.         /*
  664.          * Create the ".." entry.
  665.          */
  666.         direntry = parentdir->msfl_Msd;
  667.         strncpy(direntry.msd_Name, DotDot, 8 + 3);
  668.         direntry.msd_Attributes = ATTR_DIRECTORY;
  669.         OtherEndianMsd(&direntry);
  670.         ((struct MsDirEntry *) sec)[1] = direntry;
  671.  
  672.         MSUnLock(parentdir);
  673.  
  674.         MarkSecDirty(sec);
  675.         FreeSec(sec);
  676.  
  677.         /*
  678.          * Clear out the rest of the newly created directory.
  679.          */
  680.  
  681.         while ((sector = NextClusteredSector(sector)) != SEC_EOF) {
  682.         sec = EmptySec(sector);
  683.         if (sec == NULL)
  684.             goto error_no_free_store;
  685.         setmem(sec, (int) Disk.bps, 0);
  686.         MarkSecDirty(sec);
  687.         FreeSec(sec);
  688.         }
  689.     } else {
  690.         MSUnLock(fl);
  691.         fl = NULL;
  692.         MSDeleteFile(parentdir, name);
  693.         error = ERROR_DISK_FULL;
  694.     }
  695.     }
  696.     if (EmptyFileLock) {
  697.     MSUnLock(EmptyFileLock);
  698.     EmptyFileLock = NULL;
  699.     }
  700.     return fl;
  701.  
  702. error_no_free_store:
  703.     error = ERROR_NO_FREE_STORE;
  704. error:
  705.     if (fl)
  706.     MSUnLock(fl);
  707.     return DOSFALSE;
  708. #endif
  709. }
  710.  
  711. /*
  712.  * Rename a file or directory, possibly moving it to a different
  713.  * directory.
  714.  *
  715.  * "Tuned" to also work in full directories by first deleting the source
  716.  * name, then look for a slot to put the destination name. If that fails,
  717.  * we undo the deletion. By playing with the cache, we even avoid a write
  718.  * of the sector with the undeleted entry.
  719.  */
  720.  
  721. long
  722. MSRename(slock, sname, dlock, dname)
  723. struct MSFileLock *slock;
  724. byte           *sname;
  725. struct MSFileLock *dlock;
  726. byte           *dname;
  727. {
  728. #ifdef READONLY
  729.     return DOSFALSE;
  730. #else
  731.     struct MSFileLock *sfl;
  732.     struct MSFileLock *dfl;
  733.     long        success;
  734.     struct CacheSec *scache;
  735.     ulong        oldstatus;
  736.  
  737.     success = DOSFALSE;
  738.     scache = NULL;
  739.     dfl = NULL;
  740.  
  741.     sfl = MSLock(slock, sname, SHARED_LOCK);
  742.     if (sfl == NULL || sfl == RootLock)
  743.     goto error;
  744.  
  745.     /*
  746.      * Now we are going to pull a dirty trick with the cache. We are going
  747.      * to temporarily delete the source file, in the chache only, and
  748.      * undelete it again if we cannot create the new name. And above all
  749.      * we want to avoid unnecessary writes if we decide not to do the
  750.      * deletion after all.
  751.      */
  752.     {
  753.     byte           *sec;
  754.     byte        old;
  755.  
  756.     if ((sec = GetSec(sfl->msfl_DirSector)) == NULL)
  757.         goto error;
  758.     scache = FindSecByBuffer(sec);
  759.     oldstatus = scache->sec_Refcount;
  760.  
  761.     old = sfl->msfl_Msd.msd_Name[0];
  762.     sfl->msfl_Msd.msd_Name[0] = DIR_DELETED;
  763.     WriteFileLock(sfl);
  764.     sfl->msfl_Msd.msd_Name[0] = old;
  765.  
  766.     /*
  767.      * Don't FreeSec it yet; we don't want it written out to disk.
  768.      */
  769.     }
  770.  
  771.     /*
  772.      * Now we have freed the directory entry of the source name, we might
  773.      * be able to use it for the destination name. But only if we also
  774.      * temporarily hide the MSFileLock on that spot. Gross hack ahead!
  775.      */
  776.  
  777.     sfl->msfl_DirOffset = ~sfl->msfl_DirOffset;
  778.     dfl = MSLock(dlock, dname, EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
  779.     sfl->msfl_DirOffset = ~sfl->msfl_DirOffset;
  780.  
  781.     if (dfl != NULL || error == ERROR_OBJECT_IN_USE) {
  782.     error = ERROR_OBJECT_EXISTS;
  783.     goto undelete;
  784.     }
  785.     dfl = EmptyFileLock;
  786.     EmptyFileLock = NULL;
  787.     if (dfl == NULL) {
  788.     /*
  789.      * Sigh, we could not create the new name. But because of that, we
  790.      * are sure that we need to write nothing to the disk at all. So
  791.      * we can safely reset the sector-dirty flag to what it was
  792.      * before, if we also restore the cached sector.
  793.      */
  794. undelete:
  795.     WriteFileLock(sfl);
  796.     scache->sec_Refcount = oldstatus;
  797.     goto error;
  798.     }
  799.     /*
  800.      * Now, if the moved entry was a directory, and it was moved to a
  801.      * different directory, we need to adapt its "..", which is the second
  802.      * entry.
  803.      */
  804.  
  805.     if (sfl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY &&
  806.     sfl->msfl_Parent != dfl->msfl_Parent) {
  807.     struct MSFileLock *parentdir;
  808.     struct MsDirEntry *dir;
  809.  
  810.     if (dir = (struct MsDirEntry *)
  811.         GetSec(DirClusterToSector(sfl->msfl_Msd.msd_Cluster))) {
  812.         parentdir = MSParentDir(dfl);
  813.         /*
  814.          * Copy everything except the name which must remain "..". But
  815.          * first a quick consistency check...
  816.          */
  817.         debug(("Creating new \"..\"  "));
  818.         if (dir[1].msd_Name[1] == '.') {
  819.         CopyMem(&parentdir->msfl_Msd.msd_Attributes,
  820.             &dir[1].msd_Attributes,
  821.             (long) sizeof (struct MsDirEntry) -
  822.             OFFSETOF(MsDirEntry, msd_Attributes));
  823.         dir[1].msd_Attributes = ATTR_DIRECTORY;
  824.         OtherEndianMsd(&dir[1]);
  825.         MarkSecDirty(dir);
  826.         }
  827. #ifdef DEBUG
  828.         else
  829.         debug(("!!! No \"..\" found ??\n"));
  830. #endif
  831.         MSUnLock(parentdir);
  832.         FreeSec(dir);
  833.     }
  834.     }
  835.     /*
  836.      * Move the name from the new entry to the old filelock. We do this
  837.      * for the case that somebody else has a lock on the (possibly moved)
  838.      * file/directory. Also move the other administration.
  839.      */
  840.  
  841.     strncpy(sfl->msfl_Msd.msd_Name, dfl->msfl_Msd.msd_Name, 8 + 3);
  842.     sfl->msfl_DirSector = dfl->msfl_DirSector;
  843.     sfl->msfl_DirOffset = dfl->msfl_DirOffset;
  844.     /*
  845.      * Free the old, and get the new parent directory. They might be the
  846.      * same, of course...
  847.      */
  848.     MSUnLock(sfl->msfl_Parent);
  849.     sfl->msfl_Parent = dfl->msfl_Parent;
  850.     dfl->msfl_Parent = NULL;
  851.     sfl->msfl_Msd.msd_Attributes &= ~ATTR_ARCHIVED;
  852.     WriteFileLock(sfl);         /* Write the new name; the old name
  853.                  * already has been deleted. */
  854.     success = DOSTRUE;
  855.  
  856. error:
  857.     if (sfl)
  858.     MSUnLock(sfl);
  859.     if (dfl)
  860.     MSUnLock(dfl);
  861.     if (scache)
  862.     FreeSec(scache->sec_Data);
  863.  
  864.     return success;
  865. #endif
  866. }
  867.