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

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