Bug Summary

File:obj-scan-build/fatfs/../../fatfs/dir.c
Location:line 539, column 13
Description:Access to field 'attribute' results in a dereference of a null pointer (loaded from variable 'entry')

Annotated Source Code

1/* dir.c - FAT filesystem.
2
3 Copyright (C) 1997, 1998, 1999, 2002, 2003, 2007
4 Free Software Foundation, Inc.
5
6 Written by Thomas Bushnell, n/BSG and Marcus Brinkmann.
7
8 This file is part of the GNU Hurd.
9
10 The GNU Hurd is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2, or (at your option)
13 any later version.
14
15 The GNU Hurd is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
23
24#include <ctype.h>
25#include <string.h>
26#include <dirent.h>
27#include <hurd/fsys.h>
28
29#include "fatfs.h"
30
31/* The size of a directory block is usually just the cluster size.
32 However, the root directory of FAT12/16 file systems is stored in
33 sectors in a special region, so we settle on the greatest common
34 divisor here. */
35#define DIRBLKSIZbytes_per_sector bytes_per_sector
36#define LOG2_DIRBLKSIZlog2_bytes_per_sector log2_bytes_per_sector
37
38enum slot_status
39{
40 /* This means we haven't yet found room for a new entry. */
41 LOOKING,
42
43 /* This means that the specified entry is free and should be used. */
44 TAKE,
45
46 /* This means that the specified entry has enough room at the end
47 to hold the new entry. */
48 SHRINK,
49
50 /* This means that there is enough space in the block, but not in
51 any one single entry, so they all have to be shifted to make
52 room. */
53 COMPRESS,
54
55 /* This means that the directory will have to be grown to hold the
56 entry. */
57 EXTEND,
58
59 /* For removal and rename, this means that this is the location
60 of the entry found. */
61 HERE_TIS,
62};
63
64struct dirstat
65{
66 /* Type of followp operation expected. */
67 enum lookup_type type;
68
69 /* One of the statuses above. */
70 enum slot_status stat;
71
72 /* Mapped address and length of directory. */
73 vm_address_t mapbuf;
74 vm_size_t mapextent;
75
76 /* Index of this directory block. */
77 int idx;
78
79 /* For stat COMPRESS, this is the address (inside mapbuf)
80 of the first direct in the directory block to be compressed. */
81 /* For stat HERE_TIS, SHRINK, and TAKE, this is the entry referenced. */
82 struct dirrect *entry;
83
84 /* For stat HERE_TIS, type REMOVE, this is the address of the immediately
85 previous direct in this directory block, or zero if this is the first. */
86 struct dirrect *preventry;
87
88 /* For stat COMPRESS, this is the number of bytes needed to be copied
89 in order to undertake the compression. */
90 size_t nbytes;
91};
92
93const size_t diskfs_dirstat_size = sizeof (struct dirstat);
94
95/* Initialize DS such that diskfs_drop_dirstat will ignore it. */
96void
97diskfs_null_dirstat (struct dirstat *ds)
98{
99 ds->type = LOOKUP;
100}
101
102/* Forward declaration. */
103static error_t
104dirscanblock (vm_address_t blockoff, struct node *dp, int idx,
105 const char *name, int namelen, enum lookup_type type,
106 struct dirstat *ds, ino_t *inum);
107
108static int
109fatnamematch (const char *dirname, const char *username, size_t unamelen)
110{
111 char *dn = strdup(dirname);
112 int dpos = 0;
113 int upos = 0;
114 int ext = 0;
115
116 /* Deleted files. */
117 if (dn[0] == FAT_DIR_NAME_DELETED'\xe5' || dn[0] == FAT_DIR_NAME_LAST'\x00')
118 return 0;
119 if (dn[0] == FAT_DIR_NAME_REPLACE_DELETED'\x05')
120 dn[0] = FAT_DIR_NAME_DELETED'\xe5';
121
122 /* Special representations for `.' and `..'. */
123 if (!memcmp(dn, FAT_DIR_NAME_DOT". ", 11))
124 return unamelen == 1 && username[0] == '.';
125
126 if (!memcmp (dn, FAT_DIR_NAME_DOTDOT".. ", 11))
127 return unamelen == 2 && username[0] == '.' && username[1] == '.';
128
129 if (unamelen > 12)
130 return 0;
131
132 do
133 {
134 /* First check if we have reached the extension without coming
135 across blanks. */
136 if (dpos == 8 && !ext)
137 {
138 if (username[upos] == '.')
139 {
140 upos++;
141 ext = 1;
142 }
143 else
144 break;
145 }
146 /* Second, skip blanks in base part. */
147 if (dn[dpos] == ' ')
148 {
149 if (ext)
150 break;
151 while (dpos < 8 && dn[++dpos] == ' ');
152 if (username[upos] == '.')
153 upos++;
154 ext = 1;
155 }
156 else
157 {
158 if (tolower(dn[dpos]) == tolower(username[upos]))
159 {
160 dpos++;
161 upos++;
162 }
163 else
164 break;
165 }
166 } while (upos < unamelen && dpos < 11);
167 while (dpos < 11 && dn[dpos] == ' ')
168 dpos++;
169 return (upos == unamelen && dpos == 11);
170}
171
172/* Implement the diskfs_lookup callback from the diskfs library. See
173 <hurd/diskfs.h> for the interface specification. */
174error_t
175diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type,
176 struct node **npp, struct dirstat *ds, struct protid *cred)
177{
178 error_t err;
179 ino_t inum;
180 int namelen;
181 int spec_dotdot;
182 struct node *np = 0;
183 int retry_dotdot = 0;
184 vm_prot_t prot =
185 (type == LOOKUP) ? VM_PROT_READ((vm_prot_t) 0x01) : (VM_PROT_READ((vm_prot_t) 0x01) | VM_PROT_WRITE((vm_prot_t) 0x02));
1
Assuming 'type' is not equal to LOOKUP
2
'?' condition is false
186 memory_object_t memobj;
187 vm_address_t buf = 0;
188 vm_size_t buflen = 0;
189 int blockaddr;
190 int idx, lastidx;
191 int looped;
192
193 if ((type == REMOVE) || (type == RENAME))
3
Assuming 'type' is not equal to REMOVE
4
Assuming 'type' is not equal to RENAME
5
Taking false branch
194 assert (npp)((npp) ? (void) (0) : __assert_fail ("npp", "../../fatfs/dir.c"
, 194, __PRETTY_FUNCTION__))
;
195
196 if (npp)
6
Assuming 'npp' is null
7
Taking false branch
197 *npp = 0;
198
199 spec_dotdot = type & SPEC_DOTDOT0x10000000;
200 type &= ~SPEC_DOTDOT0x10000000;
201
202 namelen = strlen (name);
203
204 if (namelen > FAT_NAME_MAX12)
8
Assuming 'namelen' is <= 12
9
Taking false branch
205 return ENAMETOOLONG((0x10 << 26) | ((63) & 0x3fff));
206
207 try_again:
208 if (ds)
10
Assuming 'ds' is null
11
Taking false branch
209 {
210 ds->type = LOOKUP;
211 ds->mapbuf = 0;
212 ds->mapextent = 0;
213 }
214 if (buf)
12
Taking false branch
215 {
216 munmap ((caddr_t) buf, buflen);
217 buf = 0;
218 }
219 if (ds && (type == CREATE || type == RENAME))
220 ds->stat = LOOKING;
221
222 /* Map in the directory contents. */
223 memobj = diskfs_get_filemap (dp, prot);
224
225 if (memobj == MACH_PORT_NULL((mach_port_t) 0))
13
Assuming 'memobj' is not equal to 0
14
Taking false branch
226 return errno(*__errno_location ());
227
228 buf = 0;
229 /* We allow extra space in case we have to do an EXTEND. */
230 buflen = round_page (dp->dn_stat.st_size + DIRBLKSIZ)((((vm_offset_t) (dp->dn_stat.st_size + bytes_per_sector) +
__vm_page_size - 1) / __vm_page_size) * __vm_page_size)
;
231 err = vm_map (mach_task_self ()((__mach_task_self_ + 0)),
232 &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
233 mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), memobj);
234
235 inum = 0;
236
237 diskfs_set_node_atime (dp);
238
239 /* Start the lookup at DP->dn->dir_idx. */
240 idx = dp->dn->dir_idx;
241 if (idx << LOG2_DIRBLKSIZlog2_bytes_per_sector > dp->dn_stat.st_size)
15
Taking false branch
242 idx = 0; /* just in case */
243 blockaddr = buf + (idx << LOG2_DIRBLKSIZlog2_bytes_per_sector);
244 looped = (idx == 0);
16
Assuming 'idx' is not equal to 0
245 lastidx = idx;
246 if (lastidx == 0)
17
Taking false branch
247 lastidx = dp->dn_stat.st_size >> LOG2_DIRBLKSIZlog2_bytes_per_sector;
248
249 while (!looped || idx < lastidx)
250 {
251 err = dirscanblock (blockaddr, dp, idx, name, namelen, type, ds, &inum);
18
Calling 'dirscanblock'
252 if (!err)
253 {
254 dp->dn->dir_idx = idx;
255 break;
256 }
257 if (err != ENOENT((0x10 << 26) | ((2) & 0x3fff)))
258 {
259 munmap ((caddr_t) buf, buflen);
260 return err;
261 }
262
263 blockaddr += DIRBLKSIZbytes_per_sector;
264 idx++;
265 if (blockaddr - buf >= dp->dn_stat.st_size && !looped)
266 {
267 /* We've gotten to the end; start back at the beginning. */
268 looped = 1;
269 blockaddr = buf;
270 idx = 0;
271 }
272 }
273
274 diskfs_set_node_atime (dp);
275 if (diskfs_synchronous)
276 diskfs_node_update (dp, 1);
277
278 /* If err is set here, it's ENOENT, and we don't want to
279 think about that as an error yet. */
280 err = 0;
281
282 if (inum && npp)
283 {
284 if (namelen != 2 || name[0] != '.' || name[1] != '.')
285 {
286 if (inum == dp->cache_id)
287 {
288 np = dp;
289 diskfs_nref (np);
290 }
291 else
292 {
293 err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
294 if (err)
295 goto out;
296 }
297 }
298
299 /* We are looking up "..". */
300 /* Check to see if this is the root of the filesystem. */
301 else if (dp == diskfs_root_node)
302 {
303 err = EAGAIN((0x10 << 26) | ((35) & 0x3fff));
304 goto out;
305 }
306
307 /* We can't just do diskfs_cached_lookup, because we would then
308 deadlock. So we do this. Ick. */
309 else if (retry_dotdot)
310 {
311 /* Check to see that we got the same answer as last time. */
312 if (inum != retry_dotdot)
313 {
314 /* Drop what we *thought* was .. (but isn't any more) and
315 try *again*. */
316 diskfs_nput (np);
317 pthread_mutex_unlock (&dp->lock);
318 err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
319 pthread_mutex_lock (&dp->lock);
320 if (err)
321 goto out;
322 retry_dotdot = inum;
323 goto try_again;
324 }
325 /* Otherwise, we got it fine and np is already set properly. */
326 }
327 else if (!spec_dotdot)
328 {
329 /* Lock them in the proper order, and then
330 repeat the directory scan to see if this is still
331 right. */
332 pthread_mutex_unlock (&dp->lock);
333 err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
334 pthread_mutex_lock (&dp->lock);
335 if (err)
336 goto out;
337 retry_dotdot = inum;
338 goto try_again;
339 }
340
341 /* Here below are the spec dotdot cases. */
342 else if (type == RENAME || type == REMOVE)
343 np = ifind (inum);
344
345 else if (type == LOOKUP)
346 {
347 diskfs_nput (dp);
348 err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
349 if (err)
350 goto out;
351 }
352 else
353 assert (0)((0) ? (void) (0) : __assert_fail ("0", "../../fatfs/dir.c", 353
, __PRETTY_FUNCTION__))
;
354 }
355
356 if ((type == CREATE || type == RENAME) && !inum && ds && ds->stat == LOOKING)
357 {
358 /* We didn't find any room, so mark ds to extend the dir. */
359 ds->type = CREATE;
360 ds->stat = EXTEND;
361 ds->idx = dp->dn_stat.st_size >> LOG2_DIRBLKSIZlog2_bytes_per_sector;
362 }
363
364 /* Return to the user; if we can't, release the reference
365 (and lock) we acquired above. */
366 out:
367 /* Deallocate or save the mapping. */
368 if ((err && err != ENOENT((0x10 << 26) | ((2) & 0x3fff)))
369 || !ds
370 || ds->type == LOOKUP)
371 {
372 munmap ((caddr_t) buf, buflen);
373 if (ds)
374 ds->type = LOOKUP; /* Set to be ignored by drop_dirstat. */
375 }
376 else
377 {
378 ds->mapbuf = buf;
379 ds->mapextent = buflen;
380 }
381
382 if (np)
383 {
384 assert (npp)((npp) ? (void) (0) : __assert_fail ("npp", "../../fatfs/dir.c"
, 384, __PRETTY_FUNCTION__))
;
385 if (err)
386 {
387 if (!spec_dotdot)
388 {
389 /* Normal case. */
390 if (np == dp)
391 diskfs_nrele (np);
392 else
393 diskfs_nput (np);
394 }
395 else if (type == RENAME || type == REMOVE)
396 /* We just did ifind to get np; that allocates
397 no new references, so we don't have anything to do. */
398 ;
399 else if (type == LOOKUP)
400 /* We did diskfs_cached_lookup. */
401 diskfs_nput (np);
402 }
403 else
404 *npp = np;
405 }
406
407 return err ? : inum ? 0 : ENOENT((0x10 << 26) | ((2) & 0x3fff));
408}
409
410/* Scan block at address BLKADDR (of node DP; block index IDX), for
411 name NAME of length NAMELEN. Args TYPE, DS are as for
412 diskfs_lookup. If found, set *INUM to the inode number, else
413 return ENOENT. */
414static error_t
415dirscanblock (vm_address_t blockaddr, struct node *dp, int idx,
416 const char *name, int namelen, enum lookup_type type,
417 struct dirstat *ds, ino_t *inum)
418{
419 int nfree = 0;
420 int needed = 0;
421 vm_address_t currentoff, prevoff = 0;
422 struct dirrect *entry = 0;
19
Variable 'entry' initialized to a null pointer value
423 size_t nbytes = 0;
424 int looking = 0;
425 int countcopies = 0;
426 int consider_compress = 0;
427 inode_t inode;
428 vi_key_t entry_key = vi_zero_key;
429
430 /* FAT lacks the "." and ".." directory record in the root directory,
431 so we emulate them here. */
432 if (idx == 0 && dp == diskfs_root_node
433 && (fatnamematch (FAT_DIR_NAME_DOT". ", name, namelen)
434 || fatnamematch (FAT_DIR_NAME_DOTDOT".. ", name, namelen)))
435 {
436 entry_key.dir_inode = diskfs_root_node->cache_id;
437 currentoff = blockaddr;
438 }
439 else
440 {
441 if (ds && (ds->stat == LOOKING
442 || ds->stat == COMPRESS))
443 {
444 looking = 1;
445 countcopies = 1;
446 needed = FAT_DIR_RECORDS (namelen)32;
447 }
448
449 for (currentoff = blockaddr, prevoff = 0;
20
Loop condition is false. Execution continues on line 502
450 currentoff < blockaddr + DIRBLKSIZbytes_per_sector;
451 prevoff = currentoff, currentoff += FAT_DIR_REC_LEN32)
452 {
453 entry = (struct dirrect *)currentoff;
454
455 if (looking || countcopies)
456 {
457 int thisfree;
458
459 /* Count how much free space this entry has in it. */
460 if ((char) entry->name[0] == FAT_DIR_NAME_LAST'\x00' ||
461 (char) entry->name[0] == FAT_DIR_NAME_DELETED'\xe5')
462 thisfree = FAT_DIR_REC_LEN32;
463 else
464 thisfree = 0;
465
466 /* If this isn't at the front of the block, then it will
467 have to be copied if we do a compression; count the
468 number of bytes there too. */
469 if (countcopies && currentoff != blockaddr)
470 nbytes += FAT_DIR_REC_LEN32;
471
472 if (ds->stat == COMPRESS && nbytes > ds->nbytes)
473 /* The previously found compress is better than this
474 one, so don't bother counting any more. */
475 countcopies = 0;
476
477 if (thisfree >= needed)
478 {
479 ds->type = CREATE;
480 ds->stat = TAKE;
481 ds->entry = entry;
482 ds->idx = idx;
483 looking = countcopies = 0;
484 }
485 else
486 {
487 nfree += thisfree;
488 if (nfree >= needed)
489 consider_compress = 1;
490 }
491 }
492
493 if (entry->attribute & FAT_DIR_ATTR_LABEL0x08)
494 /* Either the volume label in root dir or a long filename
495 component. */
496 continue;
497
498 if (fatnamematch (entry->name, name, namelen))
499 break;
500 }
501
502 if (consider_compress
503 && (ds->type == LOOKING
504 || (ds->type == COMPRESS && ds->nbytes > nbytes)))
505 {
506 ds->type = CREATE;
507 ds->stat = COMPRESS;
508 ds->entry = (struct dirrect *) blockaddr;
509 ds->idx = idx;
510 ds->nbytes = nbytes;
511 }
512 }
513
514 if (currentoff >= blockaddr + DIRBLKSIZbytes_per_sector)
21
Taking false branch
515 {
516 /* The name is not in this block. */
517
518 return ENOENT((0x10 << 26) | ((2) & 0x3fff));
519 }
520
521 /* We have found the required name. */
522
523 if (ds && type == CREATE)
524 ds->type = LOOKUP; /* It's invalid now. */
525 else if (ds && (type == REMOVE || type == RENAME))
526 {
527 ds->type = type;
528 ds->stat = HERE_TIS;
529 ds->entry = entry;
530 ds->idx = idx;
531 ds->preventry = (struct dirrect *) prevoff;
532 }
533
534 if (entry_key.dir_inode)
22
Taking false branch
535 {
536 /* The required name is "." or ".." in the root dir. */
537 *inum = entry_key.dir_inode;
538 }
539 else if ((entry->attribute & FAT_DIR_ATTR_DIR0x10)
23
Access to field 'attribute' results in a dereference of a null pointer (loaded from variable 'entry')
540 && !memcmp (entry->name, FAT_DIR_NAME_DOT". ", 11))
541 {
542 /* "." and ".." have to be treated special. We don't want their
543 directory records, but the records of the directories they
544 point to. */
545
546 *inum = dp->cache_id;
547 }
548 else if ((entry->attribute & FAT_DIR_ATTR_DIR0x10)
549 && !memcmp (entry->name, FAT_DIR_NAME_DOTDOT".. ", 11))
550 {
551 if (entry->first_cluster_low[0] == 0
552 && entry->first_cluster_low[1] == 0
553 && entry->first_cluster_high[0] == 0
554 && entry->first_cluster_high[1] == 0)
555 {
556 *inum = diskfs_root_node->cache_id;
557 }
558 else
559 {
560 struct vi_key vk = vi_key (dp->dn->inode);
561 *inum = vk.dir_inode;
562 }
563 }
564 else
565 {
566 entry_key.dir_inode = dp->cache_id;
567 entry_key.dir_offset = (currentoff - blockaddr) + (idx << LOG2_DIRBLKSIZlog2_bytes_per_sector);
568 return vi_rlookup(entry_key, inum, &inode, 1);
569 }
570 return 0;
571}
572
573/* Following a lookup call for CREATE, this adds a node to a
574 directory. DP is the directory to be modified; NAME is the name to
575 be entered; NP is the node being linked in; DS is the cached
576 information returned by lookup; CRED describes the user making the
577 call. This call may only be made if the directory has been held
578 locked continuously since the preceding lookup call, and only if
579 that call returned ENOENT. */
580error_t
581diskfs_direnter_hard (struct node *dp, const char *name, struct node *np,
582 struct dirstat *ds, struct protid *cred)
583{
584 struct dirrect *new;
585 int namelen = strlen (name);
586 int needed = FAT_DIR_RECORDS (namelen)32;
587 error_t err;
588 loff_t oldsize = 0;
589
590 assert (ds->type == CREATE)((ds->type == CREATE) ? (void) (0) : __assert_fail ("ds->type == CREATE"
, "../../fatfs/dir.c", 590, __PRETTY_FUNCTION__))
;
591
592 assert (!diskfs_readonly)((!diskfs_readonly) ? (void) (0) : __assert_fail ("!diskfs_readonly"
, "../../fatfs/dir.c", 592, __PRETTY_FUNCTION__))
;
593
594 dp->dn_set_mtime = 1;
595
596 /* Select a location for the new directory entry. Each branch of
597 this switch is responsible for setting NEW to point to the
598 on-disk directory entry being written. */
599
600 switch (ds->stat)
601 {
602 case TAKE:
603 /* We are supposed to consume this slot. */
604 assert ((char)ds->entry->name[0] == FAT_DIR_NAME_LAST(((char)ds->entry->name[0] == '\x00' || (char)ds->entry
->name[0] == '\xe5') ? (void) (0) : __assert_fail ("(char)ds->entry->name[0] == '\\x00' || (char)ds->entry->name[0] == '\\xe5'"
, "../../fatfs/dir.c", 605, __PRETTY_FUNCTION__))
605 || (char)ds->entry->name[0] == FAT_DIR_NAME_DELETED)(((char)ds->entry->name[0] == '\x00' || (char)ds->entry
->name[0] == '\xe5') ? (void) (0) : __assert_fail ("(char)ds->entry->name[0] == '\\x00' || (char)ds->entry->name[0] == '\\xe5'"
, "../../fatfs/dir.c", 605, __PRETTY_FUNCTION__))
;
606
607 new = ds->entry;
608 break;
609
610 case EXTEND:
611 /* Extend the file. */
612 assert (needed <= bytes_per_cluster)((needed <= bytes_per_cluster) ? (void) (0) : __assert_fail
("needed <= bytes_per_cluster", "../../fatfs/dir.c", 612,
__PRETTY_FUNCTION__))
;
613
614 oldsize = dp->dn_stat.st_size;
615 while (oldsize + bytes_per_cluster > dp->allocsize)
616 {
617 err = diskfs_grow (dp, oldsize + bytes_per_cluster, cred);
618 if (err)
619 {
620 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
621 return err;
622 }
623 memset ((caddr_t) ds->mapbuf + oldsize, 0, bytes_per_cluster);
624 }
625
626 new = (struct dirrect *) ((char *) ds->mapbuf + oldsize);
627
628 dp->dn_stat.st_size = oldsize + bytes_per_cluster;
629 dp->dn_set_ctime = 1;
630
631 break;
632
633 case SHRINK:
634 case COMPRESS:
635 default:
636 assert(0)((0) ? (void) (0) : __assert_fail ("0", "../../fatfs/dir.c", 636
, __PRETTY_FUNCTION__))
;
637
638 /* COMPRESS will be used later, with long filenames, but shrink
639 does not make sense on fat, as all entries have fixed
640 size. */
641 }
642
643 /* NEW points to the directory entry being written. Now fill in the
644 data. */
645
646 memcpy (new->name, " ", 11);
647 memcpy (new->name, name, namelen % 11); /* XXX */
648
649 write_word (new->first_cluster_low, np->dn->start_cluster & 0xffff);
650 write_word (new->first_cluster_high, np->dn->start_cluster >> 16);
651 write_dword (new->file_size, np->dn_stat.st_size);
652
653 if (!(name[0] == '.' && (name[1] == '\0'
654 || (name[1] == '.' && name[2] =='\0'))))
655 {
656 vi_key_t entry_key;
657
658 entry_key.dir_inode = dp->cache_id;
659 entry_key.dir_offset = ((int) ds->entry) - ((int) ds->mapbuf);
660
661 /* Set the key for this inode now because it wasn't know when
662 the inode was initialized. */
663 vi_change (vi_lookup (np->cache_id), entry_key);
664
665 if (np->dn_stat.st_mode & S_IFDIR0040000)
666 new->attribute = FAT_DIR_ATTR_DIR0x10;
667 }
668 else
669 new->attribute = FAT_DIR_ATTR_DIR0x10;
670
671 /* Mark the directory inode has having been written. */
672 dp->dn_set_mtime = 1;
673
674 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
675
676 diskfs_file_update (dp, 1);
677
678 return 0;
679}
680
681/* Following a lookup call for REMOVE, this removes the link from the
682 directory. DP is the directory being changed and DS is the cached
683 information returned from lookup. This call is only valid if the
684 directory has been locked continuously since the call to lookup, and
685 only if that call succeeded. */
686error_t
687diskfs_dirremove_hard (struct node *dp, struct dirstat *ds)
688{
689 assert (ds->type == REMOVE)((ds->type == REMOVE) ? (void) (0) : __assert_fail ("ds->type == REMOVE"
, "../../fatfs/dir.c", 689, __PRETTY_FUNCTION__))
;
690 assert (ds->stat == HERE_TIS)((ds->stat == HERE_TIS) ? (void) (0) : __assert_fail ("ds->stat == HERE_TIS"
, "../../fatfs/dir.c", 690, __PRETTY_FUNCTION__))
;
691
692 assert (!diskfs_readonly)((!diskfs_readonly) ? (void) (0) : __assert_fail ("!diskfs_readonly"
, "../../fatfs/dir.c", 692, __PRETTY_FUNCTION__))
;
693
694 dp->dn_set_mtime = 1;
695
696 ds->entry->name[0] = FAT_DIR_NAME_DELETED'\xe5';
697
698 /* XXX Do something with dirrect? inode? */
699
700 dp->dn_set_mtime = 1;
701
702 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
703
704 diskfs_file_update (dp, 1);
705
706 return 0;
707}
708
709/* Following a lookup call for RENAME, this changes the inode number
710 on a directory entry. DP is the directory being changed; NP is the
711 new node being linked in; DP is the cached information returned by
712 lookup. This call is only valid if the directory has been locked
713 continuously since the call to lookup, and only if that call
714 succeeded. */
715error_t
716diskfs_dirrewrite_hard (struct node *dp, struct node *np, struct dirstat *ds)
717{
718 error_t err;
719 vi_key_t entry_key;
720 mach_port_t control = MACH_PORT_NULL((mach_port_t) 0);
721 struct node *oldnp;
722 ino_t inode;
723 inode_t vinode;
724
725 /* We need the inode and vinode of the old node. */
726 entry_key.dir_inode = dp->cache_id;
727 entry_key.dir_offset = ((int) ds->entry) - ((int) ds->mapbuf);
728 err = vi_rlookup (entry_key, &inode, &vinode, 0);
729
730 assert (err != EINVAL)((err != ((0x10 << 26) | ((22) & 0x3fff))) ? (void)
(0) : __assert_fail ("err != ((0x10 << 26) | ((22) & 0x3fff))"
, "../../fatfs/dir.c", 730, __PRETTY_FUNCTION__))
;
731
732 /* Lookup the node, we already have a reference. */
733 oldnp = ifind (inode);
734
735 assert (ds->type == RENAME)((ds->type == RENAME) ? (void) (0) : __assert_fail ("ds->type == RENAME"
, "../../fatfs/dir.c", 735, __PRETTY_FUNCTION__))
;
736 assert (ds->stat == HERE_TIS)((ds->stat == HERE_TIS) ? (void) (0) : __assert_fail ("ds->stat == HERE_TIS"
, "../../fatfs/dir.c", 736, __PRETTY_FUNCTION__))
;
737
738 assert (!diskfs_readonly)((!diskfs_readonly) ? (void) (0) : __assert_fail ("!diskfs_readonly"
, "../../fatfs/dir.c", 738, __PRETTY_FUNCTION__))
;
739
740 /* The link count must be 0 so the file will be removed and
741 the node will be dropped. */
742 oldnp->dn_stat.st_nlink--;
743 assert (!oldnp->dn_stat.st_nlink)((!oldnp->dn_stat.st_nlink) ? (void) (0) : __assert_fail (
"!oldnp->dn_stat.st_nlink", "../../fatfs/dir.c", 743, __PRETTY_FUNCTION__
))
;
744
745 /* Close the file, free the referenced held by clients. */
746 fshelp_fetch_control (&oldnp->transbox, &control);
747
748 if (control)
749 {
750 fsys_goaway (control, FSYS_GOAWAY_UNLINK0x00000008);
751 mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), control);
752 }
753
754 /* Put the new key in the vinode. */
755 vi_change (vi_lookup (np->cache_id), entry_key);
756
757 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
758
759 dp->dn_set_mtime = 1;
760 diskfs_file_update (dp, 1);
761
762 return 0;
763}
764
765/* Tell if DP is an empty directory (has only "." and ".." entries).
766 This routine must be called from inside a catch_exception (). */
767int
768diskfs_dirempty (struct node *dp, struct protid *cred)
769{
770 error_t err;
771 vm_address_t buf = 0, curoff;
772 struct dirrect *entry;
773 int hit = 0; /* Found something in the directory. */
774 memory_object_t memobj = diskfs_get_filemap (dp, VM_PROT_READ((vm_prot_t) 0x01));
775
776 if (memobj == MACH_PORT_NULL((mach_port_t) 0))
777 /* XXX should reflect error properly. */
778 return 0;
779
780 err = vm_map (mach_task_self ()((__mach_task_self_ + 0)), &buf, dp->dn_stat.st_size, 0,
781 1, memobj, 0, 0, VM_PROT_READ((vm_prot_t) 0x01), VM_PROT_READ((vm_prot_t) 0x01), 0);
782 mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), memobj);
783 assert (!err)((!err) ? (void) (0) : __assert_fail ("!err", "../../fatfs/dir.c"
, 783, __PRETTY_FUNCTION__))
;
784
785 diskfs_set_node_atime (dp);
786
787 for (curoff = buf;
788 !hit && curoff < buf + dp->dn_stat.st_size;
789 curoff += FAT_DIR_REC_LEN32)
790 {
791 entry = (struct dirrect *) curoff;
792
793 if (entry->name[0] == FAT_DIR_NAME_LAST'\x00')
794 break;
795 if ((char) entry->name[0] != FAT_DIR_NAME_DELETED'\xe5'
796 && memcmp (entry->name, FAT_DIR_NAME_DOT". ", 11)
797 && memcmp (entry->name, FAT_DIR_NAME_DOTDOT".. ", 11))
798 hit = 1;
799 }
800
801 diskfs_set_node_atime (dp);
802 if (diskfs_synchronous)
803 diskfs_node_update (dp, 1);
804
805 munmap ((caddr_t) buf, dp->dn_stat.st_size);
806
807 return !hit;
808}
809
810/* Make DS an invalid dirstat. */
811error_t
812diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
813{
814 if (ds->type != LOOKUP)
815 {
816 assert (ds->mapbuf)((ds->mapbuf) ? (void) (0) : __assert_fail ("ds->mapbuf"
, "../../fatfs/dir.c", 816, __PRETTY_FUNCTION__))
;
817 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
818 ds->type = LOOKUP;
819 }
820 return 0;
821}
822
823
824/* Implement the diskfs_get_directs callback as described in
825 <hurd/diskfs.h>. */
826error_t
827diskfs_get_directs (struct node *dp,
828 int entry,
829 int nentries,
830 char **data,
831 u_int *datacnt,
832 vm_size_t bufsiz,
833 int *amt)
834{
835 volatile vm_size_t allocsize;
836 struct dirrect *ep;
837 struct dirent *userp;
838 int i;
839 char *datap;
840 volatile int ouralloc = 0;
841 error_t err;
842 vm_prot_t prot = VM_PROT_READ((vm_prot_t) 0x01);
843 memory_object_t memobj;
844 vm_address_t buf = 0, bufp;
845 vm_size_t buflen = 0;
846
847 /* Allocate some space to hold the returned data. */
848 allocsize = bufsiz ? round_page (bufsiz)((((vm_offset_t) (bufsiz) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
: vm_page_size * 4;
849 if (allocsize > *datacnt)
850 {
851 *data = mmap (0, allocsize, PROT_READ0x04|PROT_WRITE0x02, MAP_ANON0x0002, 0, 0);
852 ouralloc = 1;
853 }
854
855 /* Map in the directory contents. */
856 memobj = diskfs_get_filemap (dp, prot);
857
858 if (memobj == MACH_PORT_NULL((mach_port_t) 0))
859 return errno(*__errno_location ());
860
861 /* We allow extra space in case we have to do an EXTEND. */
862 buflen = round_page (dp->dn_stat.st_size)((((vm_offset_t) (dp->dn_stat.st_size) + __vm_page_size - 1
) / __vm_page_size) * __vm_page_size)
;
863 err = vm_map (mach_task_self ()((__mach_task_self_ + 0)),
864 &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
865 mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), memobj);
866
867 bufp = buf;
868 for (i = 0; i < entry; i ++)
869 {
870 /* The root directory in FAT file systems doesn't contain
871 entries for DOT and DOTDOT, they are special cased below. */
872 if (dp == diskfs_root_node && i < 2)
873 continue;
874
875 ep = (struct dirrect *) bufp;
876
877 if (bufp >= buf + buflen || (char)ep->name[0] == FAT_DIR_NAME_LAST'\x00')
878 {
879 /* Not that many entries in the directory; return nothing. */
880 if (allocsize > *datacnt)
881 munmap (data, allocsize);
882 munmap ((caddr_t) buf, buflen);
883 *datacnt = 0;
884 *amt = 0;
885 return 0;
886 }
887
888 /* Ignore and skip deleted and label entries (catches also long
889 filenames). */
890 if ((char)ep->name[0] == FAT_DIR_NAME_DELETED'\xe5'
891 || (ep->attribute & FAT_DIR_ATTR_LABEL0x08))
892 i--;
893 bufp = bufp + FAT_DIR_REC_LEN32;
894 }
895
896 /* Now copy entries one at a time. */
897 i = 0;
898 datap = *data;
899 while (((nentries == -1) || (i < nentries))
900 && (!bufsiz || datap - *data < bufsiz)
901 && bufp < buf + buflen)
902 {
903 char name[13];
904 size_t namlen, reclen;
905 struct dirrect dot = { FAT_DIR_NAME_DOT". ", FAT_DIR_ATTR_DIR0x10 };
906 struct dirrect dotdot = { FAT_DIR_NAME_DOTDOT".. ", FAT_DIR_ATTR_DIR0x10 };
907
908 /* The root directory in FAT file systems doesn't contain
909 entries for DOT and DOTDOT, they are special cased below. */
910 if (dp == diskfs_root_node && (i + entry == 0))
911 ep = &dot;
912 else if (dp == diskfs_root_node && (i + entry == 1))
913 ep = &dotdot;
914 else
915 ep = (struct dirrect *) bufp;
916
917 if ((char)ep->name[0] == FAT_DIR_NAME_LAST'\x00')
918 {
919 /* Last entry. */
920 bufp = buf + buflen;
921 continue;
922 }
923
924 if ((char)ep->name[0] == FAT_DIR_NAME_DELETED'\xe5' || (ep->attribute & FAT_DIR_ATTR_LABEL0x08))
925 {
926 bufp = bufp + FAT_DIR_REC_LEN32;
927 continue;
928 }
929
930 /* See if there's room to hold this one. */
931
932 fat_to_unix_filename(ep->name, name);
933 namlen = strlen(name);
934
935 /* Perhaps downcase it? */
936
937 reclen = sizeof (struct dirent) + namlen;
938 reclen = (reclen + 3) & ~3;
939
940 /* Expand buffer if necessary. */
941 if (datap - *data + reclen > allocsize)
942 {
943 vm_address_t newdata;
944
945 vm_allocate (mach_task_self ()((__mach_task_self_ + 0)), &newdata,
946 (ouralloc
947 ? (allocsize *= 2)
948 : (allocsize = vm_page_size * 2)), 1);
949 memcpy ((void *) newdata, (void *) *data, datap - *data);
950
951 if (ouralloc)
952 munmap (*data, allocsize / 2);
953
954 datap = (char *) newdata + (datap - *data);
955 *data = (char *) newdata;
956 ouralloc = 1;
957 }
958
959 userp = (struct dirent *) datap;
960
961 /* Fill in entry. */
962 {
963 ino_t inode;
964 inode_t v_inode;
965 vi_key_t entry_key;
966
967 entry_key.dir_inode = dp->cache_id;
968 entry_key.dir_offset = bufp - buf;
969
970 vi_rlookup (entry_key, &inode, &v_inode, 1);
971 userp->d_filenod_ino = inode;
972 }
973 userp->d_type = DT_UNKNOWNDT_UNKNOWN;
974 userp->d_reclen = reclen;
975 userp->d_namlen = namlen;
976 memcpy (userp->d_name, name, namlen);
977 userp->d_name[namlen] = '\0';
978
979 /* And move along. */
980 datap = datap + reclen;
981 if (!(dp == diskfs_root_node && i + entry < 2))
982 bufp = bufp + FAT_DIR_REC_LEN32;
983 i++;
984 }
985
986 /* If we didn't use all the pages of a buffer we allocated, free
987 the excess. */
988 if (ouralloc
989 && round_page (datap - *data)((((vm_offset_t) (datap - *data) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
< round_page (allocsize)((((vm_offset_t) (allocsize) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
)
990 munmap ((caddr_t) round_page (datap)((((vm_offset_t) (datap) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
,
991 round_page (allocsize)((((vm_offset_t) (allocsize) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
- round_page (datap - *data)((((vm_offset_t) (datap - *data) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
);
992
993 munmap ((caddr_t) buf, buflen);
994
995 /* Return. */
996 *amt = i;
997 *datacnt = datap - *data;
998 return 0;
999}