home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.2 / LINUX-1.2 / LINUX-1 / linux / fs / umsdos / emd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-23  |  12.3 KB  |  510 lines

  1. /*
  2.  *  linux/fs/umsdos/emd.c
  3.  *
  4.  *  Written 1993 by Jacques Gelinas
  5.  *
  6.  *  Extended MS-DOS directory handling functions
  7.  */
  8. #ifdef MODULE
  9. #include <linux/module.h>
  10. #endif
  11.  
  12. #include <linux/types.h>
  13. #include <linux/fcntl.h>
  14. #include <linux/kernel.h>
  15. #include <asm/segment.h>
  16. #include <linux/sched.h>
  17. #include <linux/errno.h>
  18. #include <linux/string.h>
  19. #include <linux/msdos_fs.h>
  20. #include <linux/umsdos_fs.h>
  21.  
  22. #define PRINTK(x)
  23. #define Printk(x) printk x
  24.  
  25. int umsdos_readdir_kmem(
  26.     struct inode *inode,
  27.     struct file *filp,
  28.     struct dirent *dirent,
  29.     int count)
  30. {
  31.     int ret;
  32.     int old_fs = get_fs();
  33.     set_fs (KERNEL_DS);
  34.     ret = msdos_readdir(inode,filp,dirent,count);
  35.     set_fs (old_fs);
  36.     return ret;
  37. }
  38. /*
  39.     Read a file into kernel space memory
  40. */
  41. int umsdos_file_read_kmem(
  42.     struct inode *inode,
  43.     struct file *filp,
  44.     char *buf,
  45.     int count)
  46. {
  47.     int ret;
  48.     int old_fs = get_fs();    
  49.     set_fs (KERNEL_DS);
  50.     ret = msdos_file_read(inode,filp,buf,count);
  51.     set_fs (old_fs);
  52.     return ret;
  53. }
  54. /*
  55.     Write to a file from kernel space
  56. */
  57. int umsdos_file_write_kmem(
  58.     struct inode *inode,
  59.     struct file *filp,
  60.     char *buf,
  61.     int count)
  62. {
  63.     int ret;
  64.     int old_fs = get_fs();
  65.     set_fs (KERNEL_DS);
  66.     ret = msdos_file_write(inode,filp,buf,count);
  67.     set_fs (old_fs);
  68.     return ret;
  69. }
  70.  
  71.  
  72. /*
  73.     Write a block of bytes into one EMD file.
  74.     The block of data is NOT in user space.
  75.  
  76.     Return 0 if ok, a negative error code if not.
  77. */
  78. int umsdos_emd_dir_write (
  79.     struct inode *emd_dir,
  80.     struct file *filp,
  81.     char *buf,    /* buffer in kernel memory, not in user space */
  82.     int count)
  83. {
  84.     int written;
  85.     filp->f_flags = 0;
  86.     written = umsdos_file_write_kmem (emd_dir,filp,buf,count);
  87.     return written != count ? -EIO : 0;
  88. }
  89. /*
  90.     Read a block of bytes from one EMD file.
  91.     The block of data is NOT in user space.
  92.     Return 0 if ok, -EIO if any error.
  93. */
  94. int umsdos_emd_dir_read (
  95.     struct inode *emd_dir,
  96.     struct file *filp,
  97.     char *buf,    /* buffer in kernel memory, not in user space */
  98.     int count)
  99. {
  100.     int ret = 0;
  101.     int sizeread;
  102.     filp->f_flags = 0;
  103.     sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
  104.     if (sizeread != count){
  105.         printk ("UMSDOS: problem with EMD file. Can't read\n");
  106.         ret = -EIO;
  107.     }
  108.     return ret;
  109.  
  110. }
  111. /*
  112.     Locate the EMD file in a directory and optionally, creates it.
  113.  
  114.     Return NULL if error. If ok, dir->u.umsdos_i.emd_inode 
  115. */
  116. struct inode *umsdos_emd_dir_lookup(struct inode *dir, int creat)
  117. {
  118.     struct inode *ret = NULL;
  119.     if (dir->u.umsdos_i.i_emd_dir != 0){
  120.         ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
  121.         PRINTK (("deja trouve %d %x [%d] "
  122.             ,dir->u.umsdos_i.i_emd_dir,ret,ret->i_count));
  123.     }else{
  124.         umsdos_real_lookup (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN,&ret);
  125.         PRINTK (("emd_dir_lookup "));
  126.         if (ret != NULL){
  127.             PRINTK (("Find --linux "));
  128.             dir->u.umsdos_i.i_emd_dir = ret->i_ino;
  129.         }else if (creat){
  130.             int code;
  131.             PRINTK (("avant create "));
  132.             dir->i_count++;
  133.             code = msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
  134.                 ,S_IFREG|0777,&ret);
  135.             PRINTK (("Creat EMD code %d ret %x ",code,ret));
  136.             if (ret != NULL){
  137.                 dir->u.umsdos_i.i_emd_dir = ret->i_ino;
  138.             }else{
  139.                 printk ("UMSDOS: Can't create EMD file\n");
  140.             }
  141.         }
  142.     }
  143.     if (ret != NULL){
  144.         /* Disable UMSDOS_notify_change() for EMD file */
  145.         ret->u.umsdos_i.i_emd_owner = 0xffffffff;
  146.     }
  147.     return ret;
  148. }
  149.  
  150. /*
  151.     Read an entry from the EMD file.
  152.     Support variable length record.
  153.     Return -EIO if error, 0 if ok.
  154. */
  155. int umsdos_emd_dir_readentry (
  156.     struct inode *emd_dir,
  157.     struct file *filp,
  158.     struct umsdos_dirent *entry)
  159. {
  160.     int ret = umsdos_emd_dir_read(emd_dir,filp,(char*)entry,UMSDOS_REC_SIZE);
  161.     if (ret == 0){
  162.         /* Variable size record. Maybe, we have to read some more */
  163.         int recsize = umsdos_evalrecsize (entry->name_len);
  164.         if (recsize > UMSDOS_REC_SIZE){
  165.             ret = umsdos_emd_dir_read(emd_dir,filp
  166.                 ,((char*)entry)+UMSDOS_REC_SIZE,recsize - UMSDOS_REC_SIZE);
  167.             
  168.         }
  169.     }
  170.     return ret;
  171. }
  172. /*
  173.     Write an entry in the EMD file.
  174.     Return 0 if ok, -EIO if some error.
  175. */
  176. int umsdos_writeentry (
  177.     struct inode *dir,
  178.     struct inode *emd_dir,
  179.     struct umsdos_info *info,
  180.     int free_entry)        /* This entry is deleted, so Write all 0's */
  181. {
  182.     int ret = 0;
  183.     struct file filp;
  184.     struct umsdos_dirent *entry = &info->entry;
  185.     struct umsdos_dirent entry0;
  186.     if (free_entry){
  187.         /* #Specification: EMD file / empty entries
  188.             Unused entry in the EMD file are identify
  189.             by the name_len field equal to 0. However to
  190.             help future extension (or bug correction :-( ),
  191.             empty entries are filled with 0.
  192.         */
  193.         memset (&entry0,0,sizeof(entry0));
  194.         entry = &entry0;
  195.     }else if (entry->name_len > 0){
  196.         memset (entry->name+entry->name_len,'\0'
  197.             ,sizeof(entry->name)-entry->name_len);
  198.         /* #Specification: EMD file / spare bytes
  199.             10 bytes are unused in each record of the EMD. They
  200.             are set to 0 all the time. So it will be possible
  201.             to do new stuff and rely on the state of those
  202.             bytes in old EMD file around.
  203.         */
  204.         memset (entry->spare,0,sizeof(entry->spare));
  205.     }
  206.     filp.f_pos = info->f_pos;
  207.     filp.f_reada = 0;
  208.     ret = umsdos_emd_dir_write(emd_dir,&filp,(char*)entry,info->recsize);
  209.     if (ret != 0){
  210.         printk ("UMSDOS: problem with EMD file. Can't write\n");
  211.     }else{
  212.         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  213.         dir->i_dirt = 1;
  214.     }
  215.     return ret;
  216. }
  217.  
  218. #define CHUNK_SIZE (8*UMSDOS_REC_SIZE)
  219. struct find_buffer{
  220.     char buffer[CHUNK_SIZE];
  221.     int pos;    /* read offset in buffer */
  222.     int size;    /* Current size of buffer */
  223.     struct file filp;
  224. };
  225.  
  226. /*
  227.     Fill the read buffer and take care of the byte remaining inside.
  228.     Unread bytes are simply move to the beginning.
  229.  
  230.     Return -ENOENT if EOF, 0 if ok, a negative error code if any problem.
  231. */
  232. static int umsdos_fillbuf (
  233.     struct inode *inode,
  234.     struct find_buffer *buf)
  235. {
  236.     int ret = -ENOENT;
  237.     int mustmove = buf->size - buf->pos;
  238.     int mustread;
  239.     int remain;
  240.     if (mustmove > 0){
  241.         memcpy (buf->buffer,buf->buffer+buf->pos,mustmove);
  242.     }
  243.     buf->pos = 0;
  244.     mustread = CHUNK_SIZE - mustmove;
  245.     remain = inode->i_size - buf->filp.f_pos;
  246.     if (remain < mustread) mustread = remain;
  247.     if (mustread > 0){
  248.         ret = umsdos_emd_dir_read (inode,&buf->filp,buf->buffer+mustmove
  249.             ,mustread);
  250.         if (ret == 0) buf->size = mustmove + mustread;        
  251.     }else if (mustmove){
  252.         buf->size = mustmove;
  253.         ret = 0;
  254.     }
  255.     return ret;
  256. }
  257.  
  258. /*
  259.     General search, locate a name in the EMD file or an empty slot to
  260.     store it. if info->entry.name_len == 0, search the first empty
  261.     slot (of the proper size).
  262.  
  263.     Caller must do iput on *pt_emd_dir.
  264.  
  265.     Return 0 if found, -ENOENT if not found, another error code if
  266.     other problem.
  267.  
  268.     So this routine is used to either find an existing entry or to
  269.     create a new one, while making sure it is a new one. After you
  270.     get -ENOENT, you make sure the entry is stuffed correctly and
  271.     call umsdos_writeentry().
  272.  
  273.     To delete an entry, you find it, zero out the entry (memset)
  274.     and call umsdos_writeentry().
  275.  
  276.     All this to say that umsdos_writeentry must be call after this
  277.     function since it rely on the f_pos field of info.
  278. */
  279. static int umsdos_find (
  280.     struct inode *dir,
  281.     struct umsdos_info *info,        /* Hold name and name_len */
  282.                                     /* Will hold the entry found */
  283.     struct inode **pt_emd_dir)        /* Will hold the emd_dir inode */
  284.                                     /* or NULL if not found */
  285. {
  286.     /* #Specification: EMD file structure
  287.         The EMD file uses a fairly simple layout. It is made of records
  288.         (UMSDOS_REC_SIZE == 64). When a name can't be written is a single
  289.         record, multiple contiguous record are allocated.
  290.     */
  291.     int ret = -ENOENT;
  292.     struct inode *emd_dir = umsdos_emd_dir_lookup(dir,1);
  293.     if (emd_dir != NULL){
  294.         struct umsdos_dirent *entry = &info->entry;
  295.         int recsize = info->recsize;
  296.         struct {
  297.             off_t posok;    /* Position available to store the entry */
  298.             int found;        /* A valid empty position has been found */
  299.             off_t one;        /* One empty position -> maybe <- large enough */
  300.             int onesize;    /* size of empty region starting at one */
  301.         }empty;
  302.         /* Read several entries at a time to speed up the search */
  303.         struct find_buffer buf;
  304.         buf.pos = 0;
  305.         buf.size = 0;
  306.         buf.filp.f_pos = 0;
  307.         buf.filp.f_reada = 1;
  308.         empty.found = 0;
  309.         empty.posok = emd_dir->i_size;
  310.         empty.onesize = 0;
  311.         while (1){
  312.             struct umsdos_dirent *rentry = (struct umsdos_dirent*)
  313.                 (buf.buffer + buf.pos);
  314.             int file_pos = buf.filp.f_pos - buf.size + buf.pos;
  315.             if (buf.pos == buf.size){
  316.                 ret = umsdos_fillbuf (emd_dir,&buf);
  317.                 if (ret < 0){
  318.                     /* Not found, so note where it can be added */
  319.                     info->f_pos = empty.posok;
  320.                     break;
  321.                 }
  322.             }else if (rentry->name_len == 0){
  323.                 /* We are looking for an empty section at least */
  324.                 /* recsize large */
  325.                 if (entry->name_len == 0){
  326.                     info->f_pos = file_pos;
  327.                     ret = 0;
  328.                     break;
  329.                 }else if (!empty.found){
  330.                     if (empty.onesize == 0){
  331.                         /* This is the first empty record of a section */
  332.                         empty.one = file_pos;
  333.                     }
  334.                     /* grow the empty section */
  335.                     empty.onesize += UMSDOS_REC_SIZE;
  336.                     if (empty.onesize == recsize){
  337.                         /* here is a large enough section */
  338.                         empty.posok = empty.one;
  339.                         empty.found = 1;
  340.                     }
  341.                 }
  342.                 buf.pos += UMSDOS_REC_SIZE;
  343.             }else{
  344.                 int entry_size = umsdos_evalrecsize(rentry->name_len);
  345.                 if (buf.pos+entry_size > buf.size){
  346.                     ret = umsdos_fillbuf (emd_dir,&buf);
  347.                     if (ret < 0){
  348.                         /* Not found, so note where it can be added */
  349.                         info->f_pos = empty.posok;
  350.                         break;
  351.                     }
  352.                 }else{
  353.                     empty.onesize = 0;    /* Reset the free slot search */
  354.                     if (entry->name_len == rentry->name_len
  355.                         && memcmp(entry->name,rentry->name,rentry->name_len)
  356.                             ==0){
  357.                         info->f_pos = file_pos;
  358.                         *entry = *rentry;
  359.                         ret = 0;
  360.                         break;
  361.                     }else{
  362.                         buf.pos += entry_size;
  363.                     }
  364.                 }
  365.             }    
  366.         }
  367.         umsdos_manglename(info);
  368.     }
  369.     *pt_emd_dir = emd_dir;
  370.     return ret;
  371. }
  372. /*
  373.     Add a new entry in the emd file
  374.     Return 0 if ok or a negative error code.
  375.     Return -EEXIST if the entry already exist.
  376.  
  377.     Complete the information missing in info.
  378. */
  379. int umsdos_newentry (
  380.     struct inode *dir,
  381.     struct umsdos_info *info)
  382. {
  383.     struct inode *emd_dir;
  384.     int ret = umsdos_find (dir,info,&emd_dir);
  385.     if (ret == 0){
  386.         ret = -EEXIST;
  387.     }else if (ret == -ENOENT){
  388.         ret = umsdos_writeentry(dir,emd_dir,info,0);
  389.         PRINTK (("umsdos_newentry EDM ret = %d\n",ret));
  390.     }
  391.     iput (emd_dir);
  392.     return ret;
  393. }
  394. /*
  395.     Create a new hidden link.
  396.     Return 0 if ok, an error code if not.
  397. */
  398. int umsdos_newhidden (
  399.     struct inode *dir,
  400.     struct umsdos_info *info)
  401. {
  402.     struct inode *emd_dir;
  403.     int ret;
  404.     umsdos_parse ("..LINK",6,info);
  405.     info->entry.name_len = 0;
  406.     ret = umsdos_find (dir,info,&emd_dir);
  407.     iput (emd_dir);
  408.     if (ret == -ENOENT || ret == 0){
  409.         /* #Specification: hard link / hidden name
  410.             When a hard link is created, the original file is renamed
  411.             to a hidden name. The name is "..LINKNNN" where NNN is a
  412.             number define from the entry offset in the EMD file.
  413.         */
  414.         info->entry.name_len = sprintf (info->entry.name,"..LINK%ld"
  415.             ,info->f_pos);
  416.         ret = 0;
  417.     }
  418.     return ret;
  419. }
  420. /*
  421.     Remove an entry from the emd file
  422.     Return 0 if ok, a negative error code otherwise.
  423.  
  424.     Complete the information missing in info.
  425. */
  426. int umsdos_delentry (
  427.     struct inode *dir,
  428.     struct umsdos_info *info,
  429.     int isdir)
  430. {
  431.     struct inode *emd_dir;
  432.     int ret = umsdos_find (dir,info,&emd_dir);
  433.     if (ret == 0){
  434.         if (info->entry.name_len != 0){
  435.             if ((isdir != 0) != (S_ISDIR(info->entry.mode) != 0)){
  436.                 if (S_ISDIR(info->entry.mode)){
  437.                     ret = -EISDIR;
  438.                 }else{
  439.                     ret = -ENOTDIR;
  440.                 }
  441.             }else{
  442.                 ret = umsdos_writeentry(dir,emd_dir,info,1);
  443.             }
  444.         }
  445.     }
  446.     iput(emd_dir);
  447.     return ret;
  448. }
  449.  
  450.  
  451. /*
  452.     Verify is a EMD directory is empty.
  453.     Return 0 if not empty
  454.            1 if empty
  455.            2 if empty, no EMD file.
  456. */
  457. int umsdos_isempty (struct inode *dir)
  458. {
  459.     int ret = 2;
  460.     struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
  461.     /* If the EMD file does not exist, it is certainly empty :-) */
  462.     if (emd_dir != NULL){
  463.         struct file filp;
  464.         /* Find an empty slot */
  465.         filp.f_pos = 0;
  466.         filp.f_reada = 1;
  467.         filp.f_flags = O_RDONLY;
  468.         ret = 1;
  469.         while (filp.f_pos < emd_dir->i_size){
  470.             struct umsdos_dirent entry;
  471.             if (umsdos_emd_dir_readentry(emd_dir,&filp,&entry)!=0){
  472.                 ret = 0;
  473.                 break;
  474.             }else if (entry.name_len != 0){
  475.                 ret = 0;
  476.                 break;
  477.             }    
  478.         }
  479.         iput (emd_dir);
  480.     }
  481.     return ret;
  482. }
  483.  
  484. /*
  485.     Locate an entry in a EMD directory.
  486.     Return 0 if ok, errcod if not, generally -ENOENT.
  487. */
  488. int umsdos_findentry (
  489.     struct inode *dir,
  490.     struct umsdos_info *info,
  491.     int expect)        /* 0: anything */
  492.                     /* 1: file */
  493.                     /* 2: directory */
  494. {
  495.     struct inode *emd_dir;
  496.     int ret = umsdos_find (dir,info,&emd_dir);
  497.     if (ret == 0){
  498.         if (expect != 0){
  499.             if (S_ISDIR(info->entry.mode)){
  500.                 if (expect != 2) ret = -EISDIR;
  501.             }else if (expect == 2){
  502.                 ret = -ENOTDIR;
  503.             }
  504.         }
  505.     }
  506.     iput (emd_dir);
  507.     return ret;
  508. }
  509.  
  510.