2 Copyright 1999-2001, Be Incorporated. All Rights Reserved.
3 This file may be used under the terms of the Be Sample Code License.
13 #include <KernelExport.h>
14 #include <NodeMonitor.h>
28 #define DPRINTF(a,b) if (debug_file > (a)) dprintf b
30 #define MAX_FILE_SIZE 0xffffffffLL
33 typedef struct filecookie
{
34 uint32 mode
; // open mode
39 make_mode(nspace
*volume
, vnode
*node
)
41 mode_t result
= S_IFREG
| S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IWGRP
| S_IWOTH
;
42 if (node
->mode
& FAT_SUBDIR
) {
44 result
|= S_IFDIR
| S_IXUSR
| S_IXGRP
| S_IXOTH
;
46 if ((volume
->flags
& B_FS_IS_READONLY
) || (node
->mode
& FAT_READ_ONLY
))
47 result
&= ~(S_IWUSR
| S_IWGRP
| S_IWOTH
);
54 dosfs_get_vnode_name(fs_volume
*_ns
, fs_vnode
*_node
, char *buffer
,
57 vnode
*node
= (vnode
*)_node
->private_node
;
58 strlcpy(buffer
, node
->filename
, bufferSize
);
63 status_t
write_vnode_entry(nspace
*vol
, vnode
*node
)
70 // TODO : is it needed ? vfs job ?
71 // don't update entries of deleted files
72 //if (is_vnode_removed(vol->id, node->vnid) > 0) return 0;
74 // XXX: should check if directory position is still valid even
75 // though we do the is_vnode_removed check above
77 if ((node
->cluster
!= 0) && !IS_DATA_CLUSTER(node
->cluster
)) {
78 dprintf("write_vnode_entry called on invalid cluster (%" B_PRIu32
")\n",
83 buffer
= diri_init(vol
, VNODE_PARENT_DIR_CLUSTER(node
), node
->eindex
, &diri
);
87 diri_make_writable(&diri
);
88 buffer
[0x0b] = node
->mode
; // file attributes
90 memset(buffer
+0xc, 0, 0x16-0xc);
91 i
= time_t2dos(node
->st_crtim
);
92 buffer
[0x0e] = i
& 0xff;
93 buffer
[0x0f] = (i
>> 8) & 0xff;
94 buffer
[0x10] = (i
>> 16) & 0xff;
95 buffer
[0x11] = (i
>> 24) & 0xff;
96 i
= time_t2dos(node
->st_time
);
97 buffer
[0x16] = i
& 0xff;
98 buffer
[0x17] = (i
>> 8) & 0xff;
99 buffer
[0x18] = (i
>> 16) & 0xff;
100 buffer
[0x19] = (i
>> 24) & 0xff;
101 buffer
[0x1a] = node
->cluster
& 0xff; // starting cluster
102 buffer
[0x1b] = (node
->cluster
>> 8) & 0xff;
103 if (vol
->fat_bits
== 32) {
104 buffer
[0x14] = (node
->cluster
>> 16) & 0xff;
105 buffer
[0x15] = (node
->cluster
>> 24) & 0xff;
107 if (node
->mode
& FAT_SUBDIR
) {
108 buffer
[0x1c] = buffer
[0x1d] = buffer
[0x1e] = buffer
[0x1f] = 0;
110 buffer
[0x1c] = node
->st_size
& 0xff; // file size
111 buffer
[0x1d] = (node
->st_size
>> 8) & 0xff;
112 buffer
[0x1e] = (node
->st_size
>> 16) & 0xff;
113 buffer
[0x1f] = (node
->st_size
>> 24) & 0xff;
118 // TODO: figure out which stats have actually changed
119 notify_stat_changed(vol
->id
, -1, node
->vnid
, B_STAT_MODE
| B_STAT_UID
120 | B_STAT_GID
| B_STAT_SIZE
| B_STAT_ACCESS_TIME
121 | B_STAT_MODIFICATION_TIME
| B_STAT_CREATION_TIME
122 | B_STAT_CHANGE_TIME
);
128 // called when fs is done with vnode
129 // after close, etc. free vnode resources here
131 dosfs_release_vnode(fs_volume
*_vol
, fs_vnode
*_node
, bool reenter
)
133 nspace
*vol
= (nspace
*)_vol
->private_volume
;
134 vnode
*node
= (vnode
*)_node
->private_node
;
138 DPRINTF(0, ("dosfs_release_vnode (ino_t %" B_PRIdINO
")\n", node
->vnid
));
140 if ((vol
->fs_flags
& FS_FLAGS_OP_SYNC
) && node
->dirty
) {
148 if (node
->filename
) free(node
->filename
);
151 if (node
->vnid
!= vol
->root_vnode
.vnid
) {
152 file_cache_delete(node
->cache
);
153 file_map_delete(node
->file_map
);
163 dosfs_rstat(fs_volume
*_vol
, fs_vnode
*_node
, struct stat
*st
)
165 nspace
*vol
= (nspace
*)_vol
->private_volume
;
166 vnode
*node
= (vnode
*)_node
->private_node
;
170 DPRINTF(1, ("dosfs_rstat (vnode id %" B_PRIdINO
")\n", node
->vnid
));
172 st
->st_dev
= vol
->id
;
173 st
->st_ino
= node
->vnid
;
174 st
->st_mode
= make_mode(vol
, node
);
179 st
->st_size
= node
->st_size
;
180 st
->st_blocks
= (node
->st_size
+ 511) / 512;
181 st
->st_blksize
= 0x10000; /* this value was chosen arbitrarily */
182 st
->st_atim
.tv_sec
= st
->st_mtim
.tv_sec
= st
->st_ctim
.tv_sec
184 st
->st_crtim
.tv_sec
= node
->st_crtim
;
185 st
->st_atim
.tv_nsec
= st
->st_mtim
.tv_nsec
= st
->st_ctim
.tv_nsec
186 = st
->st_crtim
.tv_nsec
= 0;
195 dosfs_wstat(fs_volume
*_vol
, fs_vnode
*_node
, const struct stat
*st
,
199 nspace
*vol
= (nspace
*)_vol
->private_volume
;
200 vnode
*node
= (vnode
*)_node
->private_node
;
205 DPRINTF(0, ("dosfs_wstat (vnode id %" B_PRIdINO
")\n", node
->vnid
));
207 if (vol
->flags
& B_FS_IS_READONLY
) {
208 dprintf("can't wstat on read-only volume\n");
213 if (node
->disk_image
== 2) {
214 dprintf("can't wstat disk image\n");
219 if ((mask
& B_STAT_MODE
) != 0) {
220 DPRINTF(0, ("setting file mode to %o\n", st
->st_mode
));
221 if (st
->st_mode
& S_IWUSR
)
222 node
->mode
&= ~FAT_READ_ONLY
;
224 node
->mode
|= FAT_READ_ONLY
;
228 if ((mask
& B_STAT_SIZE
) != 0) {
229 DPRINTF(0, ("setting file size to %" B_PRIdOFF
"\n", st
->st_size
));
230 if (node
->mode
& FAT_SUBDIR
) {
231 dprintf("dosfs_wstat: can't set file size of directory!\n");
233 } else if (st
->st_size
> MAX_FILE_SIZE
) {
234 dprintf("dosfs_wstat: desired file size exceeds fat limit\n");
237 uint32 clusters
= (st
->st_size
+ vol
->bytes_per_sector
238 * vol
->sectors_per_cluster
- 1) / vol
->bytes_per_sector
239 / vol
->sectors_per_cluster
;
240 DPRINTF(0, ("setting fat chain length to %" B_PRIu32
" clusters\n",
242 if ((err
= set_fat_chain_length(vol
, node
, clusters
, false))
244 node
->st_size
= st
->st_size
;
247 file_cache_set_size(node
->cache
, node
->st_size
);
248 file_map_set_size(node
->file_map
, node
->st_size
);
253 if ((mask
& B_STAT_MODIFICATION_TIME
) != 0) {
254 DPRINTF(0, ("setting modification time\n"));
255 if ((node
->mode
& FAT_SUBDIR
) == 0)
256 node
->mode
|= FAT_ARCHIVE
;
257 node
->st_time
= st
->st_mtime
;
261 if ((mask
& B_STAT_CREATION_TIME
) != 0) {
262 DPRINTF(0, ("setting creation time\n"));
263 // As a file's modification time is also set when it is created,
264 // the archive bit should be set automatically.
265 node
->st_crtim
= st
->st_crtime
;
270 write_vnode_entry(vol
, node
);
272 if (vol
->fs_flags
& FS_FLAGS_OP_SYNC
) {
273 // sync the filesystem
279 if (err
!= B_OK
) DPRINTF(0, ("dosfs_wstat (%s)\n", strerror(err
)));
288 dosfs_open(fs_volume
*_vol
, fs_vnode
*_node
, int omode
, void **_cookie
)
290 status_t result
= EINVAL
;
291 nspace
*vol
= (nspace
*)_vol
->private_volume
;
292 vnode
* node
= (vnode
*)_node
->private_node
;
299 DPRINTF(0, ("dosfs_open: vnode id %" B_PRIdINO
", omode %o\n", node
->vnid
,
302 if (omode
& O_CREAT
) {
303 dprintf("dosfs_open called with O_CREAT. call dosfs_create instead!\n");
308 if ((vol
->flags
& B_FS_IS_READONLY
) ||
309 (node
->mode
& FAT_READ_ONLY
) ||
310 (node
->disk_image
!= 0) ||
311 // allow opening directories for ioctl() calls
312 // and to let BVolume to work
313 (node
->mode
& FAT_SUBDIR
)) {
314 omode
= (omode
& ~O_RWMASK
) | O_RDONLY
;
317 if ((omode
& O_TRUNC
) && ((omode
& O_RWMASK
) == O_RDONLY
)) {
318 DPRINTF(0, ("can't open file for reading with O_TRUNC\n"));
323 if (omode
& O_TRUNC
) {
324 DPRINTF(0, ("dosfs_open called with O_TRUNC set\n"));
325 if ((result
= set_fat_chain_length(vol
, node
, 0, false)) != B_OK
) {
326 dprintf("dosfs_open: error truncating file\n");
334 if ((cookie
= calloc(sizeof(filecookie
), 1)) == NULL
) {
339 cookie
->mode
= omode
;
344 if (result
!= B_OK
) DPRINTF(0, ("dosfs_open (%s)\n", strerror(result
)));
352 dosfs_read(fs_volume
*_vol
, fs_vnode
*_node
, void *_cookie
, off_t pos
,
353 void *buf
, size_t *len
)
355 nspace
*vol
= (nspace
*)_vol
->private_volume
;
356 vnode
*node
= (vnode
*)_node
->private_node
;
357 filecookie
*cookie
= (filecookie
*)_cookie
;
362 if (node
->mode
& FAT_SUBDIR
) {
363 DPRINTF(0, ("dosfs_read called on subdirectory %" B_PRIdINO
"\n",
370 DPRINTF(0, ("dosfs_read called %" B_PRIuSIZE
" bytes at %" B_PRIdOFF
371 " (vnode id %" B_PRIdINO
")\n", *len
, pos
, node
->vnid
));
373 if (pos
< 0) pos
= 0;
375 if ((node
->st_size
== 0) || (*len
== 0) || (pos
>= node
->st_size
)) {
380 // truncate bytes to read to file size
381 if (pos
+ *len
>= node
->st_size
)
382 *len
= node
->st_size
- pos
;
384 result
= file_cache_read(node
->cache
, cookie
, pos
, buf
, len
);
387 if (result
!= B_OK
) {
388 DPRINTF(0, ("dosfs_read (%s)\n", strerror(result
)));
390 DPRINTF(0, ("dosfs_read: read %" B_PRIuSIZE
" bytes\n", *len
));
399 dosfs_write(fs_volume
*_vol
, fs_vnode
*_node
, void *_cookie
, off_t pos
,
400 const void *buf
, size_t *len
)
402 nspace
*vol
= (nspace
*)_vol
->private_volume
;
403 vnode
*node
= (vnode
*)_node
->private_node
;
404 filecookie
*cookie
= (filecookie
*)_cookie
;
409 if (node
->mode
& FAT_SUBDIR
) {
410 DPRINTF(0, ("dosfs_write called on subdirectory %" B_PRIdINO
"\n",
418 DPRINTF(0, ("dosfs_write called %" B_PRIuSIZE
" bytes at %" B_PRIdOFF
419 " from buffer at %p (vnode id %" B_PRIdINO
")\n", *len
, pos
, buf
,
422 if ((cookie
->mode
& O_RWMASK
) == O_RDONLY
) {
423 dprintf("dosfs_write: called on file opened as read-only\n");
429 if (pos
< 0) pos
= 0;
431 if (cookie
->mode
& O_APPEND
) {
435 if (pos
>= MAX_FILE_SIZE
) {
436 dprintf("dosfs_write: write position exceeds fat limits\n");
442 if (pos
+ *len
>= MAX_FILE_SIZE
) {
443 *len
= (size_t)(MAX_FILE_SIZE
- pos
);
446 // extend file size if needed
447 if (pos
+ *len
> node
->st_size
) {
448 uint32 clusters
= (pos
+ *len
+ vol
->bytes_per_sector
*vol
->sectors_per_cluster
- 1) / vol
->bytes_per_sector
/ vol
->sectors_per_cluster
;
449 if (node
->st_size
<= (clusters
- 1) * vol
->sectors_per_cluster
* vol
->bytes_per_sector
) {
450 if ((result
= set_fat_chain_length(vol
, node
, clusters
, false))
456 node
->st_size
= pos
+ *len
;
457 /* needs to be written to disk asap so that later vnid calculations
458 * by get_next_dirent are correct
460 write_vnode_entry(vol
, node
);
462 DPRINTF(0, ("setting file size to %" B_PRIdOFF
" (%" B_PRIu32
463 " clusters)\n", node
->st_size
, clusters
));
465 file_cache_set_size(node
->cache
, node
->st_size
);
466 file_map_set_size(node
->file_map
, node
->st_size
);
469 result
= file_cache_write(node
->cache
, cookie
, pos
, buf
, len
);
472 if (result
!= B_OK
) {
473 DPRINTF(0, ("dosfs_write (%s)\n", strerror(result
)));
475 DPRINTF(0, ("dosfs_write: wrote %" B_PRIuSIZE
" bytes\n", *len
));
484 dosfs_close(fs_volume
*_vol
, fs_vnode
*_node
, void *_cookie
)
486 nspace
*vol
= (nspace
*)_vol
->private_volume
;
487 vnode
*node
= (vnode
*)_node
->private_node
;
491 DPRINTF(0, ("dosfs_close (vnode id %" B_PRIdINO
")\n", node
->vnid
));
493 if ((vol
->fs_flags
& FS_FLAGS_OP_SYNC
) && node
->dirty
) {
505 dosfs_free_cookie(fs_volume
*_vol
, fs_vnode
*_node
, void *_cookie
)
507 nspace
*vol
= _vol
->private_volume
;
508 vnode
*node
= _node
->private_node
;
509 filecookie
*cookie
= _cookie
;
512 DPRINTF(0, ("dosfs_free_cookie (vnode id %" B_PRIdINO
")\n", node
->vnid
));
523 dosfs_create(fs_volume
*_vol
, fs_vnode
*_dir
, const char *name
, int omode
,
524 int perms
, void **_cookie
, ino_t
*vnid
)
526 nspace
*vol
= (nspace
*)_vol
->private_volume
;
527 vnode
*dir
= (vnode
*)_dir
->private_node
, *file
;
529 status_t result
= EINVAL
;
534 ASSERT(name
!= NULL
);
536 dprintf("dosfs_create called with null name\n");
541 DPRINTF(0, ("dosfs_create called: %" B_PRIdINO
"/%s perms=%o omode=%o\n",
542 dir
->vnid
, name
, perms
, omode
));
544 if (vol
->flags
& B_FS_IS_READONLY
) {
545 dprintf("dosfs_create called on read-only volume\n");
550 // TODO : is it needed ? vfs job ?
551 /*if (is_vnode_removed(vol->id, dir->vnid) > 0) {
552 dprintf("dosfs_create() called in removed directory. disallowed.\n");
557 if ((omode
& O_RWMASK
) == O_RDONLY
) {
558 dprintf("invalid permissions used in creating file\n");
563 // create file cookie; do it here to make cleaning up easier
564 if ((cookie
= calloc(sizeof(filecookie
), 1)) == NULL
) {
569 result
= findfile_case_duplicates(vol
, dir
, name
, vnid
, &file
, &dups_exist
);
570 if (result
== B_OK
) {
571 if (omode
& O_EXCL
) {
572 dprintf("exclusive dosfs_create called on existing file %s\n", name
);
573 put_vnode(_vol
, file
->vnid
);
578 if (file
->mode
& FAT_SUBDIR
) {
579 dprintf("can't dosfs_create over an existing subdirectory\n");
580 put_vnode(_vol
, file
->vnid
);
585 if (file
->disk_image
) {
586 dprintf("can't dosfs_create over a disk image\n");
587 put_vnode(_vol
, file
->vnid
);
592 if (omode
& O_TRUNC
) {
593 set_fat_chain_length(vol
, file
, 0, false);
597 } else if (result
== ENOENT
&& dups_exist
) {
598 // the file doesn't exist in the exact case, but another does in the
599 // non-exact case. We wont create the new file.
602 } else if (result
== ENOENT
&& !dups_exist
) {
603 // the file doesn't already exist in any case
604 vnode dummy
; /* used only to create directory entry */
606 dummy
.dir_vnid
= dir
->vnid
;
608 dummy
.end_cluster
= 0;
611 time(&(dummy
.st_time
));
612 dummy
.st_crtim
= dummy
.st_time
;
614 if ((result
= create_dir_entry(vol
, dir
, &dummy
, name
, &(dummy
.sindex
), &(dummy
.eindex
))) != B_OK
) {
615 dprintf("dosfs_create: error creating directory entry for %s (%s)\n", name
, strerror(result
));
618 dummy
.vnid
= GENERATE_DIR_INDEX_VNID(dummy
.dir_vnid
, dummy
.sindex
);
619 // XXX: dangerous construct
620 if (find_vnid_in_vcache(vol
, dummy
.vnid
) == B_OK
) {
621 dummy
.vnid
= generate_unique_vnid(vol
);
622 if ((result
= add_to_vcache(vol
, dummy
.vnid
, GENERATE_DIR_INDEX_VNID(dummy
.dir_vnid
, dummy
.sindex
))) < 0) {
623 // XXX: should remove entry on failure
624 if (vol
->fs_flags
& FS_FLAGS_OP_SYNC
)
631 result
= get_vnode(_vol
, *vnid
, (void **)&file
);
633 if (vol
->fs_flags
& FS_FLAGS_OP_SYNC
)
641 cookie
->mode
= omode
;
644 notify_entry_created(vol
->id
, dir
->vnid
, name
, *vnid
);
648 if (vol
->fs_flags
& FS_FLAGS_OP_SYNC
)
651 bi
: if (result
!= B_OK
) free(cookie
);
655 if (result
!= B_OK
) DPRINTF(0, ("dosfs_create (%s)\n", strerror(result
)));
662 dosfs_mkdir(fs_volume
*_vol
, fs_vnode
*_dir
, const char *name
, int perms
)
664 nspace
*vol
= (nspace
*)_vol
->private_volume
;
665 vnode
*dir
= (vnode
*)_dir
->private_node
, dummy
;
666 status_t result
= EINVAL
;
673 // TODO : is it needed ? vfs job ?
674 /*if (is_vnode_removed(vol->id, dir->vnid) > 0) {
675 dprintf("dosfs_mkdir() called in removed directory. disallowed.\n");
680 DPRINTF(0, ("dosfs_mkdir called: %" B_PRIdINO
"/%s (perm %o)\n", dir
->vnid
,
683 if ((dir
->mode
& FAT_SUBDIR
) == 0) {
684 dprintf("dosfs_mkdir: vnode id %" B_PRIdINO
" is not a directory\n",
690 // S_IFDIR is never set in perms, so we patch it
691 perms
&= ~S_IFMT
; perms
|= S_IFDIR
;
693 if (vol
->flags
& B_FS_IS_READONLY
) {
694 dprintf("mkdir called on read-only volume\n");
699 /* only used to create directory entry */
700 dummy
.dir_vnid
= dir
->vnid
;
701 if ((result
= allocate_n_fat_entries(vol
, 1, (int32
*)&(dummy
.cluster
))) < 0) {
702 dprintf("dosfs_mkdir: error allocating space for %s (%s))\n", name
, strerror(result
));
705 dummy
.end_cluster
= dummy
.cluster
;
706 dummy
.mode
= FAT_SUBDIR
;
707 if (!(perms
& (S_IWUSR
| S_IWGRP
| S_IWGRP
))) {
708 dummy
.mode
|= FAT_READ_ONLY
;
710 dummy
.st_size
= vol
->bytes_per_sector
*vol
->sectors_per_cluster
;
711 time(&(dummy
.st_time
));
712 dummy
.st_crtim
= dummy
.st_time
;
714 dummy
.vnid
= GENERATE_DIR_CLUSTER_VNID(dummy
.dir_vnid
, dummy
.cluster
);
715 // XXX: dangerous construct
716 if (find_vnid_in_vcache(vol
, dummy
.vnid
) == B_OK
) {
717 dummy
.vnid
= generate_unique_vnid(vol
);
718 if ((result
= add_to_vcache(vol
, dummy
.vnid
, GENERATE_DIR_CLUSTER_VNID(dummy
.dir_vnid
, dummy
.cluster
))) < 0)
722 if ((result
= dlist_add(vol
, dummy
.vnid
)) < 0) {
723 dprintf("dosfs_mkdir: error adding directory %s to dlist (%s)\n", name
, strerror(result
));
727 buffer
= malloc(vol
->bytes_per_sector
);
733 if ((result
= create_dir_entry(vol
, dir
, &dummy
, name
, &(dummy
.sindex
), &(dummy
.eindex
))) != B_OK
) {
734 dprintf("dosfs_mkdir: error creating directory entry for %s (%s))\n", name
, strerror(result
));
738 // create '.' and '..' entries and then end of directories
739 memset(buffer
, 0, vol
->bytes_per_sector
);
740 memset(buffer
, ' ', 11);
741 memset(buffer
+0x20, ' ', 11);
742 buffer
[0] = buffer
[0x20] = buffer
[0x21] = '.';
743 buffer
[0x0b] = buffer
[0x2b] = FAT_SUBDIR
;
744 i
= time_t2dos(dummy
.st_time
);
745 buffer
[0x0e] = i
& 0xff;
746 buffer
[0x0f] = (i
>> 8) & 0xff;
747 buffer
[0x10] = (i
>> 16) & 0xff;
748 buffer
[0x11] = (i
>> 24) & 0xff;
749 buffer
[0x16] = i
& 0xff;
750 buffer
[0x17] = (i
>> 8) & 0xff;
751 buffer
[0x18] = (i
>> 16) & 0xff;
752 buffer
[0x19] = (i
>> 24) & 0xff;
753 i
= time_t2dos(dir
->st_crtim
);
754 buffer
[0x2e] = i
& 0xff;
755 buffer
[0x2f] = (i
>> 8) & 0xff;
756 buffer
[0x30] = (i
>> 16) & 0xff;
757 buffer
[0x31] = (i
>> 24) & 0xff;
758 i
= time_t2dos(dir
->st_time
);
759 buffer
[0x36] = i
& 0xff;
760 buffer
[0x37] = (i
>> 8) & 0xff;
761 buffer
[0x38] = (i
>> 16) & 0xff;
762 buffer
[0x39] = (i
>> 24) & 0xff;
763 buffer
[0x1a] = dummy
.cluster
& 0xff;
764 buffer
[0x1b] = (dummy
.cluster
>> 8) & 0xff;
765 if (vol
->fat_bits
== 32) {
766 buffer
[0x14] = (dummy
.cluster
>> 16) & 0xff;
767 buffer
[0x15] = (dummy
.cluster
>> 24) & 0xff;
769 // root directory is always denoted by cluster 0, even for fat32 (!)
770 if (dir
->vnid
!= vol
->root_vnode
.vnid
) {
771 buffer
[0x3a] = dir
->cluster
& 0xff;
772 buffer
[0x3b] = (dir
->cluster
>> 8) & 0xff;
773 if (vol
->fat_bits
== 32) {
774 buffer
[0x34] = (dir
->cluster
>> 16) & 0xff;
775 buffer
[0x35] = (dir
->cluster
>> 24) & 0xff;
779 init_csi(vol
, dummy
.cluster
, 0, &csi
);
780 csi_write_block(&csi
, buffer
);
782 // clear out rest of cluster to keep scandisk happy
783 memset(buffer
, 0, vol
->bytes_per_sector
);
784 for (i
=1;i
<vol
->sectors_per_cluster
;i
++) {
785 if (iter_csi(&csi
, 1) != B_OK
) {
786 dprintf("dosfs_mkdir: error writing directory cluster\n");
789 csi_write_block(&csi
, buffer
);
794 notify_entry_created(vol
->id
, dir
->vnid
, name
, dummy
.vnid
);
798 if (vol
->fs_flags
& FS_FLAGS_OP_SYNC
)
807 dlist_remove(vol
, dummy
.vnid
);
809 if (IS_ARTIFICIAL_VNID(dummy
.vnid
))
810 remove_from_vcache(vol
, dummy
.vnid
);
812 clear_fat_chain(vol
, dummy
.cluster
, false);
813 if (vol
->fs_flags
& FS_FLAGS_OP_SYNC
)
817 if (result
!= B_OK
) DPRINTF(0, ("dosfs_mkdir (%s)\n", strerror(result
)));
823 dosfs_rename(fs_volume
*_vol
, fs_vnode
*_odir
, const char *oldname
,
824 fs_vnode
*_ndir
, const char *newname
)
826 status_t result
= EINVAL
;
827 nspace
*vol
= (nspace
*)_vol
->private_volume
;
828 vnode
*odir
= (vnode
*)_odir
->private_node
;
829 vnode
*ndir
= (vnode
*)_ndir
->private_node
;
837 DPRINTF(0, ("dosfs_rename called: %" B_PRIdINO
"/%s->%" B_PRIdINO
"/%s\n",
838 odir
->vnid
, oldname
, ndir
->vnid
, newname
));
840 if (!oldname
|| !(*oldname
) || !newname
|| !(*newname
)) {
845 if(!is_filename_legal(newname
)) {
846 dprintf("dosfs_rename called with invalid name '%s'\n", newname
);
851 if (vol
->flags
& B_FS_IS_READONLY
) {
852 dprintf("rename called on read-only volume\n");
857 if ((odir
->vnid
== ndir
->vnid
) && !strcmp(oldname
, newname
)) {
863 if ((result
= findfile_case(vol
,odir
,oldname
,NULL
,&file
)) != B_OK
) {
864 DPRINTF(0, ("dosfs_rename: can't find file %s in directory %" B_PRIdINO
865 "\n", oldname
, odir
->vnid
));
869 if (file
->disk_image
) {
870 dprintf("rename called on disk image or disk image directory\n");
875 // don't move a directory into one of its children
876 if (file
->mode
& FAT_SUBDIR
) {
877 ino_t vnid
= ndir
->vnid
;
882 if (vnid
== file
->vnid
) {
887 if (vnid
== vol
->root_vnode
.vnid
)
890 result
= get_vnode(_vol
, vnid
, (void **)&dir
);
893 parent
= dir
->dir_vnid
;
894 put_vnode(_vol
, vnid
);
899 // see if file already exists and erase it if it does
900 result
= findfile_case_duplicates(vol
, ndir
, newname
, NULL
, &file2
, &dups_exist
);
901 if (result
== B_OK
) {
902 if (file2
->mode
& FAT_SUBDIR
) {
903 dprintf("destination already occupied by a directory\n");
908 if (file2
->disk_image
) {
909 DPRINTF(0, ("dosfs_rename: can't replace disk image or disk image directory\n"));
913 ns
= file2
->sindex
; ne
= file2
->eindex
;
915 // let others know the old file is gone
916 notify_entry_removed(vol
->id
, ndir
->vnid
, oldname
, file2
->vnid
);
918 // Make sure this vnode 1) is in the vcache and 2) no longer has a
919 // location associated with it. See discussion in dosfs_unlink()
920 vcache_set_entry(vol
, file2
->vnid
, generate_unique_vnid(vol
));
922 // mark vnode for removal (dosfs_remove_vnode will clear the fat chain)
923 // note we don't have to lock the file because the fat chain doesn't
924 // get wiped from the disk until dosfs_remove_vnode() is called; we'll
925 // have a phantom chain in effect until the last file is closed.
926 remove_vnode(_vol
, file2
->vnid
); // must be done in this order
927 put_vnode(_vol
, file2
->vnid
);
931 // erase old directory entry
932 if ((result
= erase_dir_entry(vol
, file
)) != B_OK
) {
933 dprintf("dosfs_rename: error erasing old directory entry for %s (%s)\n", newname
, strerror(result
));
936 } else if (result
== ENOENT
&& (!dups_exist
|| (odir
->vnid
== ndir
->vnid
&& !strcasecmp(oldname
, newname
)))) {
937 // there isn't an entry and there are no duplicates in the target dir or
938 // there isn't an entry and the target dir is the same as the source dir and
939 // the source and target name are the same, case-insensitively
941 // erase old directory entry
942 if ((result
= erase_dir_entry(vol
, file
)) != B_OK
) {
943 dprintf("dosfs_rename: error erasing old directory entry for %s (%s)\n", newname
, strerror(result
));
949 // create the new directory entry
950 if ((result
= create_dir_entry(vol
, ndir
, file
, newname
, &ns
, &ne
)) != B_OK
) {
951 dprintf("dosfs_rename: error creating directory entry for %s\n", newname
);
954 } else if (result
== ENOENT
&& dups_exist
) {
955 // the entry doesn't exist but a non-case entry does, so we can't do it
962 // shrink the directory (an error here is not disastrous)
963 compact_directory(vol
, odir
);
967 // update vnode information
968 file
->dir_vnid
= ndir
->vnid
;
973 vcache_set_entry(vol
, file
->vnid
,
975 GENERATE_DIR_CLUSTER_VNID(file
->dir_vnid
, file
->cluster
) :
976 GENERATE_DIR_INDEX_VNID(file
->dir_vnid
, file
->sindex
));
978 // XXX: only write changes in the directory entry if needed
979 // (i.e. old entry, not new)
980 write_vnode_entry(vol
, file
);
982 if (file
->mode
& FAT_SUBDIR
) {
983 // update '..' directory entry if needed
984 // this should most properly be in write_vnode, but it is safe
985 // to keep it here since this is the only way the cluster of
986 // the parent can change.
989 if ((buffer
= diri_init(vol
, file
->cluster
, 1, &diri
)) == NULL
) {
990 dprintf("error opening directory :(\n");
995 diri_make_writable(&diri
);
997 if (memcmp(buffer
, ".. ", 11)) {
998 dprintf("invalid directory :(\n");
1002 if (ndir
->vnid
== vol
->root_vnode
.vnid
) {
1003 // root directory always has cluster = 0
1004 buffer
[0x1a] = buffer
[0x1b] = 0;
1006 buffer
[0x1a] = ndir
->cluster
& 0xff;
1007 buffer
[0x1b] = (ndir
->cluster
>> 8) & 0xff;
1008 if (vol
->fat_bits
== 32) {
1009 buffer
[0x14] = (ndir
->cluster
>> 16) & 0xff;
1010 buffer
[0x15] = (ndir
->cluster
>> 24) & 0xff;
1017 if (file
->filename
) free(file
->filename
);
1018 file
->filename
= malloc(strlen(newname
) + 1);
1019 if (file
->filename
) strcpy(file
->filename
, newname
);
1022 notify_entry_moved(vol
->id
, odir
->vnid
, oldname
, ndir
->vnid
, newname
,
1025 // update MIME information
1026 if(!(file
->mode
& FAT_SUBDIR
)) {
1027 set_mime_type(file
, newname
);
1028 notify_attribute_changed(vol
->id
, -1, file
->vnid
, "BEOS:TYPE",
1036 put_vnode(_vol
, file2
->vnid
);
1038 put_vnode(_vol
, file
->vnid
);
1040 if ((vol
->fs_flags
& FS_FLAGS_OP_SYNC
) && dirty
)
1043 if (result
!= B_OK
) DPRINTF(0, ("dosfs_rename (%s)\n", strerror(result
)));
1049 dosfs_remove_vnode(fs_volume
*_vol
, fs_vnode
*_node
, bool reenter
)
1051 nspace
*vol
= (nspace
*)_vol
->private_volume
;
1052 vnode
*node
= (vnode
*)_node
->private_node
;
1056 DPRINTF(0, ("dosfs_remove_vnode (%" B_PRIdINO
")\n", node
->vnid
));
1058 if (vol
->flags
& B_FS_IS_READONLY
) {
1059 dprintf("dosfs_remove_vnode: read-only volume\n");
1064 // clear the fat chain
1065 ASSERT((node
->cluster
== 0) || IS_DATA_CLUSTER(node
->cluster
));
1066 /* XXX: the following assertion was tripped */
1067 ASSERT((node
->cluster
!= 0) || (node
->st_size
== 0));
1068 if (node
->cluster
!= 0)
1069 clear_fat_chain(vol
, node
->cluster
, (node
->mode
& FAT_SUBDIR
) != 0);
1071 /* remove vnode id from the cache */
1072 if (find_vnid_in_vcache(vol
, node
->vnid
) == B_OK
)
1073 remove_from_vcache(vol
, node
->vnid
);
1075 /* at this point, the node shouldn't be in the dlist anymore */
1076 if ((node
->mode
& FAT_SUBDIR
) != 0) {
1077 ASSERT(dlist_find(vol
, CLUSTER_OF_DIR_CLUSTER_VNID(node
->vnid
)) == -1);
1082 if (!reenter
&& vol
->fs_flags
& FS_FLAGS_OP_SYNC
) {
1083 // sync the entire filesystem,
1084 // but only if we're not reentrant. Presumably the
1085 // function that called this will sync.
1095 // get rid of node or directory
1097 do_unlink(fs_volume
*_vol
, fs_vnode
*_dir
, const char *name
, bool is_file
)
1099 status_t result
= EINVAL
;
1100 nspace
*vol
= (nspace
*)_vol
->private_volume
;
1101 vnode
*dir
= (vnode
*)_dir
->private_node
, *file
;
1104 if (!strcmp(name
, "."))
1107 if (!strcmp(name
, ".."))
1112 DPRINTF(0, ("do_unlink %" B_PRIdINO
"/%s\n", dir
->vnid
, name
));
1114 if (vol
->flags
& B_FS_IS_READONLY
) {
1115 dprintf("do_unlink: read-only volume\n");
1121 if ((result
= findfile_case(vol
,dir
,name
,&vnid
,&file
)) != B_OK
) {
1122 DPRINTF(0, ("do_unlink: can't find file %s in directory %" B_PRIdINO
1123 "\n", name
, dir
->vnid
));
1128 if (file
->disk_image
) {
1129 DPRINTF(0, ("do_unlink: can't unlink disk image or disk image directory\n"));
1134 // don't need to check file permissions because it will be done for us
1135 // also don't need to lock the file (see dosfs_rename for reasons why)
1137 if (file
->mode
& FAT_SUBDIR
) {
1142 if ((file
->mode
& FAT_SUBDIR
) == 0) {
1147 if (file
->vnid
== vol
->root_vnode
.vnid
) {
1148 // this actually isn't a problem since the root vnode
1149 // will always be busy while the volume mounted
1150 dprintf("dosfs_rmdir: don't call this on the root directory\n");
1155 if ((result
= check_dir_empty(vol
, file
)) < 0) {
1156 if (result
== ENOTEMPTY
) DPRINTF(0, ("dosfs_rmdir called on non-empty directory\n"));
1161 // erase the entry in the parent directory
1162 if ((result
= erase_dir_entry(vol
, file
)) != B_OK
)
1165 // shrink the parent directory (errors here are not disastrous)
1166 compact_directory(vol
, dir
);
1168 notify_entry_removed(vol
->id
, dir
->vnid
, name
, file
->vnid
);
1170 /* Set the loc to a unique value. This effectively removes it from the
1171 * vcache without releasing its vnid for reuse. It also nicely reserves
1172 * the vnid from use by other nodes. This is okay because the vnode is
1173 * locked in memory after this point and loc will not be referenced from
1176 vcache_set_entry(vol
, file
->vnid
, generate_unique_vnid(vol
));
1179 dlist_remove(vol
, file
->vnid
);
1181 // fsil doesn't call dosfs_write_vnode for us, so we have to free the
1182 // vnode manually here.
1183 remove_vnode(_vol
, file
->vnid
);
1187 if (vol
->fs_flags
& FS_FLAGS_OP_SYNC
)
1191 put_vnode(_vol
, vnid
); // get 1 free
1195 if (result
!= B_OK
) DPRINTF(0, ("do_unlink (%s)\n", strerror(result
)));
1202 dosfs_unlink(fs_volume
*vol
, fs_vnode
*dir
, const char *name
)
1204 DPRINTF(1, ("dosfs_unlink called\n"));
1206 return do_unlink(vol
, dir
, name
, true);
1211 dosfs_rmdir(fs_volume
*vol
, fs_vnode
*dir
, const char *name
)
1213 DPRINTF(1, ("dosfs_rmdir called\n"));
1215 return do_unlink(vol
, dir
, name
, false);
1220 dosfs_can_page(fs_volume
*_vol
, fs_vnode
*_node
, void *_cookie
)
1222 // ToDo: we're obviously not even asked...
1228 dosfs_read_pages(fs_volume
*_vol
, fs_vnode
*_node
, void *_cookie
, off_t pos
,
1229 const iovec
*vecs
, size_t count
, size_t *_numBytes
)
1231 nspace
*vol
= (nspace
*)_vol
->private_volume
;
1232 vnode
*node
= (vnode
*)_node
->private_node
;
1233 uint32 vecIndex
= 0;
1234 size_t vecOffset
= 0;
1235 size_t bytesLeft
= *_numBytes
;
1238 if (node
->cache
== NULL
)
1239 return(B_BAD_VALUE
);
1244 struct file_io_vec fileVecs
[8];
1245 size_t fileVecCount
= 8;
1246 bool bufferOverflow
;
1247 size_t bytes
= bytesLeft
;
1249 status
= file_map_translate(node
->file_map
, pos
, bytesLeft
, fileVecs
,
1251 if (status
!= B_OK
&& status
!= B_BUFFER_OVERFLOW
)
1254 bufferOverflow
= status
== B_BUFFER_OVERFLOW
;
1256 status
= read_file_io_vec_pages(vol
->fd
, fileVecs
,
1257 fileVecCount
, vecs
, count
, &vecIndex
, &vecOffset
, &bytes
);
1258 if (status
!= B_OK
|| !bufferOverflow
)
1272 dosfs_write_pages(fs_volume
*_vol
, fs_vnode
*_node
, void *_cookie
, off_t pos
,
1273 const iovec
*vecs
, size_t count
, size_t *_numBytes
)
1275 nspace
*vol
= (nspace
*)_vol
->private_volume
;
1276 vnode
*node
= (vnode
*)_node
->private_node
;
1277 uint32 vecIndex
= 0;
1278 size_t vecOffset
= 0;
1279 size_t bytesLeft
= *_numBytes
;
1282 if (node
->cache
== NULL
)
1288 struct file_io_vec fileVecs
[8];
1289 size_t fileVecCount
= 8;
1290 bool bufferOverflow
;
1291 size_t bytes
= bytesLeft
;
1293 status
= file_map_translate(node
->file_map
, pos
, bytesLeft
, fileVecs
,
1295 if (status
!= B_OK
&& status
!= B_BUFFER_OVERFLOW
)
1298 bufferOverflow
= status
== B_BUFFER_OVERFLOW
;
1300 status
= write_file_io_vec_pages(vol
->fd
, fileVecs
,
1301 fileVecCount
, vecs
, count
, &vecIndex
, &vecOffset
, &bytes
);
1302 if (status
!= B_OK
|| !bufferOverflow
)
1316 dosfs_get_file_map(fs_volume
*_vol
, fs_vnode
*_node
, off_t position
,
1317 size_t length
, struct file_io_vec
*vecs
, size_t *_count
)
1319 nspace
*vol
= (nspace
*)_vol
->private_volume
;
1320 vnode
*node
= (vnode
*)_node
->private_node
;
1326 size_t max
= *_count
;
1331 if ((node
->mode
& FAT_SUBDIR
) != 0) {
1332 DPRINTF(0, ("dosfs_get_file_map called on subdirectory %" B_PRIdINO
1338 DPRINTF(0, ("dosfs_get_file_map called %" B_PRIuSIZE
" bytes at %" B_PRIdOFF
1339 " (vnode id %" B_PRIdINO
")\n", length
, position
, node
->vnid
));
1344 if (node
->st_size
== 0 || length
== 0 || position
>= node
->st_size
) {
1349 // Truncate to file size, taking overflow into account.
1350 if (position
+ length
>= node
->st_size
|| position
+ length
< position
)
1351 length
= node
->st_size
- position
;
1353 result
= init_csi(vol
, node
->cluster
, 0, &iter
);
1354 if (result
!= B_OK
) {
1355 dprintf("dosfs_get_file_map: invalid starting cluster (%" B_PRIu32
1356 ")\n", node
->cluster
);
1361 skipSectors
= position
/ vol
->bytes_per_sector
;
1362 if (skipSectors
> 0) {
1363 result
= iter_csi(&iter
, skipSectors
);
1364 if (result
!= B_OK
) {
1365 dprintf("dosfs_get_file_map: end of file reached (init)\n");
1371 ASSERT(iter
.cluster
== get_nth_fat_entry(vol
, node
->cluster
,
1372 position
/ vol
->bytes_per_sector
/ vol
->sectors_per_cluster
));
1374 offset
= position
% vol
->bytes_per_sector
;
1375 while (length
> 0) {
1376 off_t block
= csi_to_block(&iter
);
1379 length
-= min(length
, vol
->bytes_per_sector
- offset
);
1381 while (length
> 0) {
1382 result
= iter_csi(&iter
, 1);
1383 if (result
!= B_OK
) {
1384 dprintf("dosfs_get_file_map: end of file reached\n");
1389 if (block
+ sectors
!= csi_to_block(&iter
)) {
1390 // Disjoint sectors, need to flush and begin a new vector.
1394 length
-= min(length
, vol
->bytes_per_sector
);
1398 vecs
[index
].offset
= block
* vol
->bytes_per_sector
+ offset
;
1399 vecs
[index
].length
= sectors
* vol
->bytes_per_sector
- offset
;
1406 // we're out of file_io_vecs; let's bail out
1407 result
= B_BUFFER_OVERFLOW
;
1418 if (result
!= B_OK
) {
1419 DPRINTF(0, ("dosfs_get_file_map (%s)\n", strerror(result
)));