Bug Summary

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

Annotated Source Code

1/* Directory management routines
2
3 Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2007
4 Free Software Foundation, Inc.
5
6 Converted for ext2fs by Miles Bader <miles@gnu.org>
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2, or (at
11 your option) any later version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
21
22#include "ext2fs.h"
23
24#include <string.h>
25#include <stdio.h>
26#include <dirent.h>
27#include <stddef.h>
28
29/* This isn't quite right because a file system block may straddle several
30 device blocks, and so a write failure between writing two device blocks
31 may scramble things up a bit. But the linux doesn't do this. We could
32 try and make sure that we never wrote any modified directories with
33 entries that straddle device blocks (but read those that do)... */
34#define DIRBLKSIZblock_size block_size
35
36enum slot_status
37{
38 /* This means we haven't yet found room for a new entry. */
39 LOOKING,
40
41 /* This means that the specified entry is free and should be used. */
42 TAKE,
43
44 /* This means that the specified entry has enough room at the end
45 to hold the new entry. */
46 SHRINK,
47
48 /* This means that there is enough space in the block, but not in
49 any one single entry, so they all have to be shifted to make
50 room. */
51 COMPRESS,
52
53 /* This means that the directory will have to be grown to hold the
54 entry. */
55 EXTEND,
56
57 /* For removal and rename, this means that this is the location
58 of the entry found. */
59 HERE_TIS,
60};
61
62struct dirstat
63{
64 /* Type of followp operation expected */
65 enum lookup_type type;
66
67 /* One of the statuses above */
68 enum slot_status stat;
69
70 /* Mapped address and length of directory */
71 vm_address_t mapbuf;
72 vm_size_t mapextent;
73
74 /* Index of this directory block. */
75 int idx;
76
77 /* For stat COMPRESS, this is the address (inside mapbuf)
78 of the first direct in the directory block to be compressed. */
79 /* For stat HERE_TIS, SHRINK, and TAKE, this is the entry referenced. */
80 struct ext2_dir_entry_2 *entry;
81
82 /* For stat HERE_TIS, type REMOVE, this is the address of the immediately
83 previous direct in this directory block, or zero if this is the first. */
84 struct ext2_dir_entry_2 *preventry;
85
86 /* For stat COMPRESS, this is the number of bytes needed to be copied
87 in order to undertake the compression. */
88 size_t nbytes;
89};
90
91const size_t diskfs_dirstat_size = sizeof (struct dirstat);
92
93/* Initialize DS such that diskfs_drop_dirstat will ignore it. */
94void
95diskfs_null_dirstat (struct dirstat *ds)
96{
97 ds->type = LOOKUP;
98}
99
100static error_t
101dirscanblock (vm_address_t blockoff, struct node *dp, int idx,
102 const char *name, int namelen, enum lookup_type type,
103 struct dirstat *ds, ino_t *inum);
104
105
106#if 0 /* XXX unused for now */
107static const unsigned char ext2_file_type[EXT2_FT_MAX8] =
108{
109 [EXT2_FT_UNKNOWN0] = DT_UNKNOWNDT_UNKNOWN,
110 [EXT2_FT_REG_FILE1] = DT_REGDT_REG,
111 [EXT2_FT_DIR2] = DT_DIRDT_DIR,
112 [EXT2_FT_CHRDEV3] = DT_CHRDT_CHR,
113 [EXT2_FT_BLKDEV4] = DT_BLKDT_BLK,
114 [EXT2_FT_FIFO5] = DT_FIFODT_FIFO,
115 [EXT2_FT_SOCK6] = DT_SOCKDT_SOCK,
116 [EXT2_FT_SYMLINK7] = DT_LNKDT_LNK,
117};
118
119static const unsigned char file_type_ext2[] =
120{
121 [DT_UNKNOWNDT_UNKNOWN] = EXT2_FT_UNKNOWN0,
122 [DT_REGDT_REG] = EXT2_FT_REG_FILE1,
123 [DT_DIRDT_DIR] = EXT2_FT_DIR2,
124 [DT_CHRDT_CHR] = EXT2_FT_CHRDEV3,
125 [DT_BLKDT_BLK] = EXT2_FT_BLKDEV4,
126 [DT_FIFODT_FIFO] = EXT2_FT_FIFO5,
127 [DT_SOCKDT_SOCK] = EXT2_FT_SOCK6,
128 [DT_LNKDT_LNK] = EXT2_FT_SYMLINK7,
129};
130#endif
131
132/* Implement the diskfs_lookup from the diskfs library. See
133 <hurd/diskfs.h> for the interface specification. */
134error_t
135diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type,
136 struct node **npp, struct dirstat *ds, struct protid *cred)
137{
138 error_t err;
139 ino_t inum;
140 int namelen;
141 int spec_dotdot;
142 struct node *np = 0;
143 int retry_dotdot = 0;
144 vm_prot_t prot =
145 (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
146 memory_object_t memobj;
147 vm_address_t buf = 0;
148 vm_size_t buflen = 0;
149 int blockaddr;
150 int idx, lastidx;
151 int looped;
152
153 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
154 assert (npp)((npp) ? (void) (0) : __assert_fail ("npp", "../../ext2fs/dir.c"
, 154, __PRETTY_FUNCTION__))
;
155
156 if (npp)
6
Assuming 'npp' is null
7
Taking false branch
157 *npp = 0;
158
159 spec_dotdot = type & SPEC_DOTDOT0x10000000;
160 type &= ~SPEC_DOTDOT0x10000000;
161
162 namelen = strlen (name);
163
164 if (namelen > EXT2_NAME_LEN255)
8
Assuming 'namelen' is <= 255
9
Taking false branch
165 {
166 if (ds)
167 diskfs_null_dirstat (ds);
168 return ENAMETOOLONG((0x10 << 26) | ((63) & 0x3fff));
169 }
170
171 try_again:
172 if (ds)
10
Assuming 'ds' is null
11
Taking false branch
173 {
174 ds->type = LOOKUP;
175 ds->mapbuf = 0;
176 ds->mapextent = 0;
177 }
178 if (buf)
12
Taking false branch
179 {
180 munmap ((caddr_t) buf, buflen);
181 buf = 0;
182 }
183 if (ds && (type == CREATE || type == RENAME))
184 ds->stat = LOOKING;
185
186 /* Map in the directory contents. */
187 memobj = diskfs_get_filemap (dp, prot);
188
189 if (memobj == MACH_PORT_NULL((mach_port_t) 0))
13
Assuming 'memobj' is not equal to 0
14
Taking false branch
190 return errno(*__errno_location ());
191
192 buf = 0;
193 /* We allow extra space in case we have to do an EXTEND. */
194 buflen = round_page (dp->dn_stat.st_size + DIRBLKSIZ)((((vm_offset_t) (dp->dn_stat.st_size + block_size) + __vm_page_size
- 1) / __vm_page_size) * __vm_page_size)
;
195 err = vm_map (mach_task_self ()((__mach_task_self_ + 0)),
196 &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
197 mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), memobj);
198
199 inum = 0;
200
201 diskfs_set_node_atime (dp);
202
203 /* Start the lookup at DP->dn->dir_idx. */
204 idx = dp->dn->dir_idx;
205 if (idx * DIRBLKSIZblock_size > dp->dn_stat.st_size)
15
Taking false branch
206 idx = 0; /* just in case */
207 blockaddr = buf + idx * DIRBLKSIZblock_size;
208 looped = (idx == 0);
16
Assuming 'idx' is not equal to 0
209 lastidx = idx;
210 if (lastidx == 0)
17
Taking false branch
211 lastidx = dp->dn_stat.st_size / DIRBLKSIZblock_size;
212
213 while (!looped || idx < lastidx)
214 {
215 err = dirscanblock (blockaddr, dp, idx, name, namelen, type, ds, &inum);
18
Calling 'dirscanblock'
216 if (!err)
217 {
218 dp->dn->dir_idx = idx;
219 break;
220 }
221 if (err != ENOENT((0x10 << 26) | ((2) & 0x3fff)))
222 {
223 munmap ((caddr_t) buf, buflen);
224 return err;
225 }
226
227 blockaddr += DIRBLKSIZblock_size;
228 idx++;
229 if (blockaddr - buf >= dp->dn_stat.st_size && !looped)
230 {
231 /* We've gotten to the end; start back at the beginning */
232 looped = 1;
233 blockaddr = buf;
234 idx = 0;
235 }
236 }
237
238 diskfs_set_node_atime (dp);
239 if (diskfs_synchronous)
240 diskfs_node_update (dp, 1);
241
242 /* If err is set here, it's ENOENT, and we don't want to
243 think about that as an error yet. */
244 err = 0;
245
246 if (inum && npp)
247 {
248 if (namelen != 2 || name[0] != '.' || name[1] != '.')
249 {
250 if (inum == dp->cache_id)
251 {
252 np = dp;
253 diskfs_nref (np);
254 }
255 else
256 {
257 err = diskfs_cached_lookup (inum, &np);
258 if (err)
259 goto out;
260 }
261 }
262
263 /* We are looking up .. */
264 /* Check to see if this is the root of the filesystem. */
265 else if (dp->cache_id == 2)
266 {
267 err = EAGAIN((0x10 << 26) | ((35) & 0x3fff));
268 goto out;
269 }
270
271 /* We can't just do diskfs_cached_lookup, because we would then deadlock.
272 So we do this. Ick. */
273 else if (retry_dotdot)
274 {
275 /* Check to see that we got the same answer as last time. */
276 if (inum != retry_dotdot)
277 {
278 /* Drop what we *thought* was .. (but isn't any more) and
279 try *again*. */
280 diskfs_nput (np);
281 pthread_mutex_unlock (&dp->lock);
282 err = diskfs_cached_lookup (inum, &np);
283 pthread_mutex_lock (&dp->lock);
284 if (err)
285 goto out;
286 retry_dotdot = inum;
287 goto try_again;
288 }
289 /* Otherwise, we got it fine and np is already set properly. */
290 }
291 else if (!spec_dotdot)
292 {
293 /* Lock them in the proper order, and then
294 repeat the directory scan to see if this is still
295 right. */
296 pthread_mutex_unlock (&dp->lock);
297 err = diskfs_cached_lookup (inum, &np);
298 pthread_mutex_lock (&dp->lock);
299 if (err)
300 goto out;
301 retry_dotdot = inum;
302 goto try_again;
303 }
304
305 /* Here below are the spec dotdot cases. */
306 else if (type == RENAME || type == REMOVE)
307 np = ifind (inum);
308
309 else if (type == LOOKUP)
310 {
311 diskfs_nput (dp);
312 err = diskfs_cached_lookup (inum, &np);
313 if (err)
314 goto out;
315 }
316 else
317 assert (0)((0) ? (void) (0) : __assert_fail ("0", "../../ext2fs/dir.c",
317, __PRETTY_FUNCTION__))
;
318 }
319
320 if ((type == CREATE || type == RENAME) && !inum && ds && ds->stat == LOOKING)
321 {
322 /* We didn't find any room, so mark ds to extend the dir */
323 ds->type = CREATE;
324 ds->stat = EXTEND;
325 ds->idx = dp->dn_stat.st_size / DIRBLKSIZblock_size;
326 }
327
328 /* Return to the user; if we can't, release the reference
329 (and lock) we acquired above. */
330 out:
331 /* Deallocate or save the mapping. */
332 if ((err && err != ENOENT((0x10 << 26) | ((2) & 0x3fff)))
333 || !ds
334 || ds->type == LOOKUP)
335 {
336 munmap ((caddr_t) buf, buflen);
337 if (ds)
338 ds->type = LOOKUP; /* set to be ignored by drop_dirstat */
339 }
340 else
341 {
342 ds->mapbuf = buf;
343 ds->mapextent = buflen;
344 }
345
346 if (np)
347 {
348 assert (npp)((npp) ? (void) (0) : __assert_fail ("npp", "../../ext2fs/dir.c"
, 348, __PRETTY_FUNCTION__))
;
349 if (err)
350 {
351 if (!spec_dotdot)
352 {
353 /* Normal case */
354 if (np == dp)
355 diskfs_nrele (np);
356 else
357 diskfs_nput (np);
358 }
359 else if (type == RENAME || type == REMOVE)
360 /* We just did ifind to get np; that allocates
361 no new references, so we don't have anything to do */
362 ;
363 else if (type == LOOKUP)
364 /* We did diskfs_cached_lookup */
365 diskfs_nput (np);
366 }
367 else
368 *npp = np;
369 }
370
371 return err ? : inum ? 0 : ENOENT((0x10 << 26) | ((2) & 0x3fff));
372}
373
374/* Scan block at address BLKADDR (of node DP; block index IDX), for
375 name NAME of length NAMELEN. Args TYPE, DS are as for
376 diskfs_lookup. If found, set *INUM to the inode number, else
377 return ENOENT. */
378static error_t
379dirscanblock (vm_address_t blockaddr, struct node *dp, int idx,
380 const char *name, int namelen, enum lookup_type type,
381 struct dirstat *ds, ino_t *inum)
382{
383 int nfree = 0;
384 int needed = 0;
385 vm_address_t currentoff, prevoff;
386 struct ext2_dir_entry_2 *entry = 0;
19
Variable 'entry' initialized to a null pointer value
387 int nentries = 0;
388 size_t nbytes = 0;
389 int looking = 0;
390 int countcopies = 0;
391 int consider_compress = 0;
392
393 if (ds && (ds->stat == LOOKING
394 || ds->stat == COMPRESS))
395 {
396 looking = 1;
397 countcopies = 1;
398 needed = EXT2_DIR_REC_LEN (namelen)(((namelen) + 8 + (4 - 1)) & ~(4 - 1));
399 }
400
401 for (currentoff = blockaddr, prevoff = 0;
20
Loop condition is false. Execution continues on line 467
402 currentoff < blockaddr + DIRBLKSIZblock_size;
403 prevoff = currentoff, currentoff += entry->rec_len)
404 {
405 entry = (struct ext2_dir_entry_2 *)currentoff;
406
407 if (!entry->rec_len
408 || entry->rec_len % EXT2_DIR_PAD4
409 || entry->name_len > EXT2_NAME_LEN255
410 || currentoff + entry->rec_len > blockaddr + DIRBLKSIZblock_size
411 || EXT2_DIR_REC_LEN (entry->name_len)(((entry->name_len) + 8 + (4 - 1)) & ~(4 - 1)) > entry->rec_len
412 || memchr (entry->name, '\0', entry->name_len))
413 {
414 ext2_warning ("bad directory entry: inode: %Ld offset: %zd",
415 dp->cache_id,
416 currentoff - blockaddr + idx * DIRBLKSIZblock_size);
417 return ENOENT((0x10 << 26) | ((2) & 0x3fff));
418 }
419
420 if (looking || countcopies)
421 {
422 int thisfree;
423
424 /* Count how much free space this entry has in it. */
425 if (entry->inode == 0)
426 thisfree = entry->rec_len;
427 else
428 thisfree = entry->rec_len - EXT2_DIR_REC_LEN (entry->name_len)(((entry->name_len) + 8 + (4 - 1)) & ~(4 - 1));
429
430 /* If this isn't at the front of the block, then it will
431 have to be copied if we do a compression; count the
432 number of bytes there too. */
433 if (countcopies && currentoff != blockaddr)
434 nbytes += EXT2_DIR_REC_LEN (entry->name_len)(((entry->name_len) + 8 + (4 - 1)) & ~(4 - 1));
435
436 if (ds->stat == COMPRESS && nbytes > ds->nbytes)
437 /* The previously found compress is better than
438 this one, so don't bother counting any more. */
439 countcopies = 0;
440
441 if (thisfree >= needed)
442 {
443 ds->type = CREATE;
444 ds->stat = entry->inode == 0 ? TAKE : SHRINK;
445 ds->entry = entry;
446 ds->idx = idx;
447 looking = countcopies = 0;
448 }
449 else
450 {
451 nfree += thisfree;
452 if (nfree >= needed)
453 consider_compress = 1;
454 }
455 }
456
457 if (entry->inode)
458 nentries++;
459
460 if (entry->name_len == namelen
461 && entry->name[0] == name[0]
462 && entry->inode
463 && !bcmp (entry->name, name, namelen))
464 break;
465 }
466
467 if (consider_compress
468 && (ds->stat == LOOKING
469 || (ds->stat == COMPRESS && ds->nbytes > nbytes)))
470 {
471 ds->type = CREATE;
472 ds->stat = COMPRESS;
473 ds->entry = (struct ext2_dir_entry_2 *) blockaddr;
474 ds->idx = idx;
475 ds->nbytes = nbytes;
476 }
477
478 if (currentoff >= blockaddr + DIRBLKSIZblock_size)
21
Taking false branch
479 {
480 int i;
481 /* The name is not in this block. */
482
483 /* Because we scanned the entire block, we should write
484 down how many entries there were. */
485 if (!dp->dn->dirents)
486 {
487 dp->dn->dirents = malloc ((dp->dn_stat.st_size / DIRBLKSIZblock_size)
488 * sizeof (int));
489 for (i = 0; i < dp->dn_stat.st_size/DIRBLKSIZblock_size; i++)
490 dp->dn->dirents[i] = -1;
491 }
492 /* Make sure the count is correct if there is one now. */
493 assert (dp->dn->dirents[idx] == -1((dp->dn->dirents[idx] == -1 || dp->dn->dirents[idx
] == nentries) ? (void) (0) : __assert_fail ("dp->dn->dirents[idx] == -1 || dp->dn->dirents[idx] == nentries"
, "../../ext2fs/dir.c", 494, __PRETTY_FUNCTION__))
494 || dp->dn->dirents[idx] == nentries)((dp->dn->dirents[idx] == -1 || dp->dn->dirents[idx
] == nentries) ? (void) (0) : __assert_fail ("dp->dn->dirents[idx] == -1 || dp->dn->dirents[idx] == nentries"
, "../../ext2fs/dir.c", 494, __PRETTY_FUNCTION__))
;
495 dp->dn->dirents[idx] = nentries;
496
497 return ENOENT((0x10 << 26) | ((2) & 0x3fff));
498 }
499
500 /* We have found the required name. */
501
502 if (ds && type == CREATE)
503 ds->type = LOOKUP; /* it's invalid now */
504 else if (ds && (type == REMOVE || type == RENAME))
505 {
506 ds->type = type;
507 ds->stat = HERE_TIS;
508 ds->entry = entry;
509 ds->idx = idx;
510 ds->preventry = (struct ext2_dir_entry_2 *) prevoff;
511 }
512
513 *inum = entry->inode;
22
Access to field 'inode' results in a dereference of a null pointer (loaded from variable 'entry')
514 return 0;
515}
516
517/* Following a lookup call for CREATE, this adds a node to a directory.
518 DP is the directory to be modified; NAME is the name to be entered;
519 NP is the node being linked in; DS is the cached information returned
520 by lookup; CRED describes the user making the call. This call may
521 only be made if the directory has been held locked continuously since
522 the preceding lookup call, and only if that call returned ENOENT. */
523error_t
524diskfs_direnter_hard (struct node *dp, const char *name, struct node *np,
525 struct dirstat *ds, struct protid *cred)
526{
527 struct ext2_dir_entry_2 *new;
528 int namelen = strlen (name);
529 int needed = EXT2_DIR_REC_LEN (namelen)(((namelen) + 8 + (4 - 1)) & ~(4 - 1));
530 int oldneeded;
531 vm_address_t fromoff, tooff;
532 int totfreed;
533 error_t err;
534 size_t oldsize = 0;
535
536 assert (ds->type == CREATE)((ds->type == CREATE) ? (void) (0) : __assert_fail ("ds->type == CREATE"
, "../../ext2fs/dir.c", 536, __PRETTY_FUNCTION__))
;
537
538 assert (!diskfs_readonly)((!diskfs_readonly) ? (void) (0) : __assert_fail ("!diskfs_readonly"
, "../../ext2fs/dir.c", 538, __PRETTY_FUNCTION__))
;
539
540 dp->dn_set_mtime = 1;
541
542 /* Select a location for the new directory entry. Each branch of this
543 switch is responsible for setting NEW to point to the on-disk
544 directory entry being written, and setting NEW->rec_len appropriately. */
545
546 switch (ds->stat)
547 {
548 case TAKE:
549 /* We are supposed to consume this slot. */
550 assert (ds->entry->inode == 0 && ds->entry->rec_len >= needed)((ds->entry->inode == 0 && ds->entry->rec_len
>= needed) ? (void) (0) : __assert_fail ("ds->entry->inode == 0 && ds->entry->rec_len >= needed"
, "../../ext2fs/dir.c", 550, __PRETTY_FUNCTION__))
;
551
552 new = ds->entry;
553 break;
554
555 case SHRINK:
556 /* We are supposed to take the extra space at the end
557 of this slot. */
558 oldneeded = EXT2_DIR_REC_LEN (ds->entry->name_len)(((ds->entry->name_len) + 8 + (4 - 1)) & ~(4 - 1));
559 assert (ds->entry->rec_len - oldneeded >= needed)((ds->entry->rec_len - oldneeded >= needed) ? (void)
(0) : __assert_fail ("ds->entry->rec_len - oldneeded >= needed"
, "../../ext2fs/dir.c", 559, __PRETTY_FUNCTION__))
;
560
561 new = (struct ext2_dir_entry_2 *) ((vm_address_t) ds->entry + oldneeded);
562
563 new->rec_len = ds->entry->rec_len - oldneeded;
564 ds->entry->rec_len = oldneeded;
565 break;
566
567 case COMPRESS:
568 /* We are supposed to move all the entries to the
569 front of the block, giving each the minimum
570 necessary room. This should free up enough space
571 for the new entry. */
572 fromoff = tooff = (vm_address_t) ds->entry;
573
574 while (fromoff < (vm_address_t) ds->entry + DIRBLKSIZblock_size)
575 {
576 struct ext2_dir_entry_2 *from = (struct ext2_dir_entry_2 *)fromoff;
577 struct ext2_dir_entry_2 *to = (struct ext2_dir_entry_2 *) tooff;
578 int fromreclen = from->rec_len;
579
580 if (from->inode != 0)
581 {
582 assert (fromoff >= tooff)((fromoff >= tooff) ? (void) (0) : __assert_fail ("fromoff >= tooff"
, "../../ext2fs/dir.c", 582, __PRETTY_FUNCTION__))
;
583
584 memmove (to, from, fromreclen);
585 to->rec_len = EXT2_DIR_REC_LEN (to->name_len)(((to->name_len) + 8 + (4 - 1)) & ~(4 - 1));
586
587 tooff += to->rec_len;
588 }
589 fromoff += fromreclen;
590 }
591
592 totfreed = (vm_address_t) ds->entry + DIRBLKSIZblock_size - tooff;
593 assert (totfreed >= needed)((totfreed >= needed) ? (void) (0) : __assert_fail ("totfreed >= needed"
, "../../ext2fs/dir.c", 593, __PRETTY_FUNCTION__))
;
594
595 new = (struct ext2_dir_entry_2 *) tooff;
596 new->rec_len = totfreed;
597 break;
598
599 case EXTEND:
600 /* Extend the file. */
601 assert (needed <= DIRBLKSIZ)((needed <= block_size) ? (void) (0) : __assert_fail ("needed <= block_size"
, "../../ext2fs/dir.c", 601, __PRETTY_FUNCTION__))
;
602
603 oldsize = dp->dn_stat.st_size;
604 if ((off_t)(oldsize + DIRBLKSIZblock_size) != (dp->dn_stat.st_size + DIRBLKSIZblock_size))
605 {
606 /* We can't possibly map the whole directory in. */
607 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
608 return EOVERFLOW((0x10 << 26) | ((115) & 0x3fff));
609 }
610 while (oldsize + DIRBLKSIZblock_size > dp->allocsize)
611 {
612 err = diskfs_grow (dp, oldsize + DIRBLKSIZblock_size, cred);
613 if (err)
614 {
615 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
616 return err;
617 }
618 }
619
620 new = (struct ext2_dir_entry_2 *) (ds->mapbuf + oldsize);
621
622 dp->dn_stat.st_size = oldsize + DIRBLKSIZblock_size;
623 dp->dn_set_ctime = 1;
624
625 new->rec_len = DIRBLKSIZblock_size;
626 break;
627
628 default:
629 new = 0;
630 assert (! "impossible: bogus status field in dirstat")((! "impossible: bogus status field in dirstat") ? (void) (0)
: __assert_fail ("! \"impossible: bogus status field in dirstat\""
, "../../ext2fs/dir.c", 630, __PRETTY_FUNCTION__))
;
631 }
632
633 /* NEW points to the directory entry being written, and its
634 rec_len field is already filled in. Now fill in the rest. */
635
636 new->inode = np->cache_id;
637#if 0
638 /* XXX We cannot enable this code because file types can change
639 (and conceivably quite often) with translator settings.
640 There is no way for the translator that determines the type of
641 the virtual node to cause all the directory entries linked to
642 its underlying inode to reflect the proper type. */
643 new->file_type = (EXT2_HAS_INCOMPAT_FEATURE (sblock,( (sblock)->s_feature_incompat & (0x0002) )
644 EXT2_FEATURE_INCOMPAT_FILETYPE)( (sblock)->s_feature_incompat & (0x0002) )
645 ? file_type_ext2[IFTODT (np->dn_stat.st_mode & S_IFMT)(((np->dn_stat.st_mode & 0170000) & 0170000) >>
12)
]
646 : 0);
647#else
648 new->file_type = 0;
649#endif
650 new->name_len = namelen;
651 memcpy (new->name, name, namelen);
652
653 /* Mark the directory inode has having been written. */
654 dp->dn->info.i_flags &= ~EXT2_BTREE_FL0x00001000;
655 dp->dn_set_mtime = 1;
656
657 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
658
659 if (ds->stat != EXTEND)
660 {
661 /* If we are keeping count of this block, then keep the count up
662 to date. */
663 if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1)
664 dp->dn->dirents[ds->idx]++;
665 }
666 else
667 {
668 int i;
669 /* It's cheap, so start a count here even if we aren't counting
670 anything at all. */
671 if (dp->dn->dirents)
672 {
673 dp->dn->dirents = realloc (dp->dn->dirents,
674 (dp->dn_stat.st_size / DIRBLKSIZblock_size
675 * sizeof (int)));
676 for (i = oldsize / DIRBLKSIZblock_size;
677 i < dp->dn_stat.st_size / DIRBLKSIZblock_size;
678 i++)
679 dp->dn->dirents[i] = -1;
680
681 dp->dn->dirents[ds->idx] = 1;
682 }
683 else
684 {
685 dp->dn->dirents = malloc (dp->dn_stat.st_size / DIRBLKSIZblock_size
686 * sizeof (int));
687 for (i = 0; i < dp->dn_stat.st_size / DIRBLKSIZblock_size; i++)
688 dp->dn->dirents[i] = -1;
689 dp->dn->dirents[ds->idx] = 1;
690 }
691 }
692
693 diskfs_file_update (dp, diskfs_synchronous);
694
695 return 0;
696}
697
698/* Following a lookup call for REMOVE, this removes the link from the
699 directory. DP is the directory being changed and DS is the cached
700 information returned from lookup. This call is only valid if the
701 directory has been locked continuously since the call to lookup, and
702 only if that call succeeded. */
703error_t
704diskfs_dirremove_hard (struct node *dp, struct dirstat *ds)
705{
706 assert (ds->type == REMOVE)((ds->type == REMOVE) ? (void) (0) : __assert_fail ("ds->type == REMOVE"
, "../../ext2fs/dir.c", 706, __PRETTY_FUNCTION__))
;
707 assert (ds->stat == HERE_TIS)((ds->stat == HERE_TIS) ? (void) (0) : __assert_fail ("ds->stat == HERE_TIS"
, "../../ext2fs/dir.c", 707, __PRETTY_FUNCTION__))
;
708
709 assert (!diskfs_readonly)((!diskfs_readonly) ? (void) (0) : __assert_fail ("!diskfs_readonly"
, "../../ext2fs/dir.c", 709, __PRETTY_FUNCTION__))
;
710
711 if (ds->preventry == 0)
712 ds->entry->inode = 0;
713 else
714 {
715 assert ((vm_address_t) ds->entry - (vm_address_t) ds->preventry(((vm_address_t) ds->entry - (vm_address_t) ds->preventry
== ds->preventry->rec_len) ? (void) (0) : __assert_fail
("(vm_address_t) ds->entry - (vm_address_t) ds->preventry == ds->preventry->rec_len"
, "../../ext2fs/dir.c", 716, __PRETTY_FUNCTION__))
716 == ds->preventry->rec_len)(((vm_address_t) ds->entry - (vm_address_t) ds->preventry
== ds->preventry->rec_len) ? (void) (0) : __assert_fail
("(vm_address_t) ds->entry - (vm_address_t) ds->preventry == ds->preventry->rec_len"
, "../../ext2fs/dir.c", 716, __PRETTY_FUNCTION__))
;
717 ds->preventry->rec_len += ds->entry->rec_len;
718 }
719
720 dp->dn_set_mtime = 1;
721 dp->dn->info.i_flags &= ~EXT2_BTREE_FL0x00001000;
722
723 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
724
725 /* If we are keeping count of this block, then keep the count up
726 to date. */
727 if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1)
728 dp->dn->dirents[ds->idx]--;
729
730 diskfs_file_update (dp, diskfs_synchronous);
731
732 return 0;
733}
734
735
736/* Following a lookup call for RENAME, this changes the inode number
737 on a directory entry. DP is the directory being changed; NP is
738 the new node being linked in; DP is the cached information returned
739 by lookup. This call is only valid if the directory has been locked
740 continuously since the call to lookup, and only if that call
741 succeeded. */
742error_t
743diskfs_dirrewrite_hard (struct node *dp, struct node *np, struct dirstat *ds)
744{
745 assert (ds->type == RENAME)((ds->type == RENAME) ? (void) (0) : __assert_fail ("ds->type == RENAME"
, "../../ext2fs/dir.c", 745, __PRETTY_FUNCTION__))
;
746 assert (ds->stat == HERE_TIS)((ds->stat == HERE_TIS) ? (void) (0) : __assert_fail ("ds->stat == HERE_TIS"
, "../../ext2fs/dir.c", 746, __PRETTY_FUNCTION__))
;
747
748 assert (!diskfs_readonly)((!diskfs_readonly) ? (void) (0) : __assert_fail ("!diskfs_readonly"
, "../../ext2fs/dir.c", 748, __PRETTY_FUNCTION__))
;
749
750 ds->entry->inode = np->cache_id;
751 dp->dn_set_mtime = 1;
752 dp->dn->info.i_flags &= ~EXT2_BTREE_FL0x00001000;
753
754 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
755
756 diskfs_file_update (dp, diskfs_synchronous);
757
758 return 0;
759}
760
761/* Tell if DP is an empty directory (has only "." and ".." entries).
762 This routine must be called from inside a catch_exception (). */
763int
764diskfs_dirempty (struct node *dp, struct protid *cred)
765{
766 error_t err;
767 vm_address_t buf = 0, curoff;
768 struct ext2_dir_entry_2 *entry;
769 int hit = 0; /* Found something in the directory. */
770 memory_object_t memobj = diskfs_get_filemap (dp, VM_PROT_READ((vm_prot_t) 0x01));
771
772 if (memobj == MACH_PORT_NULL((mach_port_t) 0))
773 /* XXX should reflect error properly. */
774 return 0;
775
776 err = vm_map (mach_task_self ()((__mach_task_self_ + 0)), &buf, dp->dn_stat.st_size, 0,
777 1, memobj, 0, 0, VM_PROT_READ((vm_prot_t) 0x01), VM_PROT_READ((vm_prot_t) 0x01), 0);
778 mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), memobj);
779 assert (!err)((!err) ? (void) (0) : __assert_fail ("!err", "../../ext2fs/dir.c"
, 779, __PRETTY_FUNCTION__))
;
780
781 diskfs_set_node_atime (dp);
782
783 for (curoff = buf;
784 !hit && curoff < buf + dp->dn_stat.st_size;
785 curoff += entry->rec_len)
786 {
787 entry = (struct ext2_dir_entry_2 *) curoff;
788
789 if (entry->inode != 0
790 && (entry->name_len > 2
791 || entry->name[0] != '.'
792 || (entry->name[1] != '.'
793 && entry->name[1] != '\0')))
794 hit = 1;
795 }
796
797 diskfs_set_node_atime (dp);
798 if (diskfs_synchronous)
799 diskfs_node_update (dp, 1);
800
801 munmap ((caddr_t) buf, dp->dn_stat.st_size);
802
803 return !hit;
804}
805
806/* Make DS an invalid dirstat. */
807error_t
808diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
809{
810 if (ds->type != LOOKUP)
811 {
812 assert (ds->mapbuf)((ds->mapbuf) ? (void) (0) : __assert_fail ("ds->mapbuf"
, "../../ext2fs/dir.c", 812, __PRETTY_FUNCTION__))
;
813 munmap ((caddr_t) ds->mapbuf, ds->mapextent);
814 ds->type = LOOKUP;
815 }
816 return 0;
817}
818
819
820/* Count the entries in directory block NB for directory DP and
821 write the answer down in its dirents array. As a side affect
822 fill BUF with the block. */
823static error_t
824count_dirents (struct node *dp, int nb, char *buf)
825{
826 size_t amt;
827 char *offinblk;
828 struct ext2_dir_entry_2 *entry;
829 int count = 0;
830 error_t err;
831
832 assert (dp->dn->dirents)((dp->dn->dirents) ? (void) (0) : __assert_fail ("dp->dn->dirents"
, "../../ext2fs/dir.c", 832, __PRETTY_FUNCTION__))
;
833 assert ((nb + 1) * DIRBLKSIZ <= dp->dn_stat.st_size)(((nb + 1) * block_size <= dp->dn_stat.st_size) ? (void
) (0) : __assert_fail ("(nb + 1) * block_size <= dp->dn_stat.st_size"
, "../../ext2fs/dir.c", 833, __PRETTY_FUNCTION__))
;
834
835 err = diskfs_node_rdwr (dp, buf, nb * DIRBLKSIZblock_size, DIRBLKSIZblock_size, 0, 0, &amt);
836 if (err)
837 return err;
838 assert (amt == DIRBLKSIZ)((amt == block_size) ? (void) (0) : __assert_fail ("amt == block_size"
, "../../ext2fs/dir.c", 838, __PRETTY_FUNCTION__))
;
839
840 for (offinblk = buf;
841 offinblk < buf + DIRBLKSIZblock_size;
842 offinblk += entry->rec_len)
843 {
844 entry = (struct ext2_dir_entry_2 *) offinblk;
845 if (entry->inode)
846 count++;
847 }
848
849 assert (dp->dn->dirents[nb] == -1 || dp->dn->dirents[nb] == count)((dp->dn->dirents[nb] == -1 || dp->dn->dirents[nb
] == count) ? (void) (0) : __assert_fail ("dp->dn->dirents[nb] == -1 || dp->dn->dirents[nb] == count"
, "../../ext2fs/dir.c", 849, __PRETTY_FUNCTION__))
;
850 dp->dn->dirents[nb] = count;
851 return 0;
852}
853
854/* Returned directory entries are aligned to blocks this many bytes long.
855 Must be a power of two. */
856#define DIRENT_ALIGN4 4
857
858/* Implement the diskfs_get_directs callback as described in
859 <hurd/diskfs.h>. */
860error_t
861diskfs_get_directs (struct node *dp,
862 int entry,
863 int nentries,
864 char **data,
865 size_t *datacnt,
866 vm_size_t bufsiz,
867 int *amt)
868{
869 int blkno;
870 int nblks;
871 int curentry;
872 char buf[DIRBLKSIZblock_size];
873 char *bufp;
874 int bufvalid;
875 error_t err;
876 int i;
877 char *datap;
878 struct ext2_dir_entry_2 *entryp;
879 int allocsize;
880 size_t checklen;
881 struct dirent *userp;
882
883 nblks = dp->dn_stat.st_size/DIRBLKSIZblock_size;
884
885 if (!dp->dn->dirents)
886 {
887 dp->dn->dirents = malloc (nblks * sizeof (int));
888 for (i = 0; i < nblks; i++)
889 dp->dn->dirents[i] = -1;
890 }
891
892 /* Scan through the entries to find ENTRY. If we encounter
893 a -1 in the process then stop to fill it. When we run
894 off the end, ENTRY is too big. */
895 curentry = 0;
896 bufvalid = 0;
897 for (blkno = 0; blkno < nblks; blkno++)
898 {
899 if (dp->dn->dirents[blkno] == -1)
900 {
901 err = count_dirents (dp, blkno, buf);
902 if (err)
903 return err;
904 bufvalid = 1;
905 }
906
907 if (curentry + dp->dn->dirents[blkno] > entry)
908 /* ENTRY starts in this block. */
909 break;
910
911 curentry += dp->dn->dirents[blkno];
912
913 bufvalid = 0;
914 }
915
916 if (blkno == nblks)
917 {
918 /* We reached the end of the directory without seeing ENTRY.
919 This is treated as an EOF condition, meaning we return
920 success with empty results. */
921 *datacnt = 0;
922 *amt = 0;
923 return 0;
924 }
925
926 /* Allocate enough space to hold the maximum we might return */
927 if (!bufsiz || bufsiz > dp->dn_stat.st_size)
928 /* Allocate enough to return the entire directory. Since ext2's
929 directory format is different than the format used to return the
930 entries, we allocate enough to hold the on disk directory plus
931 whatever extra would be necessary in the worst-case. */
932 {
933 /* The minimum size of an ext2fs directory entry. */
934 size_t min_entry_size = EXT2_DIR_REC_LEN (0)(((0) + 8 + (4 - 1)) & ~(4 - 1));
935 /* The minimum size of a returned dirent entry. The +1 is for '\0'. */
936 size_t min_dirent_size = offsetof (struct dirent, d_name)__builtin_offsetof(struct dirent, d_name) + 1;
937 /* The maximum possible number of ext2fs dir entries in this dir. */
938 size_t max_entries = dp->dn_stat.st_size / min_entry_size;
939 /* The maximum difference in size per directory entry. */
940 size_t entry_extra =
941 DIRENT_ALIGN4
942 + (min_dirent_size > min_entry_size
943 ? min_dirent_size - min_entry_size : 0);
944
945 allocsize = round_page (dp->dn_stat.st_size + max_entries * entry_extra)((((vm_offset_t) (dp->dn_stat.st_size + max_entries * entry_extra
) + __vm_page_size - 1) / __vm_page_size) * __vm_page_size)
;
946 }
947 else
948 allocsize = round_page (bufsiz)((((vm_offset_t) (bufsiz) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
;
949
950 if (allocsize > *datacnt)
951 *data = mmap (0, allocsize, PROT_READ0x04|PROT_WRITE0x02, MAP_ANON0x0002, 0, 0);
952
953 /* Set bufp appropriately */
954 bufp = buf;
955 if (curentry != entry)
956 {
957 /* Look through the block to find out where to start,
958 setting bufp appropriately. */
959 if (!bufvalid)
960 {
961 err = diskfs_node_rdwr (dp, buf, blkno * DIRBLKSIZblock_size, DIRBLKSIZblock_size,
962 0, 0, &checklen);
963 if (err)
964 return err;
965 assert (checklen == DIRBLKSIZ)((checklen == block_size) ? (void) (0) : __assert_fail ("checklen == block_size"
, "../../ext2fs/dir.c", 965, __PRETTY_FUNCTION__))
;
966 bufvalid = 1;
967 }
968 for (i = 0, bufp = buf;
969 i < entry - curentry && bufp - buf < DIRBLKSIZblock_size;
970 bufp += ((struct ext2_dir_entry_2 *)bufp)->rec_len, i++)
971 ;
972 /* Make sure we didn't run off the end. */
973 assert (bufp - buf < DIRBLKSIZ)((bufp - buf < block_size) ? (void) (0) : __assert_fail ("bufp - buf < block_size"
, "../../ext2fs/dir.c", 973, __PRETTY_FUNCTION__))
;
974 }
975
976 i = 0;
977 datap = *data;
978
979 /* Copy the entries, one at a time. */
980 while (((nentries == -1) || (i < nentries))
981 && (!bufsiz || (datap - *data < bufsiz) )
982 && blkno < nblks)
983 {
984 if (!bufvalid)
985 {
986 err = diskfs_node_rdwr (dp, buf, blkno * DIRBLKSIZblock_size, DIRBLKSIZblock_size,
987 0, 0, &checklen);
988 if (err)
989 return err;
990 assert (checklen == DIRBLKSIZ)((checklen == block_size) ? (void) (0) : __assert_fail ("checklen == block_size"
, "../../ext2fs/dir.c", 990, __PRETTY_FUNCTION__))
;
991 bufvalid = 1;
992 bufp = buf;
993 }
994
995 entryp = (struct ext2_dir_entry_2 *)bufp;
996
997 if (entryp->inode)
998 {
999 int rec_len;
1000 int name_len = entryp->name_len;
1001
1002 userp = (struct dirent *) datap;
1003
1004 /* Length is structure before the name + the name + '\0', all
1005 padded to a four-byte alignment. */
1006 rec_len =
1007 ((offsetof (struct dirent, d_name)__builtin_offsetof(struct dirent, d_name)
1008 + name_len + 1
1009 + (DIRENT_ALIGN4 - 1))
1010 & ~(DIRENT_ALIGN4 - 1));
1011
1012 /* See if this record would run over the end of the return buffer. */
1013 if (bufsiz == 0)
1014 /* It shouldn't ever, as we calculated the worst case size. */
1015 assert (datap + rec_len <= *data + allocsize)((datap + rec_len <= *data + allocsize) ? (void) (0) : __assert_fail
("datap + rec_len <= *data + allocsize", "../../ext2fs/dir.c"
, 1015, __PRETTY_FUNCTION__))
;
1016 else
1017 /* It's ok if it does, just leave off returning this entry. */
1018 if (datap + rec_len > *data + allocsize)
1019 break;
1020
1021 userp->d_filenod_ino = entryp->inode;
1022 userp->d_reclen = rec_len;
1023 userp->d_namlen = name_len;
1024
1025#if 0
1026 /* We don't bother to check the EXT2_FEATURE_INCOMPAT_FILETYPE
1027 flag in the superblock, because in old filesystems the
1028 file_type field is the high byte of the length field and is
1029 always zero because names cannot be that long. */
1030 if (entryp->file_type < EXT2_FT_MAX8)
1031 userp->d_type = ext2_file_type[entryp->file_type];
1032 else
1033 {
1034 ext2_warning ("bad type %d in directory entry: "
1035 "inode: %d offset: %d",
1036 entryp->file_type,
1037 dp->cache_id,
1038 blkno * DIRBLKSIZblock_size + bufp - buf);
1039 userp->d_type = DT_UNKNOWNDT_UNKNOWN;
1040 }
1041#else
1042 /* XXX
1043 For complex reasons it might not be correct to return
1044 the filesystem's d_type value to the user. */
1045 userp->d_type = DT_UNKNOWNDT_UNKNOWN;
1046#endif
1047 memcpy (userp->d_name, entryp->name, name_len);
1048 userp->d_name[name_len] = '\0';
1049
1050 datap += rec_len;
1051 i++;
1052 }
1053
1054 if (entryp->rec_len == 0)
1055 {
1056 ext2_warning ("zero length directory entry: inode: %Ld offset: %zd",
1057 dp->cache_id,
1058 blkno * DIRBLKSIZblock_size + bufp - buf);
1059 return EIO((0x10 << 26) | ((5) & 0x3fff));
1060 }
1061
1062 bufp += entryp->rec_len;
1063 if (bufp - buf == DIRBLKSIZblock_size)
1064 {
1065 blkno++;
1066 bufvalid = 0;
1067 }
1068 else if (bufp - buf > DIRBLKSIZblock_size)
1069 {
1070 ext2_warning ("directory entry too long: inode: %Ld offset: %zd",
1071 dp->cache_id,
1072 blkno * DIRBLKSIZblock_size + bufp - buf - entryp->rec_len);
1073 return EIO((0x10 << 26) | ((5) & 0x3fff));
1074 }
1075 }
1076
1077 /* We've copied all we can. If we allocated our own array
1078 but didn't fill all of it, then free whatever memory we didn't use. */
1079 if (allocsize > *datacnt)
1080 {
1081 if (round_page (datap - *data)((((vm_offset_t) (datap - *data) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
< allocsize)
1082 munmap ((caddr_t) (*data + round_page (datap - *data)((((vm_offset_t) (datap - *data) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
),
1083 allocsize - round_page (datap - *data)((((vm_offset_t) (datap - *data) + __vm_page_size - 1) / __vm_page_size
) * __vm_page_size)
);
1084 }
1085
1086 /* Set variables for return */
1087 *datacnt = datap - *data;
1088 *amt = i;
1089 return 0;
1090}