int dowriteblock(int b, int blocksize, u32_t seed, char *data) { struct buf *bp; assert(blocksize == curblocksize); if(!(bp = lmfs_get_block(MYDEV, b, NORMAL))) { e(30); return 0; } memcpy(bp->data, data, blocksize); lmfs_markdirty(bp); lmfs_put_block(bp, FULL_DATA_BLOCK); return blocksize; }
int dowriteblock(int b, int blocksize, u32_t seed, char *data) { struct buf *bp; int r; assert(blocksize == curblocksize); if ((r = lmfs_get_block(&bp, MYDEV, b, NORMAL)) != 0) { e(30); return 0; } memcpy(bp->data, data, blocksize); lmfs_markdirty(bp); lmfs_put_block(bp); return blocksize; }
/* * Perform block I/O, on "dev", starting from offset "pos", for a total of * "bytes" bytes. Reading, writing, and peeking are highly similar, and thus, * this function implements all of them. The "call" parameter indicates the * call type (one of FSC_READ, FSC_WRITE, FSC_PEEK). For read and write calls, * "data" will identify the user buffer to use; for peek calls, "data" is set * to NULL. In all cases, this function returns the number of bytes * successfully transferred, 0 on end-of-file conditions, and a negative error * code if no bytes could be transferred due to an error. Dirty data is not * flushed immediately, and thus, a successful write only indicates that the * data have been taken in by the cache (for immediate I/O, a character device * would have to be used, but MINIX3 no longer supports this), which may be * follwed later by silent failures, including undetected end-of-file cases. * In particular, write requests may or may not return 0 (EOF) immediately when * writing at or beyond the block device's size. i Since block I/O takes place * at block granularity, block-unaligned writes have to read a block from disk * before updating it, and that is the only possible source of actual I/O * errors for write calls. * TODO: reconsider the buffering-only approach, or see if we can at least * somehow throw accurate EOF errors without reading in each block first. */ ssize_t lmfs_bio(dev_t dev, struct fsdriver_data * data, size_t bytes, off_t pos, int call) { block_t block, blocks_left; size_t block_size, off, block_off, chunk; struct buf *bp; int r, write, how; if (dev == NO_DEV) return EINVAL; block_size = lmfs_fs_block_size(); write = (call == FSC_WRITE); assert(block_size > 0); /* FIXME: block_t is 32-bit, so we have to impose a limit here. */ if (pos < 0 || pos / block_size > UINT32_MAX || bytes > SSIZE_MAX) return EINVAL; off = 0; block = pos / block_size; block_off = (size_t)(pos % block_size); blocks_left = howmany(block_off + bytes, block_size); lmfs_reset_rdwt_err(); r = OK; for (off = 0; off < bytes; off += chunk) { chunk = block_size - block_off; if (chunk > bytes - off) chunk = bytes - off; /* * For read requests, help the block driver form larger I/O * requests. */ if (!write) block_prefetch(dev, block, blocks_left); /* * Do not read the block from disk if we will end up * overwriting all of its contents. */ how = (write && chunk == block_size) ? NO_READ : NORMAL; bp = lmfs_get_block(dev, block, how); assert(bp); r = lmfs_rdwt_err(); if (r == OK && data != NULL) { assert(lmfs_dev(bp) != NO_DEV); if (write) { r = fsdriver_copyin(data, off, (char *)bp->data + block_off, chunk); /* * Mark the block as dirty even if the copy * failed, since the copy may in fact have * succeeded partially. This is an interface * issue that should be resolved at some point, * but for now we do not want the cache to be * desynchronized from the disk contents. */ lmfs_markdirty(bp); } else r = fsdriver_copyout(data, off, (char *)bp->data + block_off, chunk); } lmfs_put_block(bp, FULL_DATA_BLOCK); if (r != OK) break; block++; block_off = 0; blocks_left--; } /* * If we were not able to do any I/O, return the error (or EOF, even * for writes). Otherwise, return how many bytes we did manage to * transfer. */ if (r != OK && off == 0) return (r == END_OF_FILE) ? 0 : r; return off; }
/*===========================================================================* * fs_slink * *===========================================================================*/ int fs_slink() { phys_bytes len; struct inode *sip; /* inode containing symbolic link */ struct inode *ldirp; /* directory containing link */ register int r; /* error code */ char string[NAME_MAX]; /* last component of the new dir's path name */ char* link_target_buf = NULL; /* either sip->i_block or bp->b_data */ struct buf *bp = NULL; /* disk buffer for link */ caller_uid = (uid_t) fs_m_in.REQ_UID; caller_gid = (gid_t) fs_m_in.REQ_GID; /* Copy the link name's last component */ len = fs_m_in.REQ_PATH_LEN; if (len > NAME_MAX || len > EXT2_NAME_MAX) return(ENAMETOOLONG); r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT, (vir_bytes) 0, (vir_bytes) string, (size_t) len); if (r != OK) return(r); NUL(string, len, sizeof(string)); /* Temporarily open the dir. */ if( (ldirp = get_inode(fs_dev, (ino_t) fs_m_in.REQ_INODE_NR)) == NULL) return(EINVAL); /* Create the inode for the symlink. */ sip = new_node(ldirp, string, (mode_t) (I_SYMBOLIC_LINK | RWX_MODES), (block_t) 0); /* If we can then create fast symlink (store it in inode), * Otherwise allocate a disk block for the contents of the symlink and * copy contents of symlink (the name pointed to) into first disk block. */ if( (r = err_code) == OK) { if ( (fs_m_in.REQ_MEM_SIZE + 1) > sip->i_sp->s_block_size) { r = ENAMETOOLONG; } else if ((fs_m_in.REQ_MEM_SIZE + 1) <= MAX_FAST_SYMLINK_LENGTH) { r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT3, (vir_bytes) 0, (vir_bytes) sip->i_block, (vir_bytes) fs_m_in.REQ_MEM_SIZE); sip->i_dirt = IN_DIRTY; link_target_buf = (char*) sip->i_block; } else { if ((bp = new_block(sip, (off_t) 0)) != NULL) { sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT3, (vir_bytes) 0, (vir_bytes) b_data(bp), (vir_bytes) fs_m_in.REQ_MEM_SIZE); lmfs_markdirty(bp); link_target_buf = b_data(bp); } else { r = err_code; } } if (r == OK) { assert(link_target_buf); link_target_buf[fs_m_in.REQ_MEM_SIZE] = '\0'; sip->i_size = (off_t) strlen(link_target_buf); if (sip->i_size != fs_m_in.REQ_MEM_SIZE) { /* This can happen if the user provides a buffer * with a \0 in it. This can cause a lot of trouble * when the symlink is used later. We could just use * the strlen() value, but we want to let the user * know he did something wrong. ENAMETOOLONG doesn't * exactly describe the error, but there is no * ENAMETOOWRONG. */ r = ENAMETOOLONG; } } put_block(bp, DIRECTORY_BLOCK); /* put_block() accepts NULL. */ if(r != OK) { sip->i_links_count = NO_LINK; if (search_dir(ldirp, string, NULL, DELETE, IGN_PERM, 0) != OK) panic("Symbolic link vanished"); } } /* put_inode() accepts NULL as a noop, so the below are safe. */ put_inode(sip); put_inode(ldirp); return(r); }