summaryrefslogtreecommitdiffstats
path: root/doc/libdmsdos.doc
diff options
context:
space:
mode:
authorcc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-11 14:38:47 +0000
committercc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-11 14:38:47 +0000
commitf5450bfd35a6410528d124f534c2b1a958cafe51 (patch)
treea808b12d6ad5343fabdec7b8918df6b4d844e03f /doc/libdmsdos.doc
parent5ad2bb7a6ac7e97c031908d2439808a00fff6214 (diff)
dmsdos-0.9.2.2 addeddmsdos-0.9.2.2
Diffstat (limited to 'doc/libdmsdos.doc')
-rw-r--r--doc/libdmsdos.doc614
1 files changed, 614 insertions, 0 deletions
diff --git a/doc/libdmsdos.doc b/doc/libdmsdos.doc
new file mode 100644
index 0000000..8d4ce70
--- /dev/null
+++ b/doc/libdmsdos.doc
@@ -0,0 +1,614 @@
+This is documentation for the dmsdos library. 10Sep1998
+
+
+The dmsdos library, libdmsdos.a, provides the dmsdos interface that is known
+from the kernel to a user-level process. This is acchieved by compiling the
+dmsdos code simply in a different way (well, with some rather dirty tricks).
+
+The library is intended to export the following functions:
+
+ open_cvf
+ close_cvf
+ dbl_bitfat_value
+ dbl_fat_nextcluster
+ dbl_mdfat_cluster2sector
+ dbl_mdfat_value
+ dmsdos_read_cluster
+ dmsdos_write_cluster
+ raw_bread
+ raw_brelse
+ raw_getblk
+ raw_mark_buffer_dirty
+ raw_set_uptodate
+ simple_check
+ stac_bitfat_state
+
+In fact, it exports much more, but only these listed here are guaranteed
+to be kept in future versions. Well, one thing should be important to know:
+The library does NOT export virtual sector management. It will never do.
+
+The functions are just used like in the kernel. For prototypes, see file
+dmsdos.h. How to use them see the sources. There's some example code
+how to use the library (dcread.c).
+
+The first two, open_cvf and close_cvf, are special. They are called instead
+of mounting and unmounting the CVF.
+
+How to "mount" a CVF (example code):
+
+ #include "lib_interface.h"
+ #include "dmsdos.h"
+
+ struct super_block*sb;
+ sb=open_cvf("CVF_Filename",rwflag /*0=RDONLY or 1=RDWR*/);
+ if(sb==NULL)
+ { fprintf(stderr,"open CVF failed\n");
+ exit(1);
+ }
+
+Keep the sb pointer. It must be passed to a lot of functions. It can be used
+in the same way as in the kernel. The sb pointer is used to distinguish
+between several open CVFs (in fact, in sb->s_dev the file descriptor is
+stored).
+
+After use, the CVF must be "unmounted" again:
+
+ close_cvf(sb);
+
+If you want to see more example code, look at the dcread utility source
+(file dcread.c in the src directory).
+
+Notes:
+ * The user-level functions are single-threaded. You cannot run several
+ processes on the same CVF unless all are only reading. The library
+ uses flock in order to detect such situations and just denies access
+ in that case. You can however use different CVFs in parallel, even
+ read-write, even in one program.
+ * You should use this library only to access a CVF that is currently
+ _not_ mounted. There's a high risk of destroying the CVF or even
+ crashing the system otherwise.
+ * You should not touch CVFs that are currently used by dosemu with
+ wholedisk or partition access. Again there's a high risk of destroying
+ the CVF or crashing dosemu otherwise.
+ * If you do not intend to write to the CVF, open it in read-only mode.
+ Then the CVF is not locked exclusively and other processes are allowed
+ to read the CVF at the same time.
+ * The library prints the "kernel" messages to stderr. It obeys the
+ loglevel variable. The only way to make libdmsdos quiet is to
+ direct stderr into a file or to /dev/null.
+ * The first call of open_cvf initializes the library and prints some
+ version information to stderr.
+ * open_cvf does not do a filesystem check. If you do want the same
+ simple filesystem check that dmsdos usually does when mounting, call
+ simple_check afterwards.
+ * open_cvf may open the filesystem read-only though you request
+ read-write access. This happens if write access is denied or if the
+ dmsdos subsystem detects unexpected problems with the CVF. You may
+ not like this behaviour, but it's best for long life of your CVFs :)
+ Programmers please check sb->s_flags for the MS_RDONLY flag after
+ calling open_cvf to be sure.
+ * To compile your application using the dmsdos library you should under
+ no cirumstances define the macro __KERNEL__ for the whole program.
+ It causes problems with libc6.
+
+As an idea what the dmsdos library might be good for:
+
+ * A mtools clone for CVFs, for example called dtools. (A simple program,
+ dcread, exists but its quatilty cannot be compared to mtools. Sorry.)
+ * A fsck.dmsdos. (An incomplete alpha test version exists.)
+ * A defragmentation program. (Not yet written.)
+ * For debugging dmsdos at user level (it's the same source code).
+ * An external fs for midnight commander. (A read-only interface program,
+ mcdmsdos, exists.)
+ * ... add your favourite idea here :)
+ * ... I've even received mail about whether it would be possible to
+ port libdmsdos to Win32 and use it for a Win95/98/NT driver :))
+
+Support for a shared dmsdos library
+-----------------------------------
+
+libdmsdos can be compiled as a shared library. You must edit the
+Makefile in the src directory for that purpose. Note that the default
+installation does not compile the shared library. This is intended. If
+you are not an experienced shared library hacker please be careful. It's
+easy to screw up some binaries with shared libraries :)
+
+You should not use the dmsdos shared library for now as there's currently no
+standard that ensures that future versions will be backwards compatible.
+Link statically with libdmsdos.a unless disk space or memory is very critical.
+
+WARNING: If there's a shared dmsdos library lying around, gcc defaults to
+linking *dynamically* all programs that need this library. For example, if
+libdmsdos.so is present and you recompile dmsdosfsck, the binary will be
+dynamically linked against that library. This can be very confusing and may
+not do what you want :(
+
+Built-in direct translation access mode
+---------------------------------------
+
+This feature has been added to the library for convenience. It only works
+for FAT12 or FAT16 MSDOS filesystems. It does not work, for example, for
+CVFs on a cdrom or in a FAT32 or non-FAT filesystem.
+
+The dmsdos library can access a CVF that resides in an uncompressed FAT12 or
+FAT16 MSDOS host partition even if the uncompressed host partition is not
+mounted (this was programmed in fact by cut'n'paste of some old dmsdosfs
+functions). If there are more than one CVF in that partition, the first
+valid CVF found in the root directory of the uncompressed host partition
+is used. If you want to select one of more CVFs, append a colon and the
+extension of the CVF to the filename.
+
+For example, if your uncompressed host partition is /dev/hda1 and you want
+to access the CVF DRVSPACE.001 on that partition, use "/dev/hda1:001" as
+filename when calling open_cvf. If DRVSPACE.001 is the only CVF inside
+/dev/hda1 you can even use "/dev/hda1" and it will find the CVF.
+
+This special feature allows, for example, dmsdosfsck to check a CVF in
+a partition that has not yet been mounted. This may be useful at boot time
+to check CVFs :)
+
+Standard C library functions that libdmsdos needs
+-------------------------------------------------
+
+If you want to use libdmsdos in an environment where the standard C library
+functions are not available then you must write an emulation for all these
+functions and link your code against it instead of the standard C library.
+The emulation needn't be full-featured like the C library. Look at the source
+if you are in doubt.
+
+ close (4)
+ errno
+ exit (3)
+ flock (only if compiled with -DUSE_FLOCK) (4)
+ sopen (only if compiled with -DUSE_SOPEN) (4) (8)
+ fprintf (2)
+ free (6)
+ lseek (1) (7)
+ malloc (6)
+ memcpy
+ memmove
+ memset
+ open (only if _not_ compiled with -DUSE_SOPEN) (8)
+ read (1)
+ strcat
+ strerror
+ strncat
+ strncmp
+ strncpy
+ strrchr
+ time (5)
+ vsprintf (2) (9)
+ write (1)
+
+ (1) only used in aligned 512 byte blocks
+ (2) only used for logging to stderr, no other files are used
+ (3) used for abort in case of fatal error
+ (4) locking may be left out
+ (5) a time-proportional counter would be enough, needn't be exact
+ (6) called quite often, be aware of possible memory fragmentation
+ (7) only used on 512 byte boundaries with SEEK_SET, also used to find out
+ file size with SEEK_END
+ (8) must parse filename (invent your own syntax if necessary)
+ (9) a free, portable emulation is in linux/lib/vsprint.c
+
+If possible, please avoid hacking around in dmsdos kernel code if the library
+does not work in your environment (unless it's a bug). The only files that
+should be modified due to OS or compiler differences are lib_interface.c and
+lib_interface.h. Please surround your changes with appropriate #ifdef's so
+the modified code still compiles under all those environments where it worked
+before. Please do not violate portability of the code. Hardware comes and
+goes, OSses are born and die, but portable code survives :)
+
+
+dmsdos library reference guide
+------------------------------
+
+All functions need the header files lib_interface.h and dmsdos.h. Error
+checking is not always possible. This is because some of the low-level kernel
+functions dmsdos calls when running as kernel module never fail. In user
+space, there may be failures, however. This means, libdmsdos is currently
+not 100% error-safe. As a workaround (that works under the standard C library)
+you can set errno to NO_ERROR before calling the function and afterwards
+check whether it has changed to an error value.
+
+ open_cvf:
+
+ struct super_block* open_cvf(char*filename,int rwflag)
+
+ Opens CVF described by filename. rwflag is 0 for read-only, otherwise
+ the file is opened read-write. The file is locked read-only or
+ read-write, depending on rwflag (by calling flock or sopen).
+
+ filename is interpreted in a special way if it contains a colon.
+ See chapter 'Built-in direct translation access mode' above. The colon
+ and the rest of the string after it are stripped off before the filename
+ is forwarded to the libc function open or sopen.
+
+ Return Value: Pointer to valid super_block structure (which can be
+ interpreted as a kind of file handle), NULL if failed.
+
+ Note: Keep the super_block pointer. Most dmsdos functions need it in
+ order to identify the CVF.
+
+ Note: The CVF may be opened read-only though you opened it in read-write
+ mode. This happens if the dmsdos code detects unexpected errors in the
+ CVF. Please check sb->s_flags for the MS_RDONLY bit after calling
+ open_cvf.
+
+ close_cvf:
+
+ void close_cvf(struct super_block*sb)
+
+ Closes the CVF. If necessary, the file is unlocked before.
+
+ dbl_bitfat_value:
+
+ int dbl_bitfat_value(struct super_block*sb,int sectornr,int*new)
+
+ Reads or writes bitfat value that belongs to sector sectornr.
+ *** This is a low-level function you are probably never interested in.
+ Bitfat values are CVF format specific. You do not need to know them
+ unless debugging write access :))
+
+ If new is NULL the value is read and returned.
+ Otherwise the actual value is replaced with that one found in *new.
+
+ Return value: read access: read value. write access: undefined. A
+ negative value indicates an error.
+
+ dbl_fat_nextcluster:
+
+ int dbl_fat_nextcluster(struct super_block*sb,int clusternr,int*new)
+
+ Reads or writes the FAT entry for cluster clusternr. (You should know
+ what a FAT is, otherwise please read a good book on Dos disk access.)
+
+ If new is NULL the value is read and returned.
+ Otherwise the actual value is replaced with that one found in *new.
+
+ Return value: read access: read value. write access: undefined.
+ -1 means the cluster is marked as last cluster in a file or an error
+ occured. Errors may also be represented by other negative results.
+
+ Note: -1 can also be used for write access in order to indicate end of
+ file mark. It is automatically converted to the right value for the FAT
+ size.
+
+ Note: if clusternr is invalid a -1 is returned. This usually makes
+ runaway FAT reading loops break as if there was an EOF in the cluster
+ chain. There's no means to find out whether a -1 is an error or an EOF
+ except making sure a valid cluster number is given.
+
+ dbl_mdfat_cluster2sector:
+
+ int dbl_mdfat_cluster2sector(struct super_block*sb,int clusternr)
+
+ This is a low-level function that reads the mdfat and extracts the
+ starting sector information from it. It may be useful for write
+ access with dmsdos_write_cluster. It returns the starting sector number
+ of cluster clusternr.
+ *** Do not use the standard formula that is used in a normal FAT
+ filesystem (multiplying the cluster number with the number of sectors
+ per cluster and adding some offsets) - that formula is not valid in a
+ compressed filesystem. Use dbl_mdfat_cluster2sector instead.
+
+ Return value: starting sector of the cluster clusternr. If negative,
+ an error occured.
+
+ Note: see dmsdos_write_cluster what this function is useful for.
+
+ dbl_mdfat_value:
+
+ int dbl_mdfat_value(struct super_block* sb,int clusternr,
+ Mdfat_entry*new,Mdfat_entry*mde)
+
+ Reads or writes the mdfat/allocmap entry that belongs to cluster
+ clusternr.
+ *** This is a low-level function you are probably never interested in.
+ Mdfat values are CVF format specific. You do not need to know them
+ unless debugging dmsdos :))
+
+ If new is NULL the value is read and stored in *mde.
+ Otherwise the actual value is replaced with that one found in *new.
+
+ Return value: A negative value indicates an error.
+ Note: consider *mde as undefined after write access.
+
+ dmsdos_read_cluster:
+
+ int dmsdos_read_cluster(struct super_block*sb,
+ unsigned char*clusterd, int clusternr)
+
+ Reads cluster clusternr in clusterd. The dmsdos library handles all
+ low-level access including decompression of the data.
+
+ *** Be sure to have reserved enough memory for the data that this
+ function writes into clusterd. Usually, use a full cluster size.
+ How to determine the full cluster size see the dcread example code.
+
+ Return value: number of bytes actually read (clusters may be shorter
+ than the full size in a CVF, which is not possible in an uncompressed
+ FAT filesystem). Usually, you can ignore this unless you use write
+ access. (A cluster may even have length zero.) Just in case, if the
+ cluster is shorter than full size the unused slack is zero'd out. On
+ error, a negative value is returned.
+
+ Note: This function cannot read the root directory (cluster 0). You must
+ use low-level disk access for that (i.e. raw_bread). See also the dcread
+ example code.
+
+ dmsdos_write_cluster:
+
+ int dmsdos_write_cluster(struct super_block*sb, unsigned char* clusterd,
+ int length, int clusternr, int near_sector,
+ int ucflag)
+
+ Writes cluster clusternr from clusterd back to disk. The dmsdos
+ library handles all low-level disk access including compression.
+
+ length is the position of the last valid byte plus 1 in clusterd.
+ This is usually the value that dmsdos_read_cluster returned when you
+ read this cluster before unless you modified some bytes beyond that
+ value. The idiot-proof value is always the full cluster size.
+ *** Warning: if length is too low, the data may be truncated during
+ write access. If it is too high disk space is wasted.
+
+ ucflag is a flag that indicates whether libdmsdos should try to
+ compress the data. 0 means try to compress, 1 means write the data
+ without compression. Please do not use other values than 0 and 1
+ (they do exist but have some very special meanings).
+
+ near_sector can be used to control where libdmsdos tries to place
+ the data on disk. For good performance on large files, it should
+ be set to the start sector value of the cluster that preceeds the
+ actual cluster in the file. The start sector value can be obtained with
+ dbl_mdfat_cluster2sector on that preceeding cluster. (This way actually
+ helps avoiding low-level fragmentation _and_ file data fragmentation in
+ the CVF.)
+ *** Do not use the standard formula that is used in a normal FAT
+ filesystem (multiplying the cluster number with the number of sectors
+ per cluster and adding some offsets) - that formula is not valid in a
+ compressed filesystem.
+
+ The near_sector value is just meant as a hint. If there's not enough
+ free space on the disk around near_sector, dmsdos writes the data
+ somewhere else. If near_sector is set to zero, dmsdos uses an internal
+ way to find out a good value that avoids low-level fragmentation but
+ that is not necessarily good for performance on the file that is being
+ written. Nevertheless, 0 always works.
+
+ WARNING: If you are writing a directory cluster you should not compress
+ the data (it would hurt performance a lot). Some of the original CVF Dos
+ software versions never compress directories, so you shouldn't do
+ either (it may cause compatibility problems otherwise). You also should
+ always use full cluster size for directories on some CVF versions. In
+ order to feel safe you can use the DIR_MAY_BE_COMPRESSED and
+ DIR_MAY_BE_SHORT macros. Have a look at their definition in file
+ dmsdos.h.
+
+ Return value: A negative value indicates an error.
+
+ Note: The data in clusterd are not destroyed by this function.
+
+ raw_bread:
+
+ struct buffer_head* raw_bread(struct super_block*sb,int block)
+
+ Read a 512 byte block from the CVF. The data are read without further
+ modification. Block numbering scheme begins with zero.
+
+ Return value: pointer to a buffer_head structure that contains the 512
+ bytes of data that represent the data block. The data are in
+ buffer_head->b_data in an unsigned character array (see the dcread
+ example). On error, NULL is returned.
+
+ Note: The data are returned in dynamically allocated memory. If you
+ do not need the data any more, you must free them by calling raw_brelse
+ in the buffer_head structure pointer.
+
+ Note: You may modify the data. You must call raw_mark_buffer_dirty
+ after modification (this writes the data back to disk) and before
+ raw_brelse.
+
+ Note: If you want to overwrite the whole block, use raw_getblk instead
+ of raw_bread (this leaves out the unnecessary disk read access).
+
+ raw_brelse:
+
+ void raw_brelse(struct super_block*sb,struct buffer_head*bh)
+
+ Release dynamic memory for the data read by raw_bread.
+
+ raw_getblk:
+
+ struct buffer_head* raw_getblk(struct super_block*sb,int block)
+
+ Like raw_bread, but leaves out the actual disk access. This is intended
+ to leave out unnecessary disk read access if you want to overwrite the
+ whole block. Consider the data as undefined. See raw_bread.
+
+ Note: raw_getblk should be followed by a call of raw_set_uptodate
+ for compatibility. See there.
+
+ raw_mark_buffer_dirty:
+
+ void raw_mark_buffer_dirty(struct super_block*sb,struct buffer_head*bh,
+ int dirty_val)
+
+ Write data back to disk after they have been modified.
+
+ dirty_val must be always 1 (due to a misunderstanding/bug in dmsdos).
+
+ Note: This function does not indicate an error. This is bad, but it is
+ because the Linux kernel interface also does not return error codes with
+ this function. As a hack (that works with libc) you can set errno to
+ NO_ERROR and check it afterwards, but note that this will not work in
+ the dmsdos kernel driver. Usually, there cannot be an error during that
+ kind of write access unless the disk has bad sectors.
+
+ raw_set_uptodate:
+
+ void raw_set_uptodate(struct super_block*sb,struct buffer_head*bh,int v)
+
+ No longer used (does nothing in libdmsdos).
+
+ For compatibility with the kernel driver, this should be called with
+ v=1 after raw_getblk on the buffer_head pointer that raw_getblk returned
+ (unless it's NULL due to failure).
+
+ simple_check:
+
+ int simple_check(struct super_block*sb,int repair)
+
+ Check some internal filesystem tables for consistency.
+
+ If repair is 1 libdmsdos tries to correct errors (the CVF must have been
+ opened read-write in that case). If it is 0 the tables are just checked.
+
+ Return value: a negative value indicates an error, i.e. either the
+ tables are inconsistent or the errors, if any, could not be repaired.
+
+ Note: It is strongly recommended to run simple_check immediately after
+ opening the CVF if you want to write to it. Do not write to a CVF if
+ simple_check fails on it (you may detroy some files in the CVF). As
+ CVFs are known to be very prone to so-called bitfat errors, you probably
+ want to give the program that you are writing a kind of "expert option"
+ that lets simple_check run with repair=1. The same warnings apply as to
+ the dmsdos mount option bitfaterrs:repair :))
+
+ stac_bitfat_state:
+
+ int stac_bitfat_state(struct super_block*sb,int new_state)
+
+ Stacker has a kind of clean/dirty flag that indicates whether the CVF
+ was correctly unmounted. This function can be used to manipulate it.
+ *** This is a low-level function you should not use unless you are a
+ dmsdos and Stacker expert. See the dmsdos source code.
+
+ Note: Do not call this function on non-Stacker CVFs.
+
+
+Programming notes
+-----------------
+
+In a normal libdmsdos application, you open the CVF with open_cvf. If you
+intend to write to the CVF later, you should call simple_check after that
+and refuse write access if an error is found.
+
+Then you use dmsdos_read_cluster and dmsdos_write_cluster to access the
+CVF. Only for the root directory you need raw_bread, raw_mark_buffer_dirty
+and raw_brelse. For following files you need to read the FAT with
+dbl_fat_nextcluster.
+
+You write to the FAT with dbl_fat_nextcluster in order to truncate files or
+append data to them, or even to delete files and directories and to create
+new ones - all like in a usual FAT filesystem.
+
+If you are done, don't forget to close the CVF with close_cvf.
+
+Here is a simple example that illustrates the usage of all important
+libdmsdos functions. You should not compile and execute this example as
+it's not always correct C syntax. Things like error handling have been left
+out to simplify the code (which is not good). Furthermore, the program just
+pokes around in the CVF which would be bad for your data :)
+
+
+ #include "lib_interface.h"
+ #include "dmsdos.h"
+
+ struct super_block*sb;
+ struct Dblsb*dblsb;
+
+ /* open the CVF read-write */
+ sb=open_cvf("dblspace.001",1);
+ if(sb==NULL)...error...
+
+ /* check the CVF */
+ if(simple_check(sb,0)<0)...error...
+
+ /* read and display cluster 2 as hex numbers */
+ int i;
+ int size;
+ int full_size;
+ unsigned char* clusterd;
+
+ /* determine full size */
+ dblsb=MSDOS_SB(sb)->private_data;
+ full_size=dblsp->s_sectperclust*512;
+
+ clusterd=malloc(full_size);
+ if(clusterd==NULL)...error...
+
+ size=dmsdos_read_cluster(sb,clusterd,2);
+ if(size<0)...error...
+ for(i=0;i<size;++i) printf("%x ",clusterd[i]);
+ printf("\n");
+
+ /* modify one byte at position 4234 and write cluster 2 back */
+ int pos;
+
+ pos=4234;
+ clusterd[4234]=123;
+ if(size<=pos)size=pos+1; /* adapt size */
+
+ if(dmsdos_write_cluster(sb,clusterd,size,2,0,0)<0)...error...
+
+ free(clusterd); /* free memory that is no longer needed */
+
+ /* determine which cluster follows cluster 2 in fat */
+ int fatval;
+
+ fatval=dbl_fat_nextcluster(sb,2,NULL);
+ if(fatval==0) printf("cluster 2 is unused\n");
+ else if(fatval==-1) printf("cluster 2 is marked as EOF\n");
+ else printf("cluster 2 is followed by cluster %d in FAT\n");
+
+ /* mark it as EOF */
+ int new;
+
+ new=-1;
+ dbl_fat_nextcluster(sb,2,&new);
+
+ /* read root directory */
+ int i;
+ unsigned char*data;
+ int number;
+ struct buffer_head*bh;
+
+ dblsb=MSDOS_SB(sb)->private_data;
+ number=dblsb->s_rootdirentries*32/512;
+ startblock=dblsb->s_rootdir;
+
+ data=malloc(number*512);
+ if(data==NULL)...error...
+
+ for(i=0;i<number;++i)
+ { bh=raw_bread(sb,startblock+i);
+ if(bh==NULL)...error...
+ memcpy(data+i*512,bh->b_data,512);
+ raw_brelse(sb,bh);
+ }
+ /* complete root directory is now in data */
+
+ /* modify root directory */
+ ...modify data where you like...
+
+ /* write it back completely */
+ for(i=0;i<number;++i)
+ { bh=raw_getblk(sb,startblock+i); /* or raw_bread(...) */
+ if(bh==NULL)...error...
+ raw_set_uptodate(sb,bh,1); /* leave out if raw_bread used above */
+ memcpy(bh->data,data+i*512,512);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+
+ free(data); /* free memory that is no longer needed */
+
+ /* we are done */
+ close_cvf(sb);
+
+
+For a piece of code that actually compiles and runs and does something
+eventually useful and ... does not kill your data :-)) see the dcread
+example program (file dcread.c).