vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / file_systems / fat / file.c
blob86ab67a48d5cd25f2523a640ab17cd9d93453e0a
1 /*
2 Copyright 1999-2001, Be Incorporated. All Rights Reserved.
3 This file may be used under the terms of the Be Sample Code License.
4 */
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/stat.h>
10 #include <fs_cache.h>
11 #include <fs_info.h>
12 #include <Drivers.h>
13 #include <KernelExport.h>
14 #include <NodeMonitor.h>
16 #include <time.h>
18 #include "iter.h"
19 #include "dosfs.h"
20 #include "dlist.h"
21 #include "fat.h"
22 #include "dir.h"
23 #include "file.h"
24 #include "attr.h"
25 #include "vcache.h"
26 #include "util.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
35 } filecookie;
38 mode_t
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) {
43 result &= ~S_IFREG;
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);
49 return result;
53 status_t
54 dosfs_get_vnode_name(fs_volume *_ns, fs_vnode *_node, char *buffer,
55 size_t bufferSize)
57 vnode *node = (vnode*)_node->private_node;
58 strlcpy(buffer, node->filename, bufferSize);
59 return B_OK;
63 status_t write_vnode_entry(nspace *vol, vnode *node)
65 uint32 i;
66 struct diri diri;
67 uint8 *buffer;
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",
79 node->cluster);
80 return EINVAL;
83 buffer = diri_init(vol, VNODE_PARENT_DIR_CLUSTER(node), node->eindex, &diri);
84 if (buffer == NULL)
85 return ENOENT;
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;
109 } else {
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;
116 diri_free(&diri);
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);
124 return B_OK;
128 // called when fs is done with vnode
129 // after close, etc. free vnode resources here
130 status_t
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;
136 TOUCH(reenter);
138 DPRINTF(0, ("dosfs_release_vnode (ino_t %" B_PRIdINO ")\n", node->vnid));
140 if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && node->dirty) {
141 LOCK_VOL(vol);
142 _dosfs_sync(vol);
143 UNLOCK_VOL(vol);
146 if (node != NULL) {
147 #if TRACK_FILENAME
148 if (node->filename) free(node->filename);
149 #endif
151 if (node->vnid != vol->root_vnode.vnid) {
152 file_cache_delete(node->cache);
153 file_map_delete(node->file_map);
154 free(node);
158 return 0;
162 status_t
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;
168 LOCK_VOL(vol);
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);
176 st->st_nlink = 1;
177 st->st_uid = 0;
178 st->st_gid = 0;
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
183 = node->st_time;
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;
188 UNLOCK_VOL(vol);
190 return B_NO_ERROR;
194 status_t
195 dosfs_wstat(fs_volume *_vol, fs_vnode *_node, const struct stat *st,
196 uint32 mask)
198 int err = B_OK;
199 nspace *vol = (nspace*)_vol->private_volume;
200 vnode *node = (vnode*)_node->private_node;
201 bool dirty = false;
203 LOCK_VOL(vol);
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");
209 UNLOCK_VOL(vol);
210 return EROFS;
213 if (node->disk_image == 2) {
214 dprintf("can't wstat disk image\n");
215 UNLOCK_VOL(vol);
216 return EPERM;
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;
223 else
224 node->mode |= FAT_READ_ONLY;
225 dirty = true;
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");
232 err = EISDIR;
233 } else if (st->st_size > MAX_FILE_SIZE) {
234 dprintf("dosfs_wstat: desired file size exceeds fat limit\n");
235 err = E2BIG;
236 } else {
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",
241 clusters));
242 if ((err = set_fat_chain_length(vol, node, clusters, false))
243 == B_OK) {
244 node->st_size = st->st_size;
245 node->iteration++;
246 dirty = true;
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;
258 dirty = true;
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;
266 dirty = true;
269 if (dirty) {
270 write_vnode_entry(vol, node);
272 if (vol->fs_flags & FS_FLAGS_OP_SYNC) {
273 // sync the filesystem
274 _dosfs_sync(vol);
275 node->dirty = false;
279 if (err != B_OK) DPRINTF(0, ("dosfs_wstat (%s)\n", strerror(err)));
281 UNLOCK_VOL(vol);
283 return err;
287 status_t
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;
293 filecookie *cookie;
295 *_cookie = NULL;
297 LOCK_VOL(vol);
299 DPRINTF(0, ("dosfs_open: vnode id %" B_PRIdINO ", omode %o\n", node->vnid,
300 omode));
302 if (omode & O_CREAT) {
303 dprintf("dosfs_open called with O_CREAT. call dosfs_create instead!\n");
304 result = EINVAL;
305 goto error;
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"));
319 result = EPERM;
320 goto error;
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");
327 goto error;
329 node->mode = 0;
330 node->st_size = 0;
331 node->iteration++;
334 if ((cookie = calloc(sizeof(filecookie), 1)) == NULL) {
335 result = ENOMEM;
336 goto error;
339 cookie->mode = omode;
340 *_cookie = cookie;
341 result = B_OK;
343 error:
344 if (result != B_OK) DPRINTF(0, ("dosfs_open (%s)\n", strerror(result)));
346 UNLOCK_VOL(vol);
347 return result;
351 status_t
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;
358 int result = B_OK;
360 LOCK_VOL(vol);
362 if (node->mode & FAT_SUBDIR) {
363 DPRINTF(0, ("dosfs_read called on subdirectory %" B_PRIdINO "\n",
364 node->vnid));
365 *len = 0;
366 UNLOCK_VOL(vol);
367 return EISDIR;
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)) {
376 *len = 0;
377 goto bi;
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)));
389 } else {
390 DPRINTF(0, ("dosfs_read: read %" B_PRIuSIZE " bytes\n", *len));
392 UNLOCK_VOL(vol);
394 return result;
398 status_t
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;
405 int result = B_OK;
407 LOCK_VOL(vol);
409 if (node->mode & FAT_SUBDIR) {
410 DPRINTF(0, ("dosfs_write called on subdirectory %" B_PRIdINO "\n",
411 node->vnid));
412 *len = 0;
413 UNLOCK_VOL(vol);
414 return EISDIR;
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,
420 node->vnid));
422 if ((cookie->mode & O_RWMASK) == O_RDONLY) {
423 dprintf("dosfs_write: called on file opened as read-only\n");
424 *len = 0;
425 result = EPERM;
426 goto bi;
429 if (pos < 0) pos = 0;
431 if (cookie->mode & O_APPEND) {
432 pos = node->st_size;
435 if (pos >= MAX_FILE_SIZE) {
436 dprintf("dosfs_write: write position exceeds fat limits\n");
437 *len = 0;
438 result = E2BIG;
439 goto bi;
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))
451 != B_OK) {
452 goto bi;
454 node->iteration++;
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));
464 node->dirty = true;
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)));
474 } else {
475 DPRINTF(0, ("dosfs_write: wrote %" B_PRIuSIZE " bytes\n", *len));
477 UNLOCK_VOL(vol);
479 return result;
483 status_t
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;
489 LOCK_VOL(vol);
491 DPRINTF(0, ("dosfs_close (vnode id %" B_PRIdINO ")\n", node->vnid));
493 if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && node->dirty) {
494 _dosfs_sync(vol);
495 node->dirty = false;
498 UNLOCK_VOL(vol);
500 return 0;
504 status_t
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;
510 LOCK_VOL(vol);
512 DPRINTF(0, ("dosfs_free_cookie (vnode id %" B_PRIdINO ")\n", node->vnid));
514 free(cookie);
516 UNLOCK_VOL(vol);
518 return 0;
522 status_t
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;
528 filecookie *cookie;
529 status_t result = EINVAL;
530 bool dups_exist;
532 LOCK_VOL(vol);
534 ASSERT(name != NULL);
535 if (name == NULL) {
536 dprintf("dosfs_create called with null name\n");
537 UNLOCK_VOL(vol);
538 return EINVAL;
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");
546 UNLOCK_VOL(vol);
547 return EROFS;
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");
553 UNLOCK_VOL(vol);
554 return EPERM;
557 if ((omode & O_RWMASK) == O_RDONLY) {
558 dprintf("invalid permissions used in creating file\n");
559 UNLOCK_VOL(vol);
560 return EPERM;
563 // create file cookie; do it here to make cleaning up easier
564 if ((cookie = calloc(sizeof(filecookie), 1)) == NULL) {
565 result = ENOMEM;
566 goto bi;
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);
574 result = EEXIST;
575 goto bi;
578 if (file->mode & FAT_SUBDIR) {
579 dprintf("can't dosfs_create over an existing subdirectory\n");
580 put_vnode(_vol, file->vnid);
581 result = EPERM;
582 goto bi;
585 if (file->disk_image) {
586 dprintf("can't dosfs_create over a disk image\n");
587 put_vnode(_vol, file->vnid);
588 result = EPERM;
589 goto bi;
592 if (omode & O_TRUNC) {
593 set_fat_chain_length(vol, file, 0, false);
594 file->st_size = 0;
595 file->iteration++;
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.
600 result = EEXIST;
601 goto bi;
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;
607 dummy.cluster = 0;
608 dummy.end_cluster = 0;
609 dummy.mode = 0;
610 dummy.st_size = 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));
616 goto bi;
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)
625 _dosfs_sync(vol);
626 goto bi;
629 *vnid = dummy.vnid;
631 result = get_vnode(_vol, *vnid, (void **)&file);
632 if (result < B_OK) {
633 if (vol->fs_flags & FS_FLAGS_OP_SYNC)
634 _dosfs_sync(vol);
635 goto bi;
637 } else {
638 goto bi;
641 cookie->mode = omode;
642 *_cookie = cookie;
644 notify_entry_created(vol->id, dir->vnid, name, *vnid);
646 result = 0;
648 if (vol->fs_flags & FS_FLAGS_OP_SYNC)
649 _dosfs_sync(vol);
651 bi: if (result != B_OK) free(cookie);
653 UNLOCK_VOL(vol);
655 if (result != B_OK) DPRINTF(0, ("dosfs_create (%s)\n", strerror(result)));
657 return result;
661 status_t
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;
667 struct csi csi;
668 uchar *buffer;
669 uint32 i;
671 LOCK_VOL(vol);
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");
676 UNLOCK_VOL(vol);
677 return EPERM;
680 DPRINTF(0, ("dosfs_mkdir called: %" B_PRIdINO "/%s (perm %o)\n", dir->vnid,
681 name, perms));
683 if ((dir->mode & FAT_SUBDIR) == 0) {
684 dprintf("dosfs_mkdir: vnode id %" B_PRIdINO " is not a directory\n",
685 dir->vnid);
686 UNLOCK_VOL(vol);
687 return EINVAL;
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");
695 UNLOCK_VOL(vol);
696 return EROFS;
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));
703 goto bi;
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)
719 goto bi2;
722 if ((result = dlist_add(vol, dummy.vnid)) < 0) {
723 dprintf("dosfs_mkdir: error adding directory %s to dlist (%s)\n", name, strerror(result));
724 goto bi3;
727 buffer = malloc(vol->bytes_per_sector);
728 if (!buffer) {
729 result = ENOMEM;
730 goto bi4;
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));
735 goto bi5;
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");
787 break;
789 csi_write_block(&csi, buffer);
792 free(buffer);
794 notify_entry_created(vol->id, dir->vnid, name, dummy.vnid);
796 result = B_OK;
798 if (vol->fs_flags & FS_FLAGS_OP_SYNC)
799 _dosfs_sync(vol);
801 UNLOCK_VOL(vol);
802 return result;
804 bi5:
805 free(buffer);
806 bi4:
807 dlist_remove(vol, dummy.vnid);
808 bi3:
809 if (IS_ARTIFICIAL_VNID(dummy.vnid))
810 remove_from_vcache(vol, dummy.vnid);
811 bi2:
812 clear_fat_chain(vol, dummy.cluster, false);
813 if (vol->fs_flags & FS_FLAGS_OP_SYNC)
814 _dosfs_sync(vol);
816 UNLOCK_VOL(vol);
817 if (result != B_OK) DPRINTF(0, ("dosfs_mkdir (%s)\n", strerror(result)));
818 return result;
822 status_t
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;
830 vnode *file, *file2;
831 uint32 ns, ne;
832 bool dups_exist;
833 bool dirty = false;
835 LOCK_VOL(vol);
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)) {
841 result = EINVAL;
842 goto bi;
845 if(!is_filename_legal(newname)) {
846 dprintf("dosfs_rename called with invalid name '%s'\n", newname);
847 result = EINVAL;
848 goto bi;
851 if (vol->flags & B_FS_IS_READONLY) {
852 dprintf("rename called on read-only volume\n");
853 result = EROFS;
854 goto bi;
857 if ((odir->vnid == ndir->vnid) && !strcmp(oldname, newname)) {
858 result = EPERM;
859 goto bi;
862 // locate the file
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));
866 goto bi;
869 if (file->disk_image) {
870 dprintf("rename called on disk image or disk image directory\n");
871 result = EPERM;
872 goto bi1;
875 // don't move a directory into one of its children
876 if (file->mode & FAT_SUBDIR) {
877 ino_t vnid = ndir->vnid;
878 while (1) {
879 vnode *dir;
880 ino_t parent;
882 if (vnid == file->vnid) {
883 result = EINVAL;
884 goto bi1;
887 if (vnid == vol->root_vnode.vnid)
888 break;
890 result = get_vnode(_vol, vnid, (void **)&dir);
891 if (result < B_OK)
892 goto bi1;
893 parent = dir->dir_vnid;
894 put_vnode(_vol, vnid);
895 vnid = parent;
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");
904 result = EPERM;
905 goto bi2;
908 if (file2->disk_image) {
909 DPRINTF(0, ("dosfs_rename: can't replace disk image or disk image directory\n"));
910 result = EPERM;
911 goto bi2;
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);
929 dirty = true;
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));
934 goto bi1;
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));
944 goto bi1;
947 dirty = true;
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);
952 goto bi1;
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
956 result = EEXIST;
957 goto bi1;
958 } else {
959 goto bi1;
962 // shrink the directory (an error here is not disastrous)
963 compact_directory(vol, odir);
965 dirty = true;
967 // update vnode information
968 file->dir_vnid = ndir->vnid;
969 file->sindex = ns;
970 file->eindex = ne;
972 // update vcache
973 vcache_set_entry(vol, file->vnid,
974 (file->st_size) ?
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.
987 struct diri diri;
988 uint8 *buffer;
989 if ((buffer = diri_init(vol, file->cluster, 1, &diri)) == NULL) {
990 dprintf("error opening directory :(\n");
991 result = EIO;
992 goto bi2;
995 diri_make_writable(&diri);
997 if (memcmp(buffer, ".. ", 11)) {
998 dprintf("invalid directory :(\n");
999 result = EIO;
1000 goto bi2;
1002 if (ndir->vnid == vol->root_vnode.vnid) {
1003 // root directory always has cluster = 0
1004 buffer[0x1a] = buffer[0x1b] = 0;
1005 } else {
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;
1013 diri_free(&diri);
1016 #if TRACK_FILENAME
1017 if (file->filename) free(file->filename);
1018 file->filename = malloc(strlen(newname) + 1);
1019 if (file->filename) strcpy(file->filename, newname);
1020 #endif
1022 notify_entry_moved(vol->id, odir->vnid, oldname, ndir->vnid, newname,
1023 file->vnid);
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",
1029 B_ATTR_CHANGED);
1032 result = 0;
1034 bi2:
1035 if (result != B_OK)
1036 put_vnode(_vol, file2->vnid);
1037 bi1:
1038 put_vnode(_vol, file->vnid);
1040 if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && dirty)
1041 _dosfs_sync(vol);
1042 UNLOCK_VOL(vol);
1043 if (result != B_OK) DPRINTF(0, ("dosfs_rename (%s)\n", strerror(result)));
1044 return result;
1048 status_t
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;
1054 LOCK_VOL(vol);
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");
1060 UNLOCK_VOL(vol);
1061 return EROFS;
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);
1080 free(node);
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.
1086 _dosfs_sync(vol);
1089 UNLOCK_VOL(vol);
1091 return B_OK;
1095 // get rid of node or directory
1096 static status_t
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;
1102 ino_t vnid;
1104 if (!strcmp(name, "."))
1105 return EPERM;
1107 if (!strcmp(name, ".."))
1108 return EPERM;
1110 LOCK_VOL(vol);
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");
1116 result = EROFS;
1117 goto bi;
1120 // locate the file
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));
1124 result = ENOENT;
1125 goto bi;
1128 if (file->disk_image) {
1129 DPRINTF(0, ("do_unlink: can't unlink disk image or disk image directory\n"));
1130 result = EPERM;
1131 goto bi1;
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)
1136 if (is_file) {
1137 if (file->mode & FAT_SUBDIR) {
1138 result = EISDIR;
1139 goto bi1;
1141 } else {
1142 if ((file->mode & FAT_SUBDIR) == 0) {
1143 result = ENOTDIR;
1144 goto bi1;
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");
1151 result = EPERM;
1152 goto bi1;
1155 if ((result = check_dir_empty(vol, file)) < 0) {
1156 if (result == ENOTEMPTY) DPRINTF(0, ("dosfs_rmdir called on non-empty directory\n"));
1157 goto bi1;
1161 // erase the entry in the parent directory
1162 if ((result = erase_dir_entry(vol, file)) != B_OK)
1163 goto bi1;
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
1174 * here on.
1176 vcache_set_entry(vol, file->vnid, generate_unique_vnid(vol));
1178 if (!is_file)
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);
1185 result = 0;
1187 if (vol->fs_flags & FS_FLAGS_OP_SYNC)
1188 _dosfs_sync(vol);
1190 bi1:
1191 put_vnode(_vol, vnid); // get 1 free
1193 UNLOCK_VOL(vol);
1195 if (result != B_OK) DPRINTF(0, ("do_unlink (%s)\n", strerror(result)));
1197 return result;
1201 status_t
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);
1210 status_t
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);
1219 bool
1220 dosfs_can_page(fs_volume *_vol, fs_vnode *_node, void *_cookie)
1222 // ToDo: we're obviously not even asked...
1223 return false;
1227 status_t
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;
1236 status_t status;
1238 if (node->cache == NULL)
1239 return(B_BAD_VALUE);
1241 LOCK_VOL(vol);
1243 while (true) {
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,
1250 &fileVecCount, 0);
1251 if (status != B_OK && status != B_BUFFER_OVERFLOW)
1252 break;
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)
1259 break;
1261 pos += bytes;
1262 bytesLeft -= bytes;
1265 UNLOCK_VOL(vol);
1267 return status;
1271 status_t
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;
1280 status_t status;
1282 if (node->cache == NULL)
1283 return B_BAD_VALUE;
1285 LOCK_VOL(vol);
1287 while (true) {
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,
1294 &fileVecCount, 0);
1295 if (status != B_OK && status != B_BUFFER_OVERFLOW)
1296 break;
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)
1303 break;
1305 pos += bytes;
1306 bytesLeft -= bytes;
1309 UNLOCK_VOL(vol);
1311 return status;
1315 status_t
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;
1321 struct csi iter;
1322 status_t result;
1323 uint32 skipSectors;
1324 off_t offset;
1325 size_t index = 0;
1326 size_t max = *_count;
1328 LOCK_VOL(vol);
1329 *_count = 0;
1331 if ((node->mode & FAT_SUBDIR) != 0) {
1332 DPRINTF(0, ("dosfs_get_file_map called on subdirectory %" B_PRIdINO
1333 "\n", node->vnid));
1334 UNLOCK_VOL(vol);
1335 return EISDIR;
1338 DPRINTF(0, ("dosfs_get_file_map called %" B_PRIuSIZE " bytes at %" B_PRIdOFF
1339 " (vnode id %" B_PRIdINO ")\n", length, position, node->vnid));
1341 if (position < 0)
1342 position = 0;
1344 if (node->st_size == 0 || length == 0 || position >= node->st_size) {
1345 result = B_OK;
1346 goto bi;
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);
1357 result = EIO;
1358 goto bi;
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");
1366 result = EIO;
1367 goto bi;
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);
1377 uint32 sectors = 1;
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");
1385 result = EIO;
1386 goto bi;
1389 if (block + sectors != csi_to_block(&iter)) {
1390 // Disjoint sectors, need to flush and begin a new vector.
1391 break;
1394 length -= min(length, vol->bytes_per_sector);
1395 sectors++;
1398 vecs[index].offset = block * vol->bytes_per_sector + offset;
1399 vecs[index].length = sectors * vol->bytes_per_sector - offset;
1400 index++;
1402 if (length == 0)
1403 break;
1405 if (index >= max) {
1406 // we're out of file_io_vecs; let's bail out
1407 result = B_BUFFER_OVERFLOW;
1408 goto bi;
1411 offset = 0;
1414 result = B_OK;
1416 *_count = index;
1418 if (result != B_OK) {
1419 DPRINTF(0, ("dosfs_get_file_map (%s)\n", strerror(result)));
1421 UNLOCK_VOL(vol);
1423 return result;