root/fs/umsdos/emd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. umsdos_readdir_kmem
  2. umsdos_file_read_kmem
  3. umsdos_file_write_kmem
  4. umsdos_emd_dir_write
  5. umsdos_emd_dir_read
  6. umsdos_emd_dir_lookup
  7. umsdos_emd_dir_readentry
  8. umsdos_writeentry
  9. umsdos_fillbuf
  10. umsdos_find
  11. umsdos_newentry
  12. umsdos_newhidden
  13. umsdos_delentry
  14. umsdos_isempty
  15. umsdos_findentry

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

/* [previous][next][first][last][top][bottom][index][help] */