home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 396.lha / MSH_v1.30s / src / hanfile.c < prev    next >
C/C++ Source or Header  |  1990-07-10  |  20KB  |  852 lines

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