root/fs/umsdos/emd.c

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

DEFINITIONS

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

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