static int ext2_readlink (struct inode * inode, char * buffer, int buflen) { struct buffer_head * bh = NULL; char * link; int i, err; char c; if (!S_ISLNK(inode->i_mode)) { iput (inode); return -EINVAL; } if (buflen > inode->i_sb->s_blocksize - 1) buflen = inode->i_sb->s_blocksize - 1; if (inode->i_blocks) { bh = ext2_bread (inode, 0, 0, &err); if (!bh) { iput (inode); return 0; } link = bh->b_data; } else link = (char *) inode->u.ext2_i.i_data; i = 0; while (i < buflen && (c = link[i])) { i ++; put_fs_byte (c, buffer++); } iput (inode); if (bh) brelse (bh); return i; }
static int ext2_follow_link(struct inode * dir, struct inode * inode, int flag, int mode, struct inode ** res_inode) { int error; struct buffer_head * bh = NULL; char * link; *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 (dir); iput (inode); return -ELOOP; } if (inode->i_blocks) { if (!(bh = ext2_bread (inode, 0, 0, &error))) { iput (dir); iput (inode); return -EIO; } link = bh->b_data; } else link = (char *) inode->u.ext2_i.i_data; UPDATE_ATIME(inode); current->link_count++; error = open_namei (link, flag, mode, res_inode, dir); current->link_count--; iput (inode); if (bh) brelse (bh); return error; }
static int ext2_readlink (struct inode * inode, char * buffer, int buflen) { struct buffer_head * bh = NULL; char * link; int i, err; char c; if (!S_ISLNK(inode->i_mode)) { iput (inode); return -EINVAL; } if (buflen > inode->i_sb->s_blocksize - 1) buflen = inode->i_sb->s_blocksize - 1; if (inode->i_blocks) { bh = ext2_bread (inode, 0, 0, &err); if (!bh) { iput (inode); return 0; } link = bh->b_data; } else link = (char *) inode->u.ext2_i.i_data; i = 0; #ifdef CONFIG_OSFMACH3 while (i < buflen && (c = link[i])) i++; memcpy_tofs(buffer, link, i); buffer += i; #else /* CONFIG_OSFMACH3 */ while (i < buflen && (c = link[i])) { i++; put_user (c, buffer++); } #endif /* CONFIG_OSFMACH3 */ UPDATE_ATIME(inode); iput (inode); if (bh) brelse (bh); return i; }
static int ext2_readdir (struct inode * inode, struct file * filp, struct dirent * dirent, int count) { unsigned long offset, blk; int i, num; struct buffer_head * bh, * tmp, * bha[16]; struct ext2_dir_entry * de; struct super_block * sb; int err; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; sb = inode->i_sb; while (filp->f_pos < inode->i_size) { offset = filp->f_pos & (sb->s_blocksize - 1); blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb); bh = ext2_bread (inode, blk, 0, &err); if (!bh) { filp->f_pos += sb->s_blocksize - offset; continue; } /* * Do the readahead */ if (!offset) { for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0; i > 0; i--) { tmp = ext2_getblk (inode, ++blk, 0, &err); if (tmp && !tmp->b_uptodate && !tmp->b_lock) bha[num++] = tmp; else brelse (tmp); } if (num) { ll_rw_block (READA, num, bha); for (i = 0; i < num; i++) brelse (bha[i]); } } de = (struct ext2_dir_entry *) (offset + bh->b_data); while (offset < sb->s_blocksize && filp->f_pos < inode->i_size) { if (!ext2_check_dir_entry ("ext2_readdir", inode, de, bh, offset)) { brelse (bh); return 0; } offset += de->rec_len; filp->f_pos += de->rec_len; if (de->inode) { memcpy_tofs (dirent->d_name, de->name, de->name_len); put_fs_long (de->inode, &dirent->d_ino); put_fs_byte (0, de->name_len + dirent->d_name); put_fs_word (de->name_len, &dirent->d_reclen); dcache_add(inode, de->name, de->name_len, de->inode); i = de->name_len; brelse (bh); if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } return i; } de = (struct ext2_dir_entry *) ((char *) de + de->rec_len); } brelse (bh); } if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } return 0; }
static int ext2_readdir(struct file * filp, void * dirent, filldir_t filldir) { int error = 0; unsigned long offset, blk; int i, num, stored; struct buffer_head * bh, * tmp, * bha[16]; struct ext2_dir_entry_2 * de; struct super_block * sb; int err; struct inode *inode = filp->f_dentry->d_inode; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; sb = inode->i_sb; stored = 0; bh = NULL; offset = filp->f_pos & (sb->s_blocksize - 1); while (!error && !stored && filp->f_pos < inode->i_size) { blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb); bh = ext2_bread (inode, blk, 0, &err); if (!bh) { ext2_error (sb, "ext2_readdir", "directory #%lu contains a hole at offset %lu", inode->i_ino, (unsigned long)filp->f_pos); filp->f_pos += sb->s_blocksize - offset; continue; } /* * Do the readahead */ if (!offset) { for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0; i > 0; i--) { tmp = ext2_getblk (inode, ++blk, 0, &err); if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) bha[num++] = tmp; else brelse (tmp); } if (num) { ll_rw_block (READA, num, bha); for (i = 0; i < num; i++) brelse (bha[i]); } } revalidate: /* If the dir block has changed since the last call to * readdir(2), then we might be pointing to an invalid * dirent right now. Scan from the start of the block * to make sure. */ if (filp->f_version != inode->i_version) { for (i = 0; i < sb->s_blocksize && i < offset; ) { de = (struct ext2_dir_entry_2 *) (bh->b_data + i); /* It's too expensive to do a full * dirent test each time round this * loop, but we do have to test at * least that it is non-zero. A * failure will be detected in the * dirent test below. */ if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(1)) break; i += le16_to_cpu(de->rec_len); } offset = i; filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) | offset; filp->f_version = inode->i_version; } while (!error && filp->f_pos < inode->i_size && offset < sb->s_blocksize) { de = (struct ext2_dir_entry_2 *) (bh->b_data + offset); if (!ext2_check_dir_entry ("ext2_readdir", inode, de, bh, offset)) { /* On error, skip the f_pos to the next block. */ filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + sb->s_blocksize; brelse (bh); return stored; } offset += le16_to_cpu(de->rec_len); if (le32_to_cpu(de->inode)) { /* We might block in the next section * if the data destination is * currently swapped out. So, use a * version stamp to detect whether or * not the directory has been modified * during the copy operation. */ unsigned long version = inode->i_version; error = filldir(dirent, de->name, de->name_len, filp->f_pos, le32_to_cpu(de->inode)); if (error) break; if (version != inode->i_version) goto revalidate; stored ++; } filp->f_pos += le16_to_cpu(de->rec_len); } offset = 0; brelse (bh); } UPDATE_ATIME(inode); return 0; }
static int ext2_readdir (struct inode * inode, struct file * filp, struct dirent * dirent, int count) { unsigned long offset, blk; int i, num, stored, dlen; struct buffer_head * bh, * tmp, * bha[16]; struct ext2_dir_entry * de; struct super_block * sb; int err, version; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; sb = inode->i_sb; stored = 0; bh = NULL; offset = filp->f_pos & (sb->s_blocksize - 1); while (count > 0 && !stored && filp->f_pos < inode->i_size) { blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb); bh = ext2_bread (inode, blk, 0, &err); if (!bh) { filp->f_pos += sb->s_blocksize - offset; continue; } /* * Do the readahead */ if (!offset) { for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0; i > 0; i--) { tmp = ext2_getblk (inode, ++blk, 0, &err); if (tmp && !tmp->b_uptodate && !tmp->b_lock) bha[num++] = tmp; else brelse (tmp); } if (num) { ll_rw_block (READA, num, bha); for (i = 0; i < num; i++) brelse (bha[i]); } } revalidate: /* If the dir block has changed since the last call to * readdir(2), then we might be pointing to an invalid * dirent right now. Scan from the start of the block * to make sure. */ if (filp->f_version != inode->i_version) { for (i = 0; i < sb->s_blocksize && i < offset; ) { de = (struct ext2_dir_entry *) (bh->b_data + i); /* It's too expensive to do a full * dirent test each time round this * loop, but we do have to test at * least that it is non-zero. A * failure will be detected in the * dirent test below. */ if (de->rec_len < EXT2_DIR_REC_LEN(1)) break; i += de->rec_len; } offset = i; filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) | offset; filp->f_version = inode->i_version; } while (count > 0 && filp->f_pos < inode->i_size && offset < sb->s_blocksize) { de = (struct ext2_dir_entry *) (bh->b_data + offset); if (!ext2_check_dir_entry ("ext2_readdir", inode, de, bh, offset)) { /* On error, skip the f_pos to the next block. */ filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + sb->s_blocksize; brelse (bh); return stored; } if (de->inode) { dlen = ROUND_UP(NAME_OFFSET(dirent) + de->name_len + 1); /* Old libc libraries always use a count of 1. */ if (count == 1 && !stored) count = dlen; if (count < dlen) { count = 0; break; } /* We might block in the next section * if the data destination is * currently swapped out. So, use a * version stamp to detect whether or * not the directory has been modified * during the copy operation. */ version = inode->i_version; i = de->name_len; memcpy_tofs (dirent->d_name, de->name, i); put_fs_long (de->inode, &dirent->d_ino); put_fs_byte (0, dirent->d_name + i); put_fs_word (i, &dirent->d_reclen); put_fs_long (dlen, &dirent->d_off); if (version != inode->i_version) goto revalidate; dcache_add(inode, de->name, de->name_len, de->inode); stored += dlen; count -= dlen; ((char *) dirent) += dlen; } offset += de->rec_len; filp->f_pos += de->rec_len; } offset = 0; brelse (bh); } if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } return stored; }