source: rtems/cpukit/libfs/src/dosfs/msdos_dir.c @ 01c5b9d7

Last change on this file since 01c5b9d7 was 01c5b9d7, checked in by Joel Sherrill <joel.sherrill@…>, on Mar 25, 2003 at 5:01:56 PM

2003-03-25 Thomas Doerfler <Thomas.Doerfler@…>

PR 367/filesystem

  • src/dosfs/Makefile.am, src/dosfs/fat.c, src/dosfs/fat.h, src/dosfs/fat_fat_operations.c, src/dosfs/fat_file.c, src/dosfs/msdos.h, src/dosfs/msdos_dir.c: Some bugs were still present in the DOSFS implementation:
    • FAT12 did not work properly on Big-Endian machines
    • Some synchronization and error handling problems were present
    • Some legal codings for EOC were not recognized
  • Property mode set to 100644
File size: 16.5 KB
Line 
1/*
2 *  MSDOS directory handlers implementation
3 *
4 *  Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
5 *  Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
6 *
7 *  The license and distribution terms for this file may be
8 *  found in the file LICENSE in this distribution or at
9 *  http://www.OARcorp.com/rtems/license.html.
10 *
11 *  @(#) $Id$
12 */
13#if HAVE_CONFIG_H
14#include "config.h"
15#endif
16
17#include <stdlib.h>
18#include <unistd.h>
19#include <assert.h>
20#include <errno.h>
21#include <rtems/libio_.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24
25#include <dirent.h>
26
27#include "fat.h"
28#include "fat_fat_operations.h"
29#include "fat_file.h"
30
31#include "msdos.h"
32
33/* msdos_dir_open --
34 *     Open fat-file which correspondes to the directory being opened and
35 *     set offset field of file control block to zero.
36 *
37 * PARAMETERS:
38 *     iop        - file control block
39 *     pathname   - name
40 *     flag       - flags
41 *     mode       - mode
42 *
43 * RETURNS:
44 *     RC_OK, if directory opened successfully, or -1 if error occured (errno
45 *     set apropriately)
46 */
47int 
48msdos_dir_open(rtems_libio_t *iop, const char *pathname, unsigned32 flag,
49               unsigned32 mode)
50{
51    int                rc = RC_OK; 
52    rtems_status_code  sc = RTEMS_SUCCESSFUL;
53    msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info; 
54    fat_file_fd_t     *fat_fd = iop->file_info;
55
56    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
57                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
58    if (sc != RTEMS_SUCCESSFUL)
59        set_errno_and_return_minus_one( EIO );
60 
61    rc = fat_file_reopen(fat_fd);
62    if (rc != RC_OK)
63    {
64        rtems_semaphore_release(fs_info->vol_sema);
65        return rc; 
66    }
67
68    iop->offset = 0;
69    rtems_semaphore_release(fs_info->vol_sema);
70    return RC_OK;
71} 
72
73/* msdos_dir_close --
74 *     Close  fat-file which correspondes to the directory being closed
75 *
76 * PARAMETERS:
77 *     iop - file control block
78 *
79 * RETURNS:
80 *     RC_OK, if directory closed successfully, or -1 if error occured (errno
81 *     set apropriately.
82 */
83int 
84msdos_dir_close(rtems_libio_t *iop)
85{
86    int                rc = RC_OK;
87    rtems_status_code  sc = RTEMS_SUCCESSFUL;
88    msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info; 
89    fat_file_fd_t     *fat_fd = iop->file_info;
90
91    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
92                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
93    if (sc != RTEMS_SUCCESSFUL)
94        set_errno_and_return_minus_one( EIO );
95 
96    rc = fat_file_close(iop->pathinfo.mt_entry, fat_fd);
97    if (rc != RC_OK)
98    {
99        rtems_semaphore_release(fs_info->vol_sema);
100        return rc; 
101    }
102
103    rtems_semaphore_release(fs_info->vol_sema);
104    return RC_OK;
105}
106
107/*  msdos_format_dirent_with_dot --
108 *      This routine convert a (short) MSDOS filename as present on disk
109 *      (fixed 8+3 characters, filled with blanks, without separator dot)
110 *      to a "normal" format, with between 0 and 8 name chars,
111 *      a separating dot and up to 3 extension characters
112 *   Rules to work:
113 *      - copy any (0-8) "name" part characters that are non-blank
114 *      - if an extension exists, append a dot
115 *      - copy any (0-3) non-blank extension characters
116 *      - append a '\0' (dont count it for the rturn code
117 *
118 * PARAMETERS:
119 *     dst: pointer to destination char array (must be big enough)
120 *     src: pointer to source characters
121 *
122 *
123 * RETURNS:
124 *     the number of bytes (without trailing '\0'(written to destination
125 */
126static ssize_t 
127msdos_format_dirent_with_dot(char *dst,const char *src)
128{
129  ssize_t len;
130  int i;
131  const char *src_tmp;
132
133  /*
134   * find last non-blank character of base name
135   */
136  for ((i       =       MSDOS_SHORT_BASE_LEN  ,
137        src_tmp = src + MSDOS_SHORT_BASE_LEN-1);
138       ((i > 0) &&
139        (*src_tmp == ' '));
140       i--,src_tmp--)
141    {};
142  /*
143   * copy base name to destination
144   */
145  src_tmp = src;
146  len = i;
147  while (i-- > 0) {
148    *dst++ = *src_tmp++;
149  }
150  /*
151   * find last non-blank character of extension
152   */
153  for ((i       =                            MSDOS_SHORT_EXT_LEN  ,
154        src_tmp = src + MSDOS_SHORT_BASE_LEN+MSDOS_SHORT_EXT_LEN-1);
155       ((i > 0) && 
156        (*src_tmp == ' '));
157       i--,src_tmp--)
158    {};
159  /*
160   * extension is not empty
161   */
162  if (i > 0) {
163    *dst++ = '.'; /* append dot */
164    len += i + 1; /* extension + dot */
165    src_tmp = src + MSDOS_SHORT_BASE_LEN;
166    while (i-- > 0) {
167      *dst++ = *src_tmp++;
168      len++;
169    }
170  }
171  *dst = '\0'; /* terminate string */
172
173  return len;
174}
175
176/*  msdos_dir_read --
177 *      This routine will read the next directory entry based on the directory
178 *      offset. The offset should be equal to -n- time the size of an
179 *      individual dirent structure. If n is not an integer multiple of the
180 *      sizeof a dirent structure, an integer division will be performed to
181 *      determine directory entry that will be returned in the buffer. Count
182 *      should reflect -m- times the sizeof dirent bytes to be placed in the
183 *      buffer.
184 *      If there are not -m- dirent elements from the current directory
185 *      position to the end of the exisiting file, the remaining entries will
186 *      be placed in the buffer and the returned value will be equal to
187 *      -m actual- times the size of a directory entry.
188 *
189 * PARAMETERS:
190 *     iop    - file control block
191 *     buffer - buffer provided by user
192 *     count  - count of bytes to read
193 *
194 * RETURNS:
195 *     the number of bytes read on success, or -1 if error occured (errno
196 *     set apropriately).
197 */
198ssize_t 
199msdos_dir_read(rtems_libio_t *iop, void *buffer, unsigned32 count)
200{
201    int                rc = RC_OK;
202    rtems_status_code  sc = RTEMS_SUCCESSFUL;
203    msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info; 
204    fat_file_fd_t     *fat_fd = iop->file_info;
205    fat_file_fd_t     *tmp_fat_fd = NULL;
206    struct dirent      tmp_dirent;
207    unsigned32         start = 0;
208    ssize_t            ret = 0;
209    unsigned32         cmpltd = 0;
210    unsigned32         j = 0, i = 0;
211    unsigned32         bts2rd = 0;
212    unsigned32         cur_cln = 0;
213 
214    /*
215     * cast start and count - protect against using sizes that are not exact
216     * multiples of the -dirent- size. These could result in unexpected
217     * results
218     */
219    start = iop->offset / sizeof(struct dirent);
220    count = (count / sizeof(struct dirent)) * sizeof(struct dirent);           
221 
222    /*
223     * optimization: we know that root directory for FAT12/16 volumes is
224     * sequential set of sectors and any cluster is sequential set of sectors
225     * too, so read such set of sectors is quick operation for low-level IO
226     * layer.
227     */
228    bts2rd = (FAT_FD_OF_ROOT_DIR(fat_fd) &&
229             (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) ? 
230             fat_fd->fat_file_size                              :
231             fs_info->fat.vol.bpc;     
232
233    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
234                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
235    if (sc != RTEMS_SUCCESSFUL)
236        set_errno_and_return_minus_one(EIO);
237 
238    while (count > 0)
239    { 
240        /*
241         * fat-file is already opened by open call, so read it
242         * Always read directory fat-file from the beggining because of MSDOS
243         * directories feature :( - we should count elements currently
244         * present in the directory because there may be holes :)
245         */
246        ret = fat_file_read(iop->pathinfo.mt_entry, fat_fd, (j * bts2rd), 
247                            bts2rd, fs_info->cl_buf);
248        if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
249        {
250            rtems_semaphore_release(fs_info->vol_sema);
251            set_errno_and_return_minus_one(EIO);
252        }
253
254        for (i = 0; i < ret; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
255        {
256            if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == 
257                MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
258            {
259                rtems_semaphore_release(fs_info->vol_sema);         
260                return cmpltd;
261            } 
262           
263            if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == 
264                MSDOS_THIS_DIR_ENTRY_EMPTY)
265                continue;
266     
267            /*
268             * skip active entries until get the entry to start from
269             */
270            if (start)
271            {
272                start--; 
273                continue;
274            } 
275       
276            /*
277             * Move the entry to the return buffer
278             *
279             * unfortunately there is no method to extract ino except to
280             * open fat-file descriptor :( ... so, open it
281             */
282     
283            /* get number of cluster we are working with */
284            rc = fat_file_ioctl(iop->pathinfo.mt_entry, fat_fd, F_CLU_NUM,
285                                j * bts2rd, &cur_cln);
286            if (rc != RC_OK)
287            {
288                rtems_semaphore_release(fs_info->vol_sema);
289                return rc;
290            }
291
292            rc = fat_file_open(iop->pathinfo.mt_entry, cur_cln, i, 
293                               &tmp_fat_fd);
294            if (rc != RC_OK)
295            {
296                rtems_semaphore_release(fs_info->vol_sema);
297                return rc;
298            }
299
300            tmp_fat_fd->info_cln = cur_cln;
301            tmp_fat_fd->info_ofs = i;
302
303            /* fill in dirent structure */
304            /* XXX: from what and in what d_off should be computed ?! */
305            tmp_dirent.d_off = start + cmpltd;
306            tmp_dirent.d_reclen = sizeof(struct dirent);
307            tmp_dirent.d_ino = tmp_fat_fd->ino;
308            /*
309             * convert dir entry from fixed 8+3 format (without dot)
310             * to 0..8 + 1dot + 0..3 format
311             */
312            tmp_dirent.d_namlen = 
313              msdos_format_dirent_with_dot(tmp_dirent.d_name,
314                                         fs_info->cl_buf + i); /* src text */
315            memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent));
316   
317            iop->offset = iop->offset + sizeof(struct dirent);
318            cmpltd += (sizeof(struct dirent));
319            count -= (sizeof(struct dirent));
320     
321            /* inode number extracted, close fat-file */
322            rc = fat_file_close(iop->pathinfo.mt_entry, tmp_fat_fd);
323            if (rc != RC_OK)
324            {
325                rtems_semaphore_release(fs_info->vol_sema);
326                return rc;
327            }
328
329            if (count <= 0)
330                break;
331        }
332        j++;
333    }
334
335    rtems_semaphore_release(fs_info->vol_sema);
336    return cmpltd;
337}
338
339/* msdos_dir_write --
340 *     no write for directory
341 */
342
343/* msdos_dir_lseek --
344 *
345 *  This routine will behave in one of three ways based on the state of
346 *  argument whence. Based on the state of its value the offset argument will
347 *  be interpreted using one of the following methods:
348 *
349 *     SEEK_SET - offset is the absolute byte offset from the start of the
350 *                logical start of the dirent sequence that represents the
351 *                directory
352 *     SEEK_CUR - offset is used as the relative byte offset from the current
353 *                directory position index held in the iop structure
354 *     SEEK_END - N/A --> This will cause an assert.
355 *
356 * PARAMETERS:
357 *     iop    - file control block
358 *     offset - offset
359 *     whence - predefine directive
360 *
361 * RETURNS:
362 *     RC_OK on success, or -1 if error occured (errno
363 *     set apropriately).
364 */
365int 
366msdos_dir_lseek(rtems_libio_t *iop, off_t offset, int whence)
367{
368    switch (whence) 
369    {
370        case SEEK_SET:
371        case SEEK_CUR: 
372            break;
373        /*
374         * Movement past the end of the directory via lseek is not a
375         * permitted operation
376         */
377        case SEEK_END:
378        default:
379            set_errno_and_return_minus_one( EINVAL );
380            break;
381    }
382    return RC_OK;
383}
384
385/* msdos_dir_stat --
386 *
387 * This routine will obtain the following information concerning the current
388 * directory:
389 *     st_dev      device id
390 *     st_ino      node serial number :)
391 *     st_mode     mode extracted from the node
392 *     st_size     total size in bytes
393 *     st_blksize  blocksize for filesystem I/O
394 *     st_blocks   number of blocks allocated
395 *     stat_mtime  time of last modification
396 *
397 * PARAMETERS:
398 *     loc - this directory
399 *     buf - stat buffer provided by user
400 *
401 * RETURNS:
402 *     RC_OK and filled stat buffer on success, or -1 if error occured (errno
403 *     set apropriately).
404 */
405int 
406msdos_dir_stat(
407    rtems_filesystem_location_info_t *loc,
408    struct stat                      *buf
409    )
410{
411    rtems_status_code  sc = RTEMS_SUCCESSFUL;
412    msdos_fs_info_t   *fs_info = loc->mt_entry->fs_info; 
413    fat_file_fd_t     *fat_fd = loc->node_access;
414
415    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
416                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
417    if (sc != RTEMS_SUCCESSFUL)
418        set_errno_and_return_minus_one(EIO);
419 
420    buf->st_dev = fs_info->fat.vol.dev;
421    buf->st_ino = fat_fd->ino;
422    buf->st_mode  = S_IFDIR;
423    buf->st_rdev = 0ll;
424    buf->st_size = fat_fd->fat_file_size;
425    buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS;
426    buf->st_blksize = fs_info->fat.vol.bps;
427    buf->st_mtime = fat_fd->mtime;
428 
429    rtems_semaphore_release(fs_info->vol_sema);
430    return RC_OK;
431}
432
433/* msdos_dir_truncate --
434 *     No truncate for directory.
435 *
436 * PARAMETERS:
437 *
438 * RETURNS:
439 *
440 */
441
442/* msdos_dir_sync --
443 *     The following routine does a syncronization on a MSDOS directory node.
444 *     DIR_WrtTime, DIR_WrtDate and DIR_fileSize fields of 32 Bytes Directory
445 *     Entry Structure(see M$ White Paper) should not be updated for
446 *     directories, so only call to corresponding fat-file routine.
447 *
448 * PARAMETERS:
449 *     iop - file control block
450 *
451 * RETURNS:
452 *     RC_OK on success, or -1 if error occured (errno set apropriately).
453 */
454int
455msdos_dir_sync(rtems_libio_t *iop)
456{
457    int                rc = RC_OK;
458    rtems_status_code  sc = RTEMS_SUCCESSFUL;
459    fat_file_fd_t     *fat_fd = iop->file_info;
460    msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info;
461 
462    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
463                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
464    if (sc != RTEMS_SUCCESSFUL)
465        set_errno_and_return_minus_one(EIO);
466 
467    rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd);
468
469    rtems_semaphore_release(fs_info->vol_sema);
470    return rc;
471}
472
473/* msdos_dir_rmnod --
474 *     Remove directory node.
475 *
476 *     Check that this directory node is not opened as fat-file, is empty and
477 *     not filesystem root node. If all this conditions met then delete.
478 *
479 * PARAMETERS:
480 *     pathloc - node description
481 *
482 * RETURNS:
483 *     RC_OK on success, or -1 if error occured (errno set apropriately).
484 */
485int 
486msdos_dir_rmnod(rtems_filesystem_location_info_t *pathloc)
487{
488    int                rc = RC_OK;
489    rtems_status_code  sc = RTEMS_SUCCESSFUL;
490    msdos_fs_info_t   *fs_info = pathloc->mt_entry->fs_info; 
491    fat_file_fd_t     *fat_fd = pathloc->node_access;
492    rtems_boolean      is_empty = FALSE;
493 
494    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
495                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
496    if (sc != RTEMS_SUCCESSFUL)
497        set_errno_and_return_minus_one(EIO);
498 
499    /*
500     * We deny attemp to delete open directory (if directory is current
501     * directory we assume it is open one)
502     */
503    if (fat_fd->links_num > 1)
504    {
505        rtems_semaphore_release(fs_info->vol_sema);
506        set_errno_and_return_minus_one(EBUSY);
507    } 
508 
509    /*
510     * You cannot remove a node that still has children
511     */
512    rc = msdos_dir_is_empty(pathloc->mt_entry, fat_fd, &is_empty); 
513    if (rc != RC_OK)
514    {
515        rtems_semaphore_release(fs_info->vol_sema);
516        return rc;
517    }
518
519    if (!is_empty)
520    {
521        rtems_semaphore_release(fs_info->vol_sema); 
522        set_errno_and_return_minus_one(ENOTEMPTY);
523    }     
524
525    /*
526     * You cannot remove the file system root node.
527     */
528    if (pathloc->mt_entry->mt_fs_root.node_access == pathloc->node_access)
529    {
530        rtems_semaphore_release(fs_info->vol_sema);
531        set_errno_and_return_minus_one(EBUSY);
532    }
533
534    /*
535     * You cannot remove a mountpoint.
536     * not used - mount() not implemenetd yet.
537     */
538
539    /* mark file removed */
540    rc = msdos_set_first_char4file_name(pathloc->mt_entry, fat_fd->info_cln, 
541                                        fat_fd->info_ofs, 
542                                        MSDOS_THIS_DIR_ENTRY_EMPTY);
543    if (rc != RC_OK)
544    {
545        rtems_semaphore_release(fs_info->vol_sema);
546        return rc;
547    }
548
549    fat_file_mark_removed(pathloc->mt_entry, fat_fd); 
550
551    rtems_semaphore_release(fs_info->vol_sema);
552    return rc;
553} 
Note: See TracBrowser for help on using the repository browser.