static struct buffer_head *minix_find_entry(register struct inode *dir, char *name, size_t namelen, struct minix_dir_entry **res_dir) { register struct buffer_head *bh; struct minix_sb_info *info; block_t block; loff_t offset; *res_dir = NULL; if (!dir || !dir->i_sb) return NULL; info = &dir->i_sb->u.minix_sb; if (namelen > info->s_namelen) { #ifdef NO_TRUNCATE return NULL; #else namelen = info->s_namelen; #endif } bh = NULL; block = 0; offset = 0L; while (block * BLOCK_SIZE + offset < dir->i_size) { if (!bh) { bh = minix_bread(dir, block, 0); if (!bh) { block++; continue; } map_buffer(bh); } *res_dir = (struct minix_dir_entry *) (bh->b_data + offset); if (minix_match(namelen, name, bh, &offset, info)) { return bh; } if (offset >= BLOCK_SIZE) { unmap_brelse(bh); bh = NULL; offset = 0; block++; } } unmap_brelse(bh); *res_dir = NULL; return NULL; }
int minix_rmdir(register struct inode *dir, char *name, size_t len) { int retval; register struct inode *inode; struct buffer_head *bh; struct minix_dir_entry *de; inode = NULL; bh = minix_find_entry(dir, name, len, &de); retval = -ENOENT; if (!bh) goto end_rmdir; retval = -EPERM; if (!(inode = iget(dir->i_sb, (ino_t) de->inode))) goto end_rmdir; if ((dir->i_mode & S_ISVTX) && !suser() && current->euid != inode->i_uid && current->euid != dir->i_uid) goto end_rmdir; if (inode->i_dev != dir->i_dev) goto end_rmdir; if (inode == dir) /* we may not delete ".", but "../dir" is ok */ goto end_rmdir; if (!S_ISDIR(inode->i_mode)) { retval = -ENOTDIR; goto end_rmdir; } if (!empty_dir(inode)) { retval = -ENOTEMPTY; goto end_rmdir; } if (de->inode != inode->i_ino) { retval = -ENOENT; goto end_rmdir; } if (inode->i_count > 1) { retval = -EBUSY; goto end_rmdir; } if (inode->i_nlink != 2) printk("empty directory has nlink!=2 (%u)\n", inode->i_nlink); de->inode = 0; #ifdef BLOAT_FS dir->i_version = ++event; #endif mark_buffer_dirty(bh, 1); inode->i_nlink = 0; inode->i_dirt = 1; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; dir->i_dirt = 1; retval = 0; end_rmdir: iput(dir); iput(inode); unmap_brelse(bh); return retval; }
int minix_lookup(register struct inode *dir, char *name, size_t len, register struct inode **result) { struct minix_dir_entry *de; struct buffer_head *bh; *result = NULL; if (dir) { if (S_ISDIR(dir->i_mode)) { debug("minix_lookup: Entering minix_find_entry\n"); bh = minix_find_entry(dir, name, len, &de); debug2("minix_lookup: minix_find_entry returned %x %d\n", bh, bh->b_mapcount); if (bh) { unmap_brelse(bh); *result = iget(dir->i_sb, (ino_t) de->inode); iput(dir); return (!*result) ? -EACCES : 0; } } iput(dir); } return -ENOENT; }
int minix_link(register struct inode *oldinode, register struct inode *dir, char *name, size_t len) { int error; struct buffer_head *bh; struct minix_dir_entry *de; error = -EPERM; if (S_ISDIR(oldinode->i_mode)) goto mlink_err; error = -EMLINK; if (oldinode->i_nlink >= MINIX_LINK_MAX) goto mlink_err; error = -EEXIST; bh = minix_find_entry(dir, name, len, &de); if (bh) { unmap_brelse(bh); goto mlink_err; } error = minix_add_entry(dir, name, len, oldinode->i_ino); if (!error) { oldinode->i_nlink++; oldinode->i_ctime = CURRENT_TIME; oldinode->i_dirt = 1; } mlink_err: iput(oldinode); iput(dir); return error; }
int elksfs_lookup(register struct inode *dir, char *name, size_t len, register struct inode **result) { struct buffer_head *bh; struct elksfs_dir_entry *de; ino_t ino; *result = NULL; if (!dir) return -ENOENT; if (!S_ISDIR(dir->i_mode)) { iput(dir); return -ENOENT; } debug("elksfs_lookup: Entering elksfs_find_entry\n"); bh = elksfs_find_entry(dir, name, len, &de); debug2("elksfs_lookup: elksfs_find_entry returned %x %d\n", bh, bh->b_mapcount); if (!bh) { iput(dir); return -ENOENT; } map_buffer(bh); ino = de->inode; unmap_brelse(bh); *result = iget(dir->i_sb, (ino_t) ino); if (!*result) { iput(dir); return -EACCES; } iput(dir); return 0; }
static int minix_readlink(register struct inode *inode, char *buffer, int buflen) { register struct buffer_head *bh; size_t len; if (!S_ISLNK(inode->i_mode)) { iput(inode); return -EINVAL; } bh = minix_bread(inode, 0, 0); iput(inode); if (!bh) return 0; map_buffer(bh); if((len = strlen(bh->b_data) + 1) > buflen) len = buflen; if (len > 1023) len = 1023; memcpy_tofs(buffer, bh->b_data, len); unmap_brelse(bh); return len; }
int minix_mknod(register struct inode *dir, char *name, size_t len, int mode, int rdev) { int error; register struct inode *inode; struct buffer_head *bh; struct minix_dir_entry *de; /* if (!dir) return -ENOENT;*/ /* Already checked by do_mknod() */ error = -EEXIST; bh = minix_find_entry(dir, name, len, &de); if (bh) { unmap_brelse(bh); goto mknod2; } error = -ENOSPC; inode = minix_new_inode(dir, (__u16)mode); if (!inode) goto mknod2; /*----------------------------------------------------------------------*/ if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = to_kdev_t(rdev); /*----------------------------------------------------------------------*/ error = minix_add_entry(dir, name, len, inode->i_ino); if (error) { inode->i_nlink--; inode->i_dirt = 1; } iput(inode); mknod2: iput(dir); return error; }
static int V1_trunc_indirect(register struct inode *inode, int offset, unsigned short *p) { struct buffer_head *bh; int i; unsigned short tmp; register struct buffer_head *ind_bh; unsigned short *ind; int retry = 0; tmp = *p; if (!tmp) return 0; ind_bh = bread(inode->i_dev, (block_t) tmp); if (tmp != *p) { brelse(ind_bh); return 1; } if (!ind_bh) { *p = 0; return 0; } map_buffer(ind_bh); repeat: for (i = INDIRECT_BLOCK(offset); i < 512; i++) { if (i < 0) i = 0; else if (i < INDIRECT_BLOCK(offset)) goto repeat; ind = i + (unsigned short *) ind_bh->b_data; tmp = *ind; if (!tmp) continue; bh = get_hash_table(inode->i_dev, (block_t) tmp); if (i < INDIRECT_BLOCK(offset)) { brelse(bh); goto repeat; } if ((bh && bh->b_count != 1) || tmp != *ind) { retry = 1; brelse(bh); continue; } *ind = 0; mark_buffer_dirty(ind_bh, 1); brelse(bh); minix_free_block(inode->i_sb, tmp); } ind = (unsigned short *) ind_bh->b_data; for (i = 0; i < 512; i++) if (*(ind++)) break; if (i >= 512) { if (ind_bh->b_count != 1) retry = 1; else { tmp = *p; *p = 0; minix_free_block(inode->i_sb, tmp); } } unmap_brelse(ind_bh); return retry; }
int minix_symlink(struct inode *dir, char *name, size_t len, char *symname) { int error; register struct inode *inode; register struct buffer_head *bh; struct minix_dir_entry *de; error = -EEXIST; bh = minix_find_entry(dir, name, len, &de); if (bh) { unmap_brelse(bh); goto symlink2; } error = -ENOSPC; inode = minix_new_inode(dir, S_IFLNK); if (!inode) goto symlink2; /*----------------------------------------------------------------------*/ bh = minix_bread(inode, 0, 1); if (!bh) goto symlink1; map_buffer(bh); if((error = strlen_fromfs(symname)) > 1023) error = 1023; memcpy_fromfs(bh->b_data, symname, error); bh->b_data[error] = 0; inode->i_size = (__u32) error; mark_buffer_dirty(bh, 1); unmap_brelse(bh); /*----------------------------------------------------------------------*/ error = minix_add_entry(dir, name, len, inode->i_ino); if (error) { symlink1: inode->i_nlink--; inode->i_dirt = 1; } iput(inode); symlink2: iput(dir); return error; }
static int minix_follow_link(register struct inode *dir, register struct inode *inode, int flag, int mode, struct inode **res_inode) { /* * FIXME: #1 Stack use is too high * #2 Needs to be current->link_count as this is blocking */ int error; struct buffer_head *bh; static int link_count = 0; __u16 ds, *pds; *res_inode = NULL; if (!dir) { dir = current->fs.root; dir->i_count++; } if (!inode) { iput(dir); return -ENOENT; } if (!S_ISLNK(inode->i_mode)) { iput(dir); *res_inode = inode; return 0; } if ( /* current-> */ link_count > 5) { iput(inode); iput(dir); return -ELOOP; } bh = minix_bread(inode, 0, 0); iput(inode); if (!bh) { iput(dir); return -EIO; } /* current-> */ link_count++; map_buffer(bh); pds = ¤t->t_regs.ds; ds = *pds; *pds = kernel_ds; error = open_namei(bh->b_data, flag, mode, res_inode, dir); *pds = ds; /* current-> */ link_count--; unmap_brelse(bh); return error; }
static int V1_trunc_dindirect(register struct inode *inode, int offset, unsigned short *p) { int i; unsigned short tmp; register struct buffer_head *dind_bh; unsigned short *dind; int retry = 0; if (!(tmp = *p)) return 0; dind_bh = bread(inode->i_dev, (block_t) tmp); if (tmp != *p) { brelse(dind_bh); return 1; } if (!dind_bh) { *p = 0; return 0; } map_buffer(dind_bh); repeat: for (i = DINDIRECT_BLOCK(offset); i < 512; i++) { if (i < 0) i = 0; if (i < DINDIRECT_BLOCK(offset)) goto repeat; dind = i + (unsigned short *) dind_bh->b_data; retry |= V1_trunc_indirect(inode, offset + (i << 9), dind); mark_buffer_dirty(dind_bh, 1); } dind = (unsigned short *) dind_bh->b_data; for (i = 0; i < 512; i++) if (*(dind++)) break; if (i >= 512) if (dind_bh->b_count != 1) retry = 1; else { tmp = *p; *p = 0; inode->i_dirt = 1; minix_free_block(inode->i_sb, tmp); } unmap_brelse(dind_bh); return retry; }
static int minix_add_entry(register struct inode *dir, char *name, size_t namelen, ino_t ino) { unsigned short block; loff_t offset; register struct buffer_head *bh; struct minix_dir_entry *de; struct minix_sb_info *info; if (!dir || !dir->i_sb) return -ENOENT; info = &dir->i_sb->u.minix_sb; if (namelen > info->s_namelen) { #ifdef NO_TRUNCATE return -ENAMETOOLONG; #else namelen = info->s_namelen; #endif } if (!namelen) return -ENOENT; bh = NULL; block = 0; offset = 0L; while (1) { if (!bh) { bh = minix_bread(dir, block, 1); if (!bh) return -ENOSPC; map_buffer(bh); } de = (struct minix_dir_entry *) (bh->b_data + offset); offset += info->s_dirsize; if (block * 1024L + offset > dir->i_size) { de->inode = 0; dir->i_size = block * 1024L + offset; dir->i_dirt = 1; } if (de->inode) { if (namecompare(namelen, info->s_namelen, name, de->name)) { debug2("MINIXadd_entry: file %t==%s (already exists)\n", name, de->name); unmap_brelse(bh); return -EEXIST; } } else { size_t i; dir->i_mtime = dir->i_ctime = CURRENT_TIME; dir->i_dirt = 1; memcpy_fromfs(de->name, name, namelen); if((i = info->s_namelen - namelen) > 0) memset(de->name + namelen, 0, i); #ifdef BLOAT_FS dir->i_version = ++event; #endif de->inode = ino; mark_buffer_dirty(bh, 1); unmap_brelse(bh); break; } if (offset >= BLOCK_SIZE) { unmap_brelse(bh); bh = NULL; offset = 0; block++; } } return 0; }
int elksfs_mkdir(register struct inode *dir, char *name, size_t len, int mode) { struct buffer_head *bh, *dir_block; register struct inode *inode; struct elksfs_dir_entry *de; struct elksfs_sb_info *info; int error; if (!dir || !dir->i_sb) { iput(dir); return -EINVAL; } info = &dir->i_sb->u.elksfs_sb; bh = elksfs_find_entry(dir, name, len, &de); if (bh) { brelse(bh); iput(dir); return -EEXIST; } #if 0 /* Above checks if bh is returned and exits, so bh * is NULL at this point */ map_buffer(bh); #endif if (dir->i_nlink >= ELKSFS_LINK_MAX) { iput(dir); return -EMLINK; } inode = elksfs_new_inode(dir); if (!inode) { iput(dir); return -ENOSPC; } debug("m_mkdir: new_inode succeeded\n"); inode->i_op = &elksfs_dir_inode_operations; inode->i_size = 2 * info->s_dirsize; debug("m_mkdir: starting elksfs_bread\n"); dir_block = elksfs_bread(inode, 0, 1); if (!dir_block) { iput(dir); inode->i_nlink--; inode->i_dirt = 1; iput(inode); return -ENOSPC; } debug("m_mkdir: read succeeded\n"); map_buffer(dir_block); de = (struct elksfs_dir_entry *) dir_block->b_data; de->inode = inode->i_ino; strcpy(de->name, "."); de = (struct elksfs_dir_entry *) (dir_block->b_data + info->s_dirsize); de->inode = dir->i_ino; strcpy(de->name, ".."); inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); unmap_brelse(dir_block); debug("m_mkdir: dir_block update succeeded\n"); inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs.umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; inode->i_dirt = 1; error = elksfs_add_entry(dir, name, len, &bh, &de); if (error) { iput(dir); inode->i_nlink = 0; iput(inode); return error; } map_buffer(bh); de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); dir->i_nlink++; dir->i_dirt = 1; iput(dir); iput(inode); unmap_brelse(bh); debug("m_mkdir: done!\n"); return 0; }
static int elksfs_add_entry(register struct inode *dir, char *name, size_t namelen, struct buffer_head **res_buf, struct elksfs_dir_entry **res_dir) { struct buffer_head *bh; struct elksfs_dir_entry *de; struct elksfs_sb_info *info; unsigned long int i; block_t block; loff_t offset; *res_buf = NULL; *res_dir = NULL; if (!dir || !dir->i_sb) return -ENOENT; info = &dir->i_sb->u.elksfs_sb; if (namelen > info->s_namelen) { #ifdef NO_TRUNCATE return -ENAMETOOLONG; #else namelen = info->s_namelen; #endif } if (!namelen) return -ENOENT; bh = NULL; block = 0; offset = 0; while (1) { if (!bh) { bh = elksfs_bread(dir, block, 1); if (!bh) return -ENOSPC; } map_buffer(bh); de = (struct elksfs_dir_entry *) (bh->b_data + offset); offset += info->s_dirsize; if (block * 1024 + offset > dir->i_size) { de->inode = 0; dir->i_size = block * 1024 + offset; dir->i_dirt = 1; } if (de->inode) { if (namecompare(namelen, (size_t) info->s_namelen, name, de->name)) { debug2("ELKSFSadd_entry: file %t==%s (already exists)\n", name, de->name); unmap_brelse(bh); return -EEXIST; } } else { dir->i_mtime = dir->i_ctime = CURRENT_TIME; dir->i_dirt = 1; for (i = 0; i < info->s_namelen; i++) de->name[i] = (i < namelen) ? (char) get_fs_byte((unsigned char *) name + i) : 0; #ifdef BLOAT_FS dir->i_version = ++event; #endif unmap_buffer(bh); mark_buffer_dirty(bh, 1); *res_dir = de; break; } if (offset < 1024) continue; printk("elksfs_add_entry may need another unmap_buffer :)\n"); brelse(bh); bh = NULL; offset = 0; block++; } *res_buf = bh; return 0; }
static int minix_add_entry(register struct inode *dir, char *name, size_t namelen, struct buffer_head **res_buf, struct minix_dir_entry **res_dir) { unsigned short block; loff_t offset; register struct buffer_head *bh; struct minix_dir_entry *de; struct minix_sb_info *info; *res_buf = NULL; *res_dir = NULL; if (!dir || !dir->i_sb) return -ENOENT; info = &dir->i_sb->u.minix_sb; if (namelen > info->s_namelen) { #ifdef NO_TRUNCATE return -ENAMETOOLONG; #else namelen = info->s_namelen; #endif } if (!namelen) return -ENOENT; bh = NULL; block = 0; offset = 0L; while (1) { if (!bh) { bh = minix_bread(dir, block, 1); if (!bh) return -ENOSPC; map_buffer(bh); } de = (struct minix_dir_entry *) (bh->b_data + offset); offset += info->s_dirsize; if (block * 1024L + offset > dir->i_size) { de->inode = 0; dir->i_size = block * 1024L + offset; dir->i_dirt = 1; } if (de->inode) { if (namecompare(namelen, info->s_namelen, name, de->name)) { debug2("MINIXadd_entry: file %t==%s (already exists)\n", name, de->name); unmap_brelse(bh); return -EEXIST; } } else { size_t i; dir->i_mtime = dir->i_ctime = CURRENT_TIME; dir->i_dirt = 1; for (i = 0; i < info->s_namelen; i++) de->name[i] = (i < namelen) ? (char) get_fs_byte(name + i) : '\0'; #ifdef BLOAT_FS dir->i_version = ++event; #endif unmap_buffer(bh); mark_buffer_dirty(bh, 1); *res_dir = de; break; } if (offset < 1024) continue; unmap_brelse(bh); bh = NULL; offset = 0; block++; } *res_buf = bh; return 0; }
int minix_mkdir(register struct inode *dir, char *name, size_t len, int mode) { int error; register struct inode *inode; struct buffer_head *dir_block; struct buffer_head *bh; struct minix_dir_entry *de; if (!dir || !dir->i_sb) { iput(dir); return -EINVAL; } bh = minix_find_entry(dir, name, len, &de); if (bh) { brelse(bh); iput(dir); return -EEXIST; } if (dir->i_nlink >= MINIX_LINK_MAX) { iput(dir); return -EMLINK; } inode = minix_new_inode(dir); if (!inode) { iput(dir); return -ENOSPC; } debug("m_mkdir: new_inode succeeded\n"); inode->i_op = &minix_dir_inode_operations; inode->i_size = 2 * dir->i_sb->u.minix_sb.s_dirsize; debug("m_mkdir: starting minix_bread\n"); dir_block = minix_bread(inode, 0, 1); if (!dir_block) { iput(dir); inode->i_nlink--; inode->i_dirt = 1; iput(inode); return -ENOSPC; } debug("m_mkdir: read succeeded\n"); map_buffer(dir_block); de = (struct minix_dir_entry *) dir_block->b_data; de->inode = inode->i_ino; strcpy(de->name, "."); de = (struct minix_dir_entry *) (dir_block->b_data + dir->i_sb->u.minix_sb.s_dirsize); de->inode = dir->i_ino; strcpy(de->name, ".."); inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); unmap_brelse(dir_block); debug("m_mkdir: dir_block update succeeded\n"); inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs.umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; inode->i_dirt = 1; error = minix_add_entry(dir, name, len, &bh, &de); if (error) { iput(dir); inode->i_nlink = 0; iput(inode); return error; } map_buffer(bh); de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); dir->i_nlink++; dir->i_dirt = 1; iput(dir); iput(inode); unmap_brelse(bh); debug("m_mkdir: done!\n"); return 0; }
static int blk_rw(struct inode *inode, register struct file *filp, char *buf, size_t count, int wr) { register struct buffer_head *bh; size_t chars, offset; int written = 0; while (count > 0) { /* * Offset to block/offset */ offset = ((size_t)filp->f_pos) & (BLOCK_SIZE - 1); chars = BLOCK_SIZE - offset; if (chars > count) chars = count; /* * Read the block in - use getblk on a write * of a whole block to avoid a read of the data. */ bh = getblk(inode->i_rdev, (block_t)(filp->f_pos >> BLOCK_SIZE_BITS)); if ((wr == BLOCK_READ) || (chars != BLOCK_SIZE)) { if (!readbuf(bh)) { if (!written) written = -EIO; break; } } map_buffer(bh); if (wr == BLOCK_WRITE) { /* * Alter buffer, mark dirty */ memcpy_fromfs(bh->b_data + offset, buf, chars); bh->b_uptodate = bh->b_dirty = 1; /* * Writing: queue physical I/O */ ll_rw_blk(WRITE, bh); wait_on_buffer(bh); if (!bh->b_uptodate) { /* Write error. */ unmap_brelse(bh); if (!written) written = -EIO; break; } } else { /* * Empty buffer data. Buffer unchanged */ memcpy_tofs(buf, bh->b_data + offset, chars); } /* * Move on and release buffer */ unmap_brelse(bh); buf += chars; filp->f_pos += chars; written += chars; count -= chars; } return written; }