Bug Summary

File:obj-scan-build/fatfs/../../fatfs/dir.c
Location:line 124, column 12
Description:Memory is never released; potential leak of memory pointed to by 'dn'

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);
32
Memory is allocated
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')
33
Taking false branch
118 return 0;
119 if (dn[0] == FAT_DIR_NAME_REPLACE_DELETED'\x05')
34
Taking false branch
120 dn[0] = FAT_DIR_NAME_DELETED'\xe5';
121
122 /* Special representations for `.' and `..'. */
123 if (!memcmp(dn, FAT_DIR_NAME_DOT". ", 11))
35
Taking true branch
124 return unamelen == 1 && username[0] == '.';
36
Memory is never released; potential leak of memory pointed to by 'dn'
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 if (err)
15
Assuming 'err' is 0
16
Taking false branch
235 return err;
236
237 inum = 0;
238
239 diskfs_set_node_atime (dp);
240
241 /* Start the lookup at DP->dn->dir_idx. */
242 idx = dp->dn->dir_idx;
243 if (idx << LOG2_DIRBLKSIZlog2_bytes_per_sector > dp->dn_stat.st_size)
17
Taking false branch
244 idx = 0; /* just in case */
245 blockaddr = buf + (idx << LOG2_DIRBLKSIZlog2_bytes_per_sector);
246 looped = (idx == 0);
18
Assuming 'idx' is not equal to 0
247 lastidx = idx;
248 if (lastidx == 0)
19
Taking false branch
249 lastidx = dp->dn_stat.st_size >> LOG2_DIRBLKSIZlog2_bytes_per_sector;
250
251 while (!looped || idx < lastidx)
252 {
253 err = dirscanblock (blockaddr, dp, idx, name, namelen, type, ds, &inum);
26
Calling 'dirscanblock'
254 if (!err)
20
Taking false branch
22
Taking false branch
24
Taking false branch
255 {
256 dp->dn->dir_idx = idx;
257 break;
258 }
259 if (err != ENOENT((0x10 << 26) | ((2) & 0x3fff)))
21
Taking false branch
23
Taking false branch
25
Taking false branch
260 {
261 munmap ((caddr_t) buf, buflen);
262 return err;
263 }
264
265 blockaddr += DIRBLKSIZbytes_per_sector;
266 idx++;
267 if (blockaddr - buf >= dp->dn_stat.st_size && !looped)
268 {
269 /* We've gotten to the end; start back at the beginning. */
270 looped = 1;
271 blockaddr = buf;
272 idx = 0;
273 }
274 }
275
276 diskfs_set_node_atime (dp);
277 if (diskfs_synchronous)
278 diskfs_node_update (dp, 1);
279
280 /* If err is set here, it's ENOENT, and we don't want to
281 think about that as an error yet. */
282 err = 0;
283
284 if (inum && npp)
285 {
286 if (namelen != 2 || name[0] != '.' || name[1] != '.')
287 {
288 if (inum == dp->cache_id)
289 {
290 np = dp;
291 diskfs_nref (np);
292 }
293 else
294 {
295 err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
296 if (err)
297 goto out;
298 }
299 }
300
301 /* We are looking up "..". */
302 /* Check to see if this is the root of the filesystem. */
303 else if (dp == diskfs_root_node)
304 {
305 err = EAGAIN((0x10 << 26) | ((35) & 0x3fff));
306 goto out;
307 }
308
309 /* We can't just do diskfs_cached_lookup, because we would then
310 deadlock. So we do this. Ick. */
311 else if (retry_dotdot)
312 {
313 /* Check to see that we got the same answer as last time. */
314 if (inum != retry_dotdot)
315 {
316 /* Drop what we *thought* was .. (but isn't any more) and
317 try *again*. */
318 diskfs_nput (np);
319 pthread_mutex_unlock (&dp->lock);
320 err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
321 pthread_mutex_lock (&dp->lock);
322 if (err)
323 goto out;
324 retry_dotdot = inum;
325 goto try_again;
326 }
327 /* Otherwise, we got it fine and np is already set properly. */
328 }
329 else if (!spec_dotdot)
330 {
331 /* Lock them in the proper order, and then
332 repeat the directory scan to see if this is still
333 right. */
334 pthread_mutex_unlock (&dp->lock);
335 err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
336 pthread_mutex_lock (&dp->lock);
337 if (err)
338 goto out;
339 retry_dotdot = inum;
340 goto try_again;
341 }
342
343 /* Here below are the spec dotdot cases. */
344 else if (type == RENAME || type == REMOVE)
345 np = ifind (inum);
346
347 else if (type == LOOKUP)
348 {
349 diskfs_nput (dp);
350 err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
351 if (err)
352 goto out;
353 }
354 else
355 assert (0)((0) ? (void) (0) : __assert_fail ("0", "../../fatfs/dir.c", 355
, __PRETTY_FUNCTION__))
;
356 }
357
358 if ((type == CREATE || type == RENAME) && !inum && ds && ds->stat == LOOKING)
359 {
360 /* We didn't find any room, so mark ds to extend the dir. */
361 ds->type = CREATE;
362 ds->stat = EXTEND;
363 ds->idx = dp->dn_stat.st_size >> LOG2_DIRBLKSIZlog2_bytes_per_sector;
364 }
365
366 /* Return to the user; if we can't, release the reference
367 (and lock) we acquired above. */
368 out:
369 /* Deallocate or save the mapping. */
370 if ((err && err != ENOENT((0x10 << 26) | ((2) & 0x3fff)))
371 || !ds
372 || ds->type == LOOKUP)
373 {
374 munmap ((caddr_t) buf, buflen);
375 if (ds)
376 ds->type = LOOKUP; /* Set to be ignored by drop_dirstat. */
377 }
378 else
379 {
380 ds->mapbuf = buf;
381 ds->mapextent = buflen;
382 }
383
384 if (np)
385 {
386 assert (npp)((npp) ? (void) (0) : __assert_fail ("npp", "../../fatfs/dir.c"
, 386, __PRETTY_FUNCTION__))
;
387 if (err)
388 {
389 if (!spec_dotdot)
390 {
391 /* Normal case. */
392 if (np == dp)
393 diskfs_nrele (np);
394 else
395 diskfs_nput (np);
396 }
397 else if (type == RENAME || type == REMOVE)
398 /* We just did ifind to get np; that allocates
399 no new references, so we don't have anything to do. */
400 ;
401 else if (type == LOOKUP)
402 /* We did diskfs_cached_lookup. */
403 diskfs_nput (np);
404 }
405 else
406 *npp = np;
407 }
408
409 return err ? : inum ? 0 : ENOENT((0x10 << 26) | ((2) & 0x3fff));
410}
411
412/* Scan block at address BLKADDR (of node DP; block index IDX), for
413 name NAME of length NAMELEN. Args TYPE, DS are as for
414 diskfs_lookup. If found, set *INUM to the inode number, else
415 return ENOENT. */
416static error_t
417dirscanblock (vm_address_t blockaddr, struct node *dp, int idx,
418 const char *name, int namelen, enum lookup_type type,
419 struct dirstat *ds, ino_t *inum)
420{
421 int nfree = 0;
422 int needed = 0;
423 vm_address_t currentoff, prevoff = 0;
424 struct dirrect *entry = 0;
425 size_t nbytes = 0;
426 int looking = 0;
427 int countcopies = 0;
428 int consider_compress = 0;
429 inode_t inode;
430 vi_key_t entry_key = vi_zero_key;
431
432 /* FAT lacks the "." and ".." directory record in the root directory,
433 so we emulate them here. */
434 if (idx == 0 && dp == diskfs_root_node
27
Assuming 'idx' is not equal to 0
435 && (fatnamematch (FAT_DIR_NAME_DOT". ", name, namelen)
436 || fatnamematch (FAT_DIR_NAME_DOTDOT".. ", name, namelen)))
437 {
438 entry_key.dir_inode = diskfs_root_node->cache_id;
439 currentoff = blockaddr;
440 }
441 else
442 {
443 if (ds && (ds->stat == LOOKING
444 || ds->stat == COMPRESS))
445 {
446 looking = 1;
447 countcopies = 1;
448 needed = FAT_DIR_RECORDS (namelen)32;
449 }
450
451 for (currentoff = blockaddr, prevoff = 0;
28
Loop condition is true. Entering loop body
452 currentoff < blockaddr + DIRBLKSIZbytes_per_sector;
453 prevoff = currentoff, currentoff += FAT_DIR_REC_LEN32)
454 {
455 entry = (struct dirrect *)currentoff;
456
457 if (looking || countcopies)
29
Taking false branch
458 {
459 int thisfree;
460
461 /* Count how much free space this entry has in it. */
462 if ((char) entry->name[0] == FAT_DIR_NAME_LAST'\x00' ||
463 (char) entry->name[0] == FAT_DIR_NAME_DELETED'\xe5')
464 thisfree = FAT_DIR_REC_LEN32;
465 else
466 thisfree = 0;
467
468 /* If this isn't at the front of the block, then it will
469 have to be copied if we do a compression; count the
470 number of bytes there too. */
471 if (countcopies && currentoff != blockaddr)
472 nbytes += FAT_DIR_REC_LEN32;
473
474 if (ds->stat == COMPRESS && nbytes > ds->nbytes)
475 /* The previously found compress is better than this
476 one, so don't bother counting any more. */
477 countcopies = 0;
478
479 if (thisfree >= needed)
480 {
481 ds->type = CREATE;
482 ds->stat = TAKE;
483 ds->entry = entry;
484 ds->idx = idx;
485 looking = countcopies = 0;
486 }
487 else
488 {
489 nfree += thisfree;
490 if (nfree >= needed)
491 consider_compress = 1;
492 }
493 }
494
495 if (entry->attribute & FAT_DIR_ATTR_LABEL0x08)
30
Taking false branch
496 /* Either the volume label in root dir or a long filename
497 component. */
498 continue;
499
500 if (fatnamematch (entry->name, name, namelen))
31
Calling 'fatnamematch'
501 break;
502 }
503
504 if (consider_compress
505 && (ds->type == LOOKING
506 || (ds->type == COMPRESS && ds->nbytes > nbytes)))
507 {
508 ds->type = CREATE;
509 ds->stat = COMPRESS;
510 ds->entry = (struct dirrect *) blockaddr;
511 ds->idx = idx;
512 ds->nbytes = nbytes;
513 }
514 }
515
516 if (currentoff >= blockaddr + DIRBLKSIZbytes_per_sector)
517 {
518 /* The name is not in this block. */
519
520 return ENOENT((0x10 << 26) | ((2) & 0x3fff));
521 }
522
523 /* We have found the required name. */
524
525 if (ds && type == CREATE)
526 ds->type = LOOKUP; /* It's invalid now. */
527 else if (ds && (type == REMOVE || type == RENAME))
528 {
529 ds->type = type;
530 ds->stat = HERE_TIS;
531 ds->entry = entry;
532 ds->idx = idx;
533 ds->preventry = (struct dirrect *) prevoff;
534 }
535
536 if (entry_key.dir_inode)
537 {
538 /* The required name is "." or ".." in the root dir. */
539 *inum = entry_key.dir_inode;
540 }
541 else if ((entry->attribute & FAT_DIR_ATTR_DIR0x10)
542 && !memcmp (entry->name, FAT_DIR_NAME_DOT". ", 11))
543 {
544 /* "." and ".." have to be treated special. We don't want their
545 directory records, but the records of the directories they
546 point to. */
547
548 *inum = dp->cache_id;
549 }
550 else if ((entry->attribute & FAT_DIR_ATTR_DIR0x10)
551 && !memcmp (entry->name, FAT_DIR_NAME_DOTDOT".. ", 11))
552 {
553 if (entry->first_cluster_low[0] == 0
554 && entry->first_cluster_low[1] == 0
555 && entry->first_cluster_high[0] == 0
556 && entry->first_cluster_high[1] == 0)
557 {
558 *inum = diskfs_root_node->cache_id;
559 }
560 else
561 {
562 struct vi_key vk = vi_key (dp->dn->inode);
563 *inum = vk.dir_inode;
564 }
565 }
566 else
567 {
568 entry_key.dir_inode = dp->cache_id;
569 entry_key.dir_offset = (currentoff - blockaddr) + (idx << LOG2_DIRBLKSIZlog2_bytes_per_sector);
570 return vi_rlookup(entry_key, inum, &inode, 1);
571 }
572 return 0;
573}
574
575/* Following a lookup call for CREATE, this adds a node to a
576 directory. DP is the directory to be modified; NAME is the name to
577 be entered; NP is the node being linked in; DS is the cached
578 information returned by lookup; CRED describes the user making the
579 call. This call may only be made if the directory has been held
580 locked continuously since the preceding lookup call, and only if
581 that call returned ENOENT. */
582error_t
583diskfs_direnter_hard (struct node *dp, const char *name, struct node *np,
584 struct dirstat *ds, struct protid *cred)
585{
586 struct dirrect *new;
587 int namelen = strlen (name);
588 int needed = FAT_DIR_RECORDS (namelen)32;
589 error_t err;
590 loff_t oldsize = 0;
591
592 assert (ds->type == CREATE)((ds->type == CREATE) ? (void) (0) : __assert_fail ("ds->type == CREATE"
, "../../fatfs/dir.c", 592, __PRETTY_FUNCTION__))
;
593
594 assert (!diskfs_readonly)((!diskfs_readonly) ? (void) (0) : __assert_fail ("!diskfs_readonly"
, "../../fatfs/dir.c", 594, __PRETTY_FUNCTION__))
;
595
596 dp->dn_set_mtime = 1;
597
598 /* Select a location for the new directory entry. Each branch of
599 this switch is responsible for setting NEW to point to the
600 on-disk directory entry being written. */
601
602 switch (ds->stat)
603 {
604 case TAKE:
605 /* We are supposed to consume this slot. */
606 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", 607, __PRETTY_FUNCTION__))
607 || (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", 607, __PRETTY_FUNCTION__))
;
608
609 new = ds->entry;
610 break;
611
612 case EXTEND:
613 /* Extend the file. */
614 assert (needed <= bytes_per_cluster)((needed <= bytes_per_cluster) ? (void) (0) : __assert_fail
("needed <= bytes_per_cluster", "../../fatfs/dir.c", 614,
__PRETTY_FUNCTION__))
;
615
616 oldsize = dp->dn_stat.st_size;
617 while (oldsize + bytes_per_cluster > dp->allocsize)
618 {
619 err = diskfs_grow (dp, oldsize + bytes_per_cluster, cred);
620 if (err)
621 {
622 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
623 return err;
624 }
625 memset ((caddr_t) ds->mapbuf + oldsize, 0, bytes_per_cluster);
626 }
627
628 new = (struct dirrect *) ((char *) ds->mapbuf + oldsize);
629
630 dp->dn_stat.st_size = oldsize + bytes_per_cluster;
631 dp->dn_set_ctime = 1;
632
633 break;
634
635 case SHRINK:
636 case COMPRESS:
637 default:
638 assert(0)((0) ? (void) (0) : __assert_fail ("0", "../../fatfs/dir.c", 638
, __PRETTY_FUNCTION__))
;
639
640 /* COMPRESS will be used later, with long filenames, but shrink
641 does not make sense on fat, as all entries have fixed
642 size. */
643 }
644
645 /* NEW points to the directory entry being written. Now fill in the
646 data. */
647
648 memcpy (new->name, " ", 11);
649 memcpy (new->name, name, namelen % 11); /* XXX */
650
651 write_word (new->first_cluster_low, np->dn->start_cluster & 0xffff);
652 write_word (new->first_cluster_high, np->dn->start_cluster >> 16);
653 write_dword (new->file_size, np->dn_stat.st_size);
654
655 if (!(name[0] == '.' && (name[1] == '\0'
656 || (name[1] == '.' && name[2] =='\0'))))
657 {
658 vi_key_t entry_key;
659
660 entry_key.dir_inode = dp->cache_id;
661 entry_key.dir_offset = ((int) ds->entry) - ((int) ds->mapbuf);
662
663 /* Set the key for this inode now because it wasn't know when
664 the inode was initialized. */
665 vi_change (vi_lookup (np->cache_id), entry_key);
666
667 if (np->dn_stat.st_mode & S_IFDIR0040000)
668 new->attribute = FAT_DIR_ATTR_DIR0x10;
669 }
670 else
671 new->attribute = FAT_DIR_ATTR_DIR0x10;
672
673 /* Mark the directory inode has having been written. */
674 dp->dn_set_mtime = 1;
675
676 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
677
678 diskfs_file_update (dp, 1);
679
680 return 0;
681}
682
683/* Following a lookup call for REMOVE, this removes the link from the
684 directory. DP is the directory being changed and DS is the cached
685 information returned from lookup. This call is only valid if the
686 directory has been locked continuously since the call to lookup, and
687 only if that call succeeded. */
688error_t
689diskfs_dirremove_hard (struct node *dp, struct dirstat *ds)
690{
691 assert (ds->type == REMOVE)((ds->type == REMOVE) ? (void) (0) : __assert_fail ("ds->type == REMOVE"
, "../../fatfs/dir.c", 691, __PRETTY_FUNCTION__))
;
692 assert (ds->stat == HERE_TIS)((ds->stat == HERE_TIS) ? (void) (0) : __assert_fail ("ds->stat == HERE_TIS"
, "../../fatfs/dir.c", 692, __PRETTY_FUNCTION__))
;
693
694 assert (!diskfs_readonly)((!diskfs_readonly) ? (void) (0) : __assert_fail ("!diskfs_readonly"
, "../../fatfs/dir.c", 694, __PRETTY_FUNCTION__))
;
695
696 dp->dn_set_mtime = 1;
697
698 ds->entry->name[0] = FAT_DIR_NAME_DELETED'\xe5';
699
700 /* XXX Do something with dirrect? inode? */
701
702 dp->dn_set_mtime = 1;
703
704 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
705
706 diskfs_file_update (dp, 1);
707
708 return 0;
709}
710
711/* Following a lookup call for RENAME, this changes the inode number
712 on a directory entry. DP is the directory being changed; NP is the
713 new node being linked in; DP is the cached information returned by
714 lookup. This call is only valid if the directory has been locked
715 continuously since the call to lookup, and only if that call
716 succeeded. */
717error_t
718diskfs_dirrewrite_hard (struct node *dp, struct node *np, struct dirstat *ds)
719{
720 error_t err;
721 vi_key_t entry_key;
722 mach_port_t control = MACH_PORT_NULL((mach_port_t) 0);
723 struct node *oldnp;
724 ino_t inode;
725 inode_t vinode;
726
727 /* We need the inode and vinode of the old node. */
728 entry_key.dir_inode = dp->cache_id;
729 entry_key.dir_offset = ((int) ds->entry) - ((int) ds->mapbuf);
730 err = vi_rlookup (entry_key, &inode, &vinode, 0);
731
732 assert (err != EINVAL)((err != ((0x10 << 26) | ((22) & 0x3fff))) ? (void)
(0) : __assert_fail ("err != ((0x10 << 26) | ((22) & 0x3fff))"
, "../../fatfs/dir.c", 732, __PRETTY_FUNCTION__))
;
733
734 /* Lookup the node, we already have a reference. */
735 oldnp = ifind (inode);
736
737 assert (ds->type == RENAME)((ds->type == RENAME) ? (void) (0) : __assert_fail ("ds->type == RENAME"
, "../../fatfs/dir.c", 737, __PRETTY_FUNCTION__))
;
738 assert (ds->stat == HERE_TIS)((ds->stat == HERE_TIS) ? (void) (0) : __assert_fail ("ds->stat == HERE_TIS"
, "../../fatfs/dir.c", 738, __PRETTY_FUNCTION__))
;
739
740 assert (!diskfs_readonly)((!diskfs_readonly) ? (void) (0) : __assert_fail ("!diskfs_readonly"
, "../../fatfs/dir.c", 740, __PRETTY_FUNCTION__))
;
741
742 /* The link count must be 0 so the file will be removed and
743 the node will be dropped. */
744 oldnp->dn_stat.st_nlink--;
745 assert (!oldnp->dn_stat.st_nlink)((!oldnp->dn_stat.st_nlink) ? (void) (0) : __assert_fail (
"!oldnp->dn_stat.st_nlink", "../../fatfs/dir.c", 745, __PRETTY_FUNCTION__
))
;
746
747 /* Close the file, free the referenced held by clients. */
748 fshelp_fetch_control (&oldnp->transbox, &control);
749
750 if (control)
751 {
752 fsys_goaway (control, FSYS_GOAWAY_UNLINK0x00000008);
753 mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), control);
754 }
755
756 /* Put the new key in the vinode. */
757 vi_change (vi_lookup (np->cache_id), entry_key);
758
759 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
760
761 dp->dn_set_mtime = 1;
762 diskfs_file_update (dp, 1);
763
764 return 0;
765}
766
767/* Tell if DP is an empty directory (has only "." and ".." entries).
768 This routine must be called from inside a catch_exception (). */
769int
770diskfs_dirempty (struct node *dp, struct protid *cred)
771{
772 error_t err;
773 vm_address_t buf = 0, curoff;
774 struct dirrect *entry;
775 int hit = 0; /* Found something in the directory. */
776 memory_object_t memobj = diskfs_get_filemap (dp, VM_PROT_READ((vm_prot_t) 0x01));
777
778 if (memobj == MACH_PORT_NULL((mach_port_t) 0))
779 /* XXX should reflect error properly. */
780 return 0;
781
782 err = vm_map (mach_task_self ()((__mach_task_self_ + 0)), &buf, dp->dn_stat.st_size, 0,
783 1, memobj, 0, 0, VM_PROT_READ((vm_prot_t) 0x01), VM_PROT_READ((vm_prot_t) 0x01), 0);
784 mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), memobj);
785 assert (!err)((!err) ? (void) (0) : __assert_fail ("!err", "../../fatfs/dir.c"
, 785, __PRETTY_FUNCTION__))
;
786
787 diskfs_set_node_atime (dp);
788
789 for (curoff = buf;
790 !hit && curoff < buf + dp->dn_stat.st_size;
791 curoff += FAT_DIR_REC_LEN32)
792 {
793 entry = (struct dirrect *) curoff;
794
795 if (entry->name[0] == FAT_DIR_NAME_LAST'\x00')
796 break;
797 if ((char) entry->name[0] != FAT_DIR_NAME_DELETED'\xe5'
798 && memcmp (entry->name, FAT_DIR_NAME_DOT". ", 11)
799 && memcmp (entry->name, FAT_DIR_NAME_DOTDOT".. ", 11))
800 hit = 1;
801 }
802
803 diskfs_set_node_atime (dp);
804 if (diskfs_synchronous)
805 diskfs_node_update (dp, 1);
806
807 munmap ((caddr_t) buf, dp->dn_stat.st_size);
808
809 return !hit;
810}
811
812/* Make DS an invalid dirstat. */
813error_t
814diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
815{
816 if (ds->type != LOOKUP)
817 {
818 assert (ds->mapbuf)((ds->mapbuf) ? (void) (0) : __assert_fail ("ds->mapbuf"
, "../../fatfs/dir.c", 818, __PRETTY_FUNCTION__))
;
819 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
820 ds->type = LOOKUP;
821 }
822 return 0;
823}
824
825
826/* Implement the diskfs_get_directs callback as described in
827 <hurd/diskfs.h>. */
828error_t
829diskfs_get_directs (struct node *dp,
830 int entry,
831 int nentries,
832 char **data,
833 u_int *datacnt,
834 vm_size_t bufsiz,
835 int *amt)
836{
837 volatile vm_size_t allocsize;
838 struct dirrect *ep;
839 struct dirent *userp;
840 int i;
841 char *datap;
842 volatile int ouralloc = 0;
843 error_t err;
844 vm_prot_t prot = VM_PROT_READ((vm_prot_t) 0x01);
845 memory_object_t memobj;
846 vm_address_t buf = 0, bufp;
847 vm_size_t buflen = 0;
848
849 /* Allocate some space to hold the returned data. */
850 allocsize = bufsiz ? round_page (bufsiz)((((vm_offset_t) (bufsiz) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
: vm_page_size * 4;
851 if (allocsize > *datacnt)
852 {
853 *data = mmap (0, allocsize, PROT_READ0x04|PROT_WRITE0x02, MAP_ANON0x0002, 0, 0);
854 ouralloc = 1;
855 }
856
857 /* Map in the directory contents. */
858 memobj = diskfs_get_filemap (dp, prot);
859
860 if (memobj == MACH_PORT_NULL((mach_port_t) 0))
861 return errno(*__errno_location ());
862
863 /* We allow extra space in case we have to do an EXTEND. */
864 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)
;
865 err = vm_map (mach_task_self ()((__mach_task_self_ + 0)),
866 &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
867 mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), memobj);
868 if (err)
869 return err;
870
871 bufp = buf;
872 for (i = 0; i < entry; i ++)
873 {
874 /* The root directory in FAT file systems doesn't contain
875 entries for DOT and DOTDOT, they are special cased below. */
876 if (dp == diskfs_root_node && i < 2)
877 continue;
878
879 ep = (struct dirrect *) bufp;
880
881 if (bufp >= buf + buflen || (char)ep->name[0] == FAT_DIR_NAME_LAST'\x00')
882 {
883 /* Not that many entries in the directory; return nothing. */
884 if (allocsize > *datacnt)
885 munmap (data, allocsize);
886 munmap ((caddr_t) buf, buflen);
887 *datacnt = 0;
888 *amt = 0;
889 return 0;
890 }
891
892 /* Ignore and skip deleted and label entries (catches also long
893 filenames). */
894 if ((char)ep->name[0] == FAT_DIR_NAME_DELETED'\xe5'
895 || (ep->attribute & FAT_DIR_ATTR_LABEL0x08))
896 i--;
897 bufp = bufp + FAT_DIR_REC_LEN32;
898 }
899
900 /* Now copy entries one at a time. */
901 i = 0;
902 datap = *data;
903 while (((nentries == -1) || (i < nentries))
904 && (!bufsiz || datap - *data < bufsiz)
905 && bufp < buf + buflen)
906 {
907 char name[13];
908 size_t namlen, reclen;
909 struct dirrect dot = { FAT_DIR_NAME_DOT". ", FAT_DIR_ATTR_DIR0x10 };
910 struct dirrect dotdot = { FAT_DIR_NAME_DOTDOT".. ", FAT_DIR_ATTR_DIR0x10 };
911
912 /* The root directory in FAT file systems doesn't contain
913 entries for DOT and DOTDOT, they are special cased below. */
914 if (dp == diskfs_root_node && (i + entry == 0))
915 ep = &dot;
916 else if (dp == diskfs_root_node && (i + entry == 1))
917 ep = &dotdot;
918 else
919 ep = (struct dirrect *) bufp;
920
921 if ((char)ep->name[0] == FAT_DIR_NAME_LAST'\x00')
922 {
923 /* Last entry. */
924 bufp = buf + buflen;
925 continue;
926 }
927
928 if ((char)ep->name[0] == FAT_DIR_NAME_DELETED'\xe5' || (ep->attribute & FAT_DIR_ATTR_LABEL0x08))
929 {
930 bufp = bufp + FAT_DIR_REC_LEN32;
931 continue;
932 }
933
934 /* See if there's room to hold this one. */
935
936 fat_to_unix_filename(ep->name, name);
937 namlen = strlen(name);
938
939 /* Perhaps downcase it? */
940
941 reclen = sizeof (struct dirent) + namlen;
942 reclen = (reclen + 3) & ~3;
943
944 /* Expand buffer if necessary. */
945 if (datap - *data + reclen > allocsize)
946 {
947 vm_address_t newdata;
948
949 vm_allocate (mach_task_self ()((__mach_task_self_ + 0)), &newdata,
950 (ouralloc
951 ? (allocsize *= 2)
952 : (allocsize = vm_page_size * 2)), 1);
953 memcpy ((void *) newdata, (void *) *data, datap - *data);
954
955 if (ouralloc)
956 munmap (*data, allocsize / 2);
957
958 datap = (char *) newdata + (datap - *data);
959 *data = (char *) newdata;
960 ouralloc = 1;
961 }
962
963 userp = (struct dirent *) datap;
964
965 /* Fill in entry. */
966 {
967 ino_t inode;
968 inode_t v_inode;
969 vi_key_t entry_key;
970
971 entry_key.dir_inode = dp->cache_id;
972 entry_key.dir_offset = bufp - buf;
973
974 vi_rlookup (entry_key, &inode, &v_inode, 1);
975 userp->d_filenod_ino = inode;
976 }
977 userp->d_type = DT_UNKNOWNDT_UNKNOWN;
978 userp->d_reclen = reclen;
979 userp->d_namlen = namlen;
980 memcpy (userp->d_name, name, namlen);
981 userp->d_name[namlen] = '\0';
982
983 /* And move along. */
984 datap = datap + reclen;
985 if (!(dp == diskfs_root_node && i + entry < 2))
986 bufp = bufp + FAT_DIR_REC_LEN32;
987 i++;
988 }
989
990 /* If we didn't use all the pages of a buffer we allocated, free
991 the excess. */
992 if (ouralloc
993 && 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)
)
994 munmap ((caddr_t) round_page (datap)((((vm_offset_t) (datap) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
,
995 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)
);
996
997 munmap ((caddr_t) buf, buflen);
998
999 /* Return. */
1000 *amt = i;
1001 *datacnt = datap - *data;
1002 return 0;
1003}