summaryrefslogtreecommitdiffstats
path: root/cpukit/libfs/src/dosfs/msdos_dir.c
blob: 17593b32eaabcb9c08958010dd7547fd4b0d0dca (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
/*
 *  MSDOS directory handlers implementation
 *
 *  Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
 *  Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 *
 *  @(#) $Id$
 */
#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <rtems/libio_.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <dirent.h>

#include "fat.h"
#include "fat_fat_operations.h"
#include "fat_file.h"

#include "msdos.h"

/* msdos_dir_open --
 *     Open fat-file which correspondes to the directory being opened and 
 *     set offset field of file control block to zero.
 *
 * PARAMETERS:
 *     iop        - file control block
 *     pathname   - name
 *     flag       - flags
 *     mode       - mode
 *
 * RETURNS:
 *     RC_OK, if directory opened successfully, or -1 if error occured (errno 
 *     set apropriately)
 */
int 
msdos_dir_open(rtems_libio_t *iop, const char *pathname, uint32_t   flag,
               uint32_t   mode)
{
    int                rc = RC_OK;  
    rtems_status_code  sc = RTEMS_SUCCESSFUL;
    msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info; 
    fat_file_fd_t     *fat_fd = iop->file_info;

    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
    if (sc != RTEMS_SUCCESSFUL)
        set_errno_and_return_minus_one( EIO );
  
    rc = fat_file_reopen(fat_fd);
    if (rc != RC_OK)
    {
        rtems_semaphore_release(fs_info->vol_sema);
        return rc; 
    }

    iop->offset = 0;
    rtems_semaphore_release(fs_info->vol_sema);
    return RC_OK;
} 

/* msdos_dir_close --
 *     Close  fat-file which correspondes to the directory being closed
 *
 * PARAMETERS:
 *     iop - file control block
 *
 * RETURNS:
 *     RC_OK, if directory closed successfully, or -1 if error occured (errno 
 *     set apropriately.
 */
int 
msdos_dir_close(rtems_libio_t *iop)
{
    int                rc = RC_OK;
    rtems_status_code  sc = RTEMS_SUCCESSFUL;
    msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info; 
    fat_file_fd_t     *fat_fd = iop->file_info;

    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
    if (sc != RTEMS_SUCCESSFUL)
        set_errno_and_return_minus_one( EIO );
  
    rc = fat_file_close(iop->pathinfo.mt_entry, fat_fd);
    if (rc != RC_OK)
    {
        rtems_semaphore_release(fs_info->vol_sema);
        return rc; 
    }

    rtems_semaphore_release(fs_info->vol_sema);
    return RC_OK;
}

/*  msdos_format_dirent_with_dot --
 *      This routine convert a (short) MSDOS filename as present on disk 
 *      (fixed 8+3 characters, filled with blanks, without separator dot)
 *      to a "normal" format, with between 0 and 8 name chars, 
 *      a separating dot and up to 3 extension characters
 *   Rules to work:
 *      - copy any (0-8) "name" part characters that are non-blank
 *      - if an extension exists, append a dot
 *      - copy any (0-3) non-blank extension characters
 *      - append a '\0' (dont count it for the rturn code
 *
 * PARAMETERS:
 *     dst: pointer to destination char array (must be big enough)
 *     src: pointer to source characters
 *
 *
 * RETURNS:
 *     the number of bytes (without trailing '\0'(written to destination 
 */
static ssize_t 
msdos_format_dirent_with_dot(char *dst,const char *src)
{
  ssize_t len;
  int i;
  const char *src_tmp;

  /*
   * find last non-blank character of base name
   */
  for ((i       =       MSDOS_SHORT_BASE_LEN  ,
	src_tmp = src + MSDOS_SHORT_BASE_LEN-1);
       ((i > 0) &&
	(*src_tmp == ' '));
       i--,src_tmp--)
    {};
  /*
   * copy base name to destination
   */
  src_tmp = src;
  len = i;
  while (i-- > 0) {
    *dst++ = *src_tmp++;
  }
  /*
   * find last non-blank character of extension
   */
  for ((i       =                            MSDOS_SHORT_EXT_LEN  ,
	src_tmp = src + MSDOS_SHORT_BASE_LEN+MSDOS_SHORT_EXT_LEN-1);
       ((i > 0) && 
	(*src_tmp == ' '));
       i--,src_tmp--)
    {};
  /*
   * extension is not empty
   */
  if (i > 0) {
    *dst++ = '.'; /* append dot */
    len += i + 1; /* extension + dot */
    src_tmp = src + MSDOS_SHORT_BASE_LEN;
    while (i-- > 0) {
      *dst++ = *src_tmp++;
      len++;
    }
  }
  *dst = '\0'; /* terminate string */

  return len;
}

/*  msdos_dir_read --
 *      This routine will read the next directory entry based on the directory
 *      offset. The offset should be equal to -n- time the size of an 
 *      individual dirent structure. If n is not an integer multiple of the 
 *      sizeof a dirent structure, an integer division will be performed to 
 *      determine directory entry that will be returned in the buffer. Count 
 *      should reflect -m- times the sizeof dirent bytes to be placed in the 
 *      buffer.
 *      If there are not -m- dirent elements from the current directory 
 *      position to the end of the exisiting file, the remaining entries will 
 *      be placed in the buffer and the returned value will be equal to 
 *      -m actual- times the size of a directory entry.
 *
 * PARAMETERS:
 *     iop    - file control block
 *     buffer - buffer provided by user
 *     count  - count of bytes to read
 *
 * RETURNS:
 *     the number of bytes read on success, or -1 if error occured (errno 
 *     set apropriately).
 */
ssize_t 
msdos_dir_read(rtems_libio_t *iop, void *buffer, uint32_t   count)
{
    int                rc = RC_OK;
    rtems_status_code  sc = RTEMS_SUCCESSFUL;
    msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info; 
    fat_file_fd_t     *fat_fd = iop->file_info;
    fat_file_fd_t     *tmp_fat_fd = NULL;
    struct dirent      tmp_dirent;
    uint32_t           start = 0;
    ssize_t            ret = 0;
    uint32_t           cmpltd = 0;
    uint32_t           j = 0, i = 0;
    uint32_t           bts2rd = 0;
    uint32_t           cur_cln = 0;
  
    /* 
     * cast start and count - protect against using sizes that are not exact 
     * multiples of the -dirent- size. These could result in unexpected 
     * results 
     */
    start = iop->offset / sizeof(struct dirent);
    count = (count / sizeof(struct dirent)) * sizeof(struct dirent);           
  
    /* 
     * optimization: we know that root directory for FAT12/16 volumes is 
     * sequential set of sectors and any cluster is sequential set of sectors
     * too, so read such set of sectors is quick operation for low-level IO 
     * layer.
     */
    bts2rd = (FAT_FD_OF_ROOT_DIR(fat_fd) &&
             (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) ? 
             fat_fd->fat_file_size                              :
             fs_info->fat.vol.bpc;     

    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
    if (sc != RTEMS_SUCCESSFUL)
        set_errno_and_return_minus_one(EIO);
 
    while (count > 0)
    {  
        /* 
         * fat-file is already opened by open call, so read it 
         * Always read directory fat-file from the beggining because of MSDOS
         * directories feature :( - we should count elements currently 
         * present in the directory because there may be holes :)
         */
        ret = fat_file_read(iop->pathinfo.mt_entry, fat_fd, (j * bts2rd), 
                            bts2rd, fs_info->cl_buf);
        if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
        {
            rtems_semaphore_release(fs_info->vol_sema);
            set_errno_and_return_minus_one(EIO);
        }

        for (i = 0; i < ret; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
        {
            if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == 
                MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
            {
                rtems_semaphore_release(fs_info->vol_sema);          
                return cmpltd;
            }  
            
            if ((*MSDOS_DIR_NAME(fs_info->cl_buf + i)) == 
                MSDOS_THIS_DIR_ENTRY_EMPTY)
                continue;
      
            /* 
             * skip active entries until get the entry to start from
             */
            if (start)
            {
                start--;  
                continue;
            }  
        
            /* 
             * Move the entry to the return buffer
             *
             * unfortunately there is no method to extract ino except to 
             * open fat-file descriptor :( ... so, open it
             */
      
            /* get number of cluster we are working with */
            rc = fat_file_ioctl(iop->pathinfo.mt_entry, fat_fd, F_CLU_NUM,
                                j * bts2rd, &cur_cln);
            if (rc != RC_OK)
            {
                rtems_semaphore_release(fs_info->vol_sema);
                return rc;
            }

            rc = fat_file_open(iop->pathinfo.mt_entry, cur_cln, i, 
                               &tmp_fat_fd);
            if (rc != RC_OK)
            {
                rtems_semaphore_release(fs_info->vol_sema);
                return rc;
            }

            tmp_fat_fd->info_cln = cur_cln;
            tmp_fat_fd->info_ofs = i;

            /* fill in dirent structure */
            /* XXX: from what and in what d_off should be computed ?! */
            tmp_dirent.d_off = start + cmpltd;
            tmp_dirent.d_reclen = sizeof(struct dirent);
            tmp_dirent.d_ino = tmp_fat_fd->ino;
	    /*
	     * convert dir entry from fixed 8+3 format (without dot)
	     * to 0..8 + 1dot + 0..3 format
	     */
	    tmp_dirent.d_namlen = 
	      msdos_format_dirent_with_dot(tmp_dirent.d_name,
					 fs_info->cl_buf + i); /* src text */
            memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent));
   
            iop->offset = iop->offset + sizeof(struct dirent);
            cmpltd += (sizeof(struct dirent));
            count -= (sizeof(struct dirent));
      
            /* inode number extracted, close fat-file */
            rc = fat_file_close(iop->pathinfo.mt_entry, tmp_fat_fd);
            if (rc != RC_OK)
            {
                rtems_semaphore_release(fs_info->vol_sema);
                return rc;
            }

            if (count <= 0)
                break;
        }
        j++;
    }

    rtems_semaphore_release(fs_info->vol_sema);
    return cmpltd;
}

/* msdos_dir_write --
 *     no write for directory
 */

/* msdos_dir_lseek --
 *
 *  This routine will behave in one of three ways based on the state of
 *  argument whence. Based on the state of its value the offset argument will
 *  be interpreted using one of the following methods:
 *
 *     SEEK_SET - offset is the absolute byte offset from the start of the
 *                logical start of the dirent sequence that represents the
 *                directory
 *     SEEK_CUR - offset is used as the relative byte offset from the current
 *                directory position index held in the iop structure
 *     SEEK_END - N/A --> This will cause an assert.
 *
 * PARAMETERS:
 *     iop    - file control block
 *     offset - offset
 *     whence - predefine directive
 *
 * RETURNS:
 *     RC_OK on success, or -1 if error occured (errno 
 *     set apropriately).
 */
int 
msdos_dir_lseek(rtems_libio_t *iop, off_t offset, int whence)
{
    switch (whence) 
    {
        case SEEK_SET:
        case SEEK_CUR: 
            break;
        /* 
         * Movement past the end of the directory via lseek is not a 
         * permitted operation
         */
        case SEEK_END:
        default:
            set_errno_and_return_minus_one( EINVAL );
            break;
    }
    return RC_OK;
}

/* msdos_dir_stat --
 * 
 * This routine will obtain the following information concerning the current
 * directory:
 *     st_dev      device id
 *     st_ino      node serial number :)
 *     st_mode     mode extracted from the node
 *     st_size     total size in bytes
 *     st_blksize  blocksize for filesystem I/O
 *     st_blocks   number of blocks allocated 
 *     stat_mtime  time of last modification
 *
 * PARAMETERS:
 *     loc - this directory
 *     buf - stat buffer provided by user
 *
 * RETURNS:
 *     RC_OK and filled stat buffer on success, or -1 if error occured (errno 
 *     set apropriately).
 */
int 
msdos_dir_stat(
    rtems_filesystem_location_info_t *loc,
    struct stat                      *buf
    )
{
    rtems_status_code  sc = RTEMS_SUCCESSFUL;
    msdos_fs_info_t   *fs_info = loc->mt_entry->fs_info; 
    fat_file_fd_t     *fat_fd = loc->node_access;

    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
    if (sc != RTEMS_SUCCESSFUL)
        set_errno_and_return_minus_one(EIO);
  
    buf->st_dev = fs_info->fat.vol.dev;
    buf->st_ino = fat_fd->ino;
    buf->st_mode  = S_IFDIR;
    buf->st_rdev = 0ll;
    buf->st_size = fat_fd->fat_file_size;
    buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS;
    buf->st_blksize = fs_info->fat.vol.bps;
    buf->st_mtime = fat_fd->mtime;
  
    rtems_semaphore_release(fs_info->vol_sema);
    return RC_OK;
}

/* msdos_dir_truncate --
 *     No truncate for directory.
 *
 * PARAMETERS:
 *
 * RETURNS:
 *
 */

/* msdos_dir_sync --
 *     The following routine does a syncronization on a MSDOS directory node.
 *     DIR_WrtTime, DIR_WrtDate and DIR_fileSize fields of 32 Bytes Directory 
 *     Entry Structure should not be updated for directories, so only call 
 *     to corresponding fat-file routine.
 *
 * PARAMETERS:
 *     iop - file control block
 *
 * RETURNS:
 *     RC_OK on success, or -1 if error occured (errno set apropriately).
 */
int
msdos_dir_sync(rtems_libio_t *iop)
{
    int                rc = RC_OK;
    rtems_status_code  sc = RTEMS_SUCCESSFUL;
    fat_file_fd_t     *fat_fd = iop->file_info;
    msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info;
  
    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
    if (sc != RTEMS_SUCCESSFUL)
        set_errno_and_return_minus_one(EIO);
  
    rc = fat_file_datasync(iop->pathinfo.mt_entry, fat_fd);

    rtems_semaphore_release(fs_info->vol_sema);
    return rc;
}

/* msdos_dir_rmnod --
 *     Remove directory node. 
 *
 *     Check that this directory node is not opened as fat-file, is empty and 
 *     not filesystem root node. If all this conditions met then delete.
 *
 * PARAMETERS:
 *     pathloc - node description
 *
 * RETURNS:
 *     RC_OK on success, or -1 if error occured (errno set apropriately).
 */
int 
msdos_dir_rmnod(rtems_filesystem_location_info_t *pathloc)
{
    int                rc = RC_OK;
    rtems_status_code  sc = RTEMS_SUCCESSFUL;
    msdos_fs_info_t   *fs_info = pathloc->mt_entry->fs_info; 
    fat_file_fd_t     *fat_fd = pathloc->node_access;
    rtems_boolean      is_empty = FALSE;
  
    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
    if (sc != RTEMS_SUCCESSFUL)
        set_errno_and_return_minus_one(EIO);
  
    /*
     * We deny attemp to delete open directory (if directory is current 
     * directory we assume it is open one)
     */
    if (fat_fd->links_num > 1)
    {
        rtems_semaphore_release(fs_info->vol_sema);
        set_errno_and_return_minus_one(EBUSY);
    } 
  
    /*
     * You cannot remove a node that still has children
     */
    rc = msdos_dir_is_empty(pathloc->mt_entry, fat_fd, &is_empty); 
    if (rc != RC_OK)
    {
        rtems_semaphore_release(fs_info->vol_sema);
        return rc;
    }

    if (!is_empty)
    {
        rtems_semaphore_release(fs_info->vol_sema); 
        set_errno_and_return_minus_one(ENOTEMPTY);
    }     

    /*
     * You cannot remove the file system root node.
     */
    if (pathloc->mt_entry->mt_fs_root.node_access == pathloc->node_access)
    {
        rtems_semaphore_release(fs_info->vol_sema);
        set_errno_and_return_minus_one(EBUSY);
    }

    /*
     * You cannot remove a mountpoint.
     * not used - mount() not implemenetd yet.
     */

    /* mark file removed */
    rc = msdos_set_first_char4file_name(pathloc->mt_entry, fat_fd->info_cln, 
                                        fat_fd->info_ofs, 
                                        MSDOS_THIS_DIR_ENTRY_EMPTY);
    if (rc != RC_OK)
    {
        rtems_semaphore_release(fs_info->vol_sema);
        return rc;
    }

    fat_file_mark_removed(pathloc->mt_entry, fat_fd); 

    rtems_semaphore_release(fs_info->vol_sema);
    return rc;
}