// Write at most 'n' bytes from 'buf' to 'fd' at the current seek position. // Advance 'fd->f_offset' by the number of bytes written. // // Returns: // The number of bytes successfully written. // < 0 on error. static ssize_t devfile_write(struct Fd *fd, const void *buf, size_t n) { // Use inode_open, inode_close, maybe inode_set_size, and inode_data. // Be careful of block boundaries! // Flush any blocks you change using BCREQ_FLUSH. // // LAB 5: Your code here. int r; size_t orig_offset = fd->fd_offset; struct Inode *ino; if((r = inode_open(fd->fd_file.inum, &ino)) < 0) return r; void *data; size_t n2 = ROUNDUP(fd->fd_offset, PGSIZE) - fd->fd_offset; if (n2 > n) n2 = n; if(n2) { n -= n2; if(fd->fd_offset + n2 > (size_t)ino->i_size && inode_set_size(ino, fd->fd_offset + n2) < 0) goto wrapup; data = inode_data(ino, fd->fd_offset); memcpy(data, buf, n2); fd->fd_offset += n2; bcache_ipc(data, BCREQ_FLUSH); buf = (void *)((char *)buf + n2); } while (n / PGSIZE) { if(fd->fd_offset + PGSIZE > (size_t)ino->i_size && inode_set_size(ino, fd->fd_offset + PGSIZE) < 0) goto wrapup; data = inode_data(ino, fd->fd_offset); memcpy(data, buf, PGSIZE); bcache_ipc(data, BCREQ_FLUSH); n -= PGSIZE; buf = (void *)((char *)buf + PGSIZE); fd->fd_offset += PGSIZE; } if (n > 0) { if(fd->fd_offset + n > (size_t)ino->i_size && inode_set_size(ino, fd->fd_offset + n) < 0) goto wrapup; data = inode_data(ino, fd->fd_offset); memcpy(data, buf, n); bcache_ipc(data, BCREQ_FLUSH); fd->fd_offset += n; } wrapup: inode_close(ino); return fd->fd_offset - orig_offset; }
int fs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) { struct inode *ino = (struct inode *)fi->fh; ino->i_mtime = time(NULL); return inode_set_size(ino, size); }
int fs_truncate(const char *path, off_t size) { struct inode *ino; int r; if ((r = inode_open(path, &ino)) < 0) return r; ino->i_mtime = time(NULL); return inode_set_size(ino, size); }
// Truncate or extend an open file to 'size' bytes static int devfile_trunc(struct Fd *fd, off_t newsize) { int r; struct Inode *ino; if ((r = inode_open(fd->fd_file.inum, &ino)) < 0) return r; r = inode_set_size(ino, newsize); inode_close(ino); return r; }
gfarm_error_t process_close_file_write(struct process *process, struct peer *peer, int fd, gfarm_off_t size, struct gfarm_timespec *atime, struct gfarm_timespec *mtime) { struct file_opening *fo; gfarm_error_t e = process_get_file_opening(process, fd, &fo); struct host *spool_host; if (e != GFARM_ERR_NO_ERROR) return (e); if (!inode_is_file(fo->inode)) return (GFARM_ERR_OPERATION_NOT_PERMITTED); if (fo->u.f.spool_opener != peer) return (GFARM_ERR_OPERATION_NOT_PERMITTED); if ((accmode_to_op(fo->flag) & GFS_W_OK) == 0) return (GFARM_ERR_BAD_FILE_DESCRIPTOR); if (fo->opener != peer && fo->opener != NULL) { spool_host = fo->u.f.spool_host; /* closing REOPENed file, but the client is still opening */ fo->u.f.spool_opener = NULL; fo->u.f.spool_host = NULL; /* * GFARM_FILE_CREATE_REPLICA means just to create a * file replica. */ if ((fo->flag & GFARM_FILE_CREATE_REPLICA) != 0) { e = inode_add_replica(fo->inode, spool_host, 1); /* if this is not the first replica, return */ if (e != GFARM_ERR_ALREADY_EXISTS) return (e); } else if (gfarm_timespec_cmp(inode_get_mtime(fo->inode), mtime)) /* invalidate file replicas if updated */ inode_remove_every_other_replicas( fo->inode, spool_host); inode_set_size(fo->inode, size); inode_set_atime(fo->inode, atime); inode_set_mtime(fo->inode, mtime); return (GFARM_ERR_NO_ERROR); } inode_close_write(fo, size, atime, mtime); file_opening_free(fo, 1); process->filetab[fd] = NULL; return (GFARM_ERR_NO_ERROR); }
// Find a directory entry in the 'ino' directory. // Looks for name 'name' with length 'namelen'. // If 'create != 0', such an entry is created if none is found. // A newly created entry will have de_inum == 0. // Returns 0 on success (storing a pointer to the struct Direntry in // *de_store), and < 0 on error. // Error codes: // -E_BAD_PATH if 'ino' is not a directory. // -E_NO_DISK if out of space. // (possibly others) // static int dir_find(struct Inode *ino, const char *name, int namelen, struct Direntry **de_store, int create) { off_t off; struct Direntry *empty = 0; *de_store = 0; if (ino->i_ftype != FTYPE_DIR) return -E_BAD_PATH; for (off = 0; off < ino->i_size; off += sizeof(struct Direntry)) { struct Direntry *de = (struct Direntry *) inode_data(ino, off); if (de->de_inum != 0 && de->de_namelen == namelen && memcmp(de->de_name, name, namelen) == 0) { *de_store = de; return 0; } if (de->de_inum == 0 && !empty) empty = de; } if (!create) return -E_NOT_FOUND; if (!empty) { int r = inode_set_size(ino, ino->i_size + sizeof(struct Direntry)); if (r < 0) return r; empty = (struct Direntry *) inode_data(ino, ino->i_size - sizeof(struct Direntry)); } memset(empty, 0, sizeof(struct Direntry)); empty->de_namelen = namelen; memcpy(empty->de_name, name, namelen); *de_store = empty; return 0; }
// Sets inode 'ino's size to 'size', // either allocating or freeing data blocks as required. // Returns 0 on success, < 0 on error. // Error codes: // -E_FILE_SIZE if 'size' is too big // -E_NO_DISK if out of disk space // possibly others // On error, the inode's size and disk's allocation state should be unchanged. // On success, any changed blocks are flushed. // static int inode_set_size(struct Inode *ino, size_t size) { // This function is correct as far as it goes, but does not handle // all cases. Read the spec carefully: what is missing? // LAB 5: Your code somewhere here int b1, b2; if (size > MAXFILESIZE) return -E_FILE_SIZE; b1 = ino->i_size / BLKSIZE; b2 = ROUNDUP(size, BLKSIZE) / BLKSIZE; if (size >= (size_t)ino->i_size) { for (; b1 < b2; ++b1) if (ino->i_direct[b1] == 0) { blocknum_t b = block_alloc(); if (b < 0) { inode_set_size(ino, ino->i_size); return -E_NO_DISK; } ino->i_direct[b1] = b; } } else { if(!(size % BLKSIZE)) b2--; bcache_ipc(freemap, BCREQ_MAP_WLOCK); for(; b2 < b1; b1--) if (ino->i_direct[b1] != 0) { freemap[ino->i_direct[b1]] = 1; ino->i_direct[b1] = 0; } bcache_ipc(freemap, BCREQ_UNLOCK_FLUSH); } ino->i_size = size; bcache_ipc(ino, BCREQ_FLUSH); return 0; }
void fs_test(void) { struct inode *ino, *ino2; int r; char *blk; uint32_t bits[4096]; // back up bitmap memmove(bits, bitmap, 4096); // allocate block if ((r = alloc_block()) < 0) panic("alloc_block: %s", strerror(-r)); // check that block was free assert(bits[r/32] & (1 << (r%32))); // and is not free any more assert(!(bitmap[r/32] & (1 << (r%32)))); free_block(r); printf("alloc_block is good\n"); if ((r = inode_open("/not-found", &ino)) < 0 && r != -ENOENT) panic("inode_open /not-found: %s", strerror(-r)); else if (r == 0) panic("inode_open /not-found succeeded!"); if ((r = inode_open("/msg", &ino)) < 0) panic("inode_open /msg: %s", strerror(-r)); printf("inode_open is good\n"); if ((r = inode_get_block(ino, 0, &blk)) < 0) panic("inode_get_block: %s", strerror(-r)); if (strcmp(blk, msg) != 0) panic("inode_get_block returned wrong data"); printf("inode_get_block is good\n"); if ((r = inode_set_size(ino, 0)) < 0) panic("inode_set_size: %s", strerror(-r)); assert(ino->i_direct[0] == 0); printf("inode_truncate is good\n"); if ((r = inode_set_size(ino, strlen(msg))) < 0) panic("inode_set_size 2: %s", strerror(-r)); if ((r = inode_get_block(ino, 0, &blk)) < 0) panic("inode_get_block 2: %s", strerror(-r)); strcpy(blk, msg); printf("file rewrite is good\n"); if ((r = inode_link("/msg", "/linkmsg")) < 0) panic("inode_link /msg /linkmsg: %s", strerror(-r)); if ((r = inode_open("/msg", &ino)) < 0) panic("inode_open /msg: %s", strerror(-r)); if ((r = inode_open("/linkmsg", &ino2)) < 0) panic("inode_open /linkmsg: %s", strerror(-r)); if (ino != ino2) panic("linked files do not point to same inode"); if (ino->i_nlink != 2) panic("link count incorrect: %u, expected 2", ino->i_nlink); printf("inode_link is good\n"); if ((r = inode_unlink("/linkmsg")) < 0) panic("inode_unlink /linkmsg: %s", strerror(-r)); if ((r = inode_open("/linkmsg", &ino2)) < 0 && r != -ENOENT) panic("inode_open /linkmsg after unlink: %s", strerror(-r)); else if (r == 0) panic("inode_open /linkmsg after unlink succeeded!"); if ((r = inode_open("/msg", &ino)) < 0) panic("inode_open /msg after /linkmsg unlinked: %s", strerror(-r)); if (ino->i_nlink != 1) panic("link count incorrect: %u, expected 1", ino->i_nlink); printf("inode_unlink is good\n"); }
// Open a file (or directory). // // Returns: // The file descriptor index on success // -E_BAD_PATH if the path is too long (>= MAXPATHLEN) // -E_BAD_PATH if an intermediate path component is not a directory // -E_MAX_FD if no more file descriptors // -E_NOT_FOUND if the file (or a path component) was not found // (and others) int open(const char *path, int mode) { // Find an unused file descriptor page using fd_find_unused // and allocate a page there (PTE_P|PTE_U|PTE_W|PTE_SHARE). // // LAB 5: Your code here int r; struct Fd *fd; if((r = fd_find_unused(&fd)) < 0 || (r = sys_page_alloc(0, fd, PTE_P|PTE_U|PTE_W|PTE_SHARE)) < 0) goto err1; // Check the pathname. Error if too long. // Use path_walk to find the corresponding directory entry. // If '(mode & O_CREAT) == 0' (Exercise 4), // Return -E_NOT_FOUND if the file is not found. // Otherwise, use inode_open to open the inode. // If '(mode & O_CREAT) != 0' (Exercise 7), // Create the file if it doesn't exist yet. // Allocate a new inode, initialize its fields, and // reference that inode from the new directory entry. // Flush any blocks you change. // Directories can be opened, but only as read-only: // return -E_IS_DIR if '(mode & O_ACCMODE) != O_RDONLY'. // // Check for errors. On error, make sure you clean up any // allocated objects. // // The root directory is a special case -- if you aren't careful, // you will deadlock when the root directory is opened. (Why?) // // LAB 5: Your code here. int i; for(i = 0; i < MAXNAMELEN && path[i]; i++); if(i == MAXNAMELEN) { r = -E_BAD_PATH; goto err2; } struct Inode *dirino; struct Direntry *de; if((r = path_walk(path, &dirino, &de, mode & O_CREAT))) goto err2; struct Inode *fileino; if(!(mode & O_CREAT)) { if(de == &super->s_root) fileino = dirino; else if((r = inode_open(de->de_inum, &fileino)) < 0) goto err3; if(fileino->i_ftype == FTYPE_DIR && ( mode & O_ACCMODE) != O_RDONLY) { r = -E_IS_DIR; goto err4; } } else { if((r = inode_alloc(&fileino)) < 0) goto err3; fileino->i_ftype = FTYPE_REG; fileino->i_refcount = 1; fileino->i_size = 0; memset(&fileino->i_direct, 0, NDIRECT * sizeof (blocknum_t)); de->de_inum = fileino->i_inum; bcache_ipc(fileino, BCREQ_FLUSH); bcache_ipc(dirino, BCREQ_FLUSH); } // If '(mode & O_TRUNC) != 0' and the open mode is not read-only, // set the file's length to 0. Flush any blocks you change. // // LAB 5: Your code here (Exercise 8). if((mode & O_TRUNC)) { inode_set_size(fileino, 0); } // The open has succeeded. // Fill in all parts of the 'fd' appropriately. Use 'devfile.dev_id'. // Copy the file's pathname into 'fd->fd_file.open_path' to improve // error messages later. // You must account for the open file reference in the inode as well. // Clean up any open inodes. // // LAB 5: Your code here (Exercise 4). fd->fd_dev_id = devfile.dev_id; fd->fd_offset = 0; fd->fd_omode = mode; fd->fd_file.inum = fileino->i_inum; strcpy(fd->fd_file.open_path, path); fileino->i_opencount++; inode_close(dirino); inode_close(fileino); return fd2num(fd); err4: inode_close(fileino); err3: inode_close(dirino); err2: sys_page_unmap(thisenv->env_id, fd); err1: return r; }