Bug Summary

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