/** * vfs_setpos_cookie - update the file offset for lseek and reset cookie * @file: file structure in question * @offset: file offset to seek to * @maxsize: maximum file size * @cookie: cookie to reset * * Update the file offset to the value specified by @offset if the given * offset is valid and it is not equal to the current file offset and * reset the specified cookie to indicate that a seek happened. * * Return the specified offset on success and -EINVAL on invalid offset.
*/ static loff_t vfs_setpos_cookie(struct file *file, loff_t offset,
loff_t maxsize, u64 *cookie)
{ if (offset < 0 && !unsigned_offsets(file)) return -EINVAL; if (offset > maxsize) return -EINVAL;
if (offset != file->f_pos) {
file->f_pos = offset; if (cookie)
*cookie = 0;
} return offset;
}
/** * vfs_setpos - update the file offset for lseek * @file: file structure in question * @offset: file offset to seek to * @maxsize: maximum file size * * This is a low-level filesystem helper for updating the file offset to * the value specified by @offset if the given offset is valid and it is * not equal to the current file offset. * * Return the specified offset on success and -EINVAL on invalid offset.
*/
loff_t vfs_setpos(struct file *file, loff_t offset, loff_t maxsize)
{ return vfs_setpos_cookie(file, offset, maxsize, NULL);
}
EXPORT_SYMBOL(vfs_setpos);
/** * must_set_pos - check whether f_pos has to be updated * @file: file to seek on * @offset: offset to use * @whence: type of seek operation * @eof: end of file * * Check whether f_pos needs to be updated and update @offset according * to @whence. * * Return: 0 if f_pos doesn't need to be updated, 1 if f_pos has to be * updated, and negative error code on failure.
*/ staticint must_set_pos(struct file *file, loff_t *offset, int whence, loff_t eof)
{ switch (whence) { case SEEK_END:
*offset += eof; break; case SEEK_CUR: /* * Here we special-case the lseek(fd, 0, SEEK_CUR) * position-querying operation. Avoid rewriting the "same" * f_pos value back to the file because a concurrent read(), * write() or lseek() might have altered it
*/ if (*offset == 0) {
*offset = file->f_pos; return 0;
} break; case SEEK_DATA: /* * In the generic case the entire file is data, so as long as * offset isn't at the end of the file then the offset is data.
*/ if ((unsignedlonglong)*offset >= eof) return -ENXIO; break; case SEEK_HOLE: /* * There is a virtual hole at the end of the file, so as long as * offset isn't i_size or larger, return i_size.
*/ if ((unsignedlonglong)*offset >= eof) return -ENXIO;
*offset = eof; break;
}
return 1;
}
/** * generic_file_llseek_size - generic llseek implementation for regular files * @file: file structure to seek on * @offset: file offset to seek to * @whence: type of seek * @maxsize: max size of this file in file system * @eof: offset used for SEEK_END position * * This is a variant of generic_file_llseek that allows passing in a custom * maximum file size and a custom EOF position, for e.g. hashed directories * * Synchronization: * SEEK_SET and SEEK_END are unsynchronized (but atomic on 64bit platforms) * SEEK_CUR is synchronized against other SEEK_CURs, but not read/writes. * read/writes behave like SEEK_SET against seeks.
*/
loff_t
generic_file_llseek_size(struct file *file, loff_t offset, int whence,
loff_t maxsize, loff_t eof)
{ int ret;
ret = must_set_pos(file, &offset, whence, eof); if (ret < 0) return ret; if (ret == 0) return offset;
if (whence == SEEK_CUR) { /* * If the file requires locking via f_pos_lock we know * that mutual exclusion for SEEK_CUR on the same file * is guaranteed. If the file isn't locked, we take * f_lock to protect against f_pos races with other * SEEK_CURs.
*/ if (file_seek_cur_needs_f_lock(file)) {
guard(spinlock)(&file->f_lock); return vfs_setpos(file, file->f_pos + offset, maxsize);
} return vfs_setpos(file, file->f_pos + offset, maxsize);
}
/** * generic_llseek_cookie - versioned llseek implementation * @file: file structure to seek on * @offset: file offset to seek to * @whence: type of seek * @cookie: cookie to update * * See generic_file_llseek for a general description and locking assumptions. * * In contrast to generic_file_llseek, this function also resets a * specified cookie to indicate a seek took place.
*/
loff_t generic_llseek_cookie(struct file *file, loff_t offset, int whence,
u64 *cookie)
{ struct inode *inode = file->f_mapping->host;
loff_t maxsize = inode->i_sb->s_maxbytes;
loff_t eof = i_size_read(inode); int ret;
if (WARN_ON_ONCE(!cookie)) return -EINVAL;
/* * Require that this is only used for directories that guarantee * synchronization between readdir and seek so that an update to * @cookie is correctly synchronized with concurrent readdir.
*/ if (WARN_ON_ONCE(!(file->f_mode & FMODE_ATOMIC_POS))) return -EINVAL;
ret = must_set_pos(file, &offset, whence, eof); if (ret < 0) return ret; if (ret == 0) return offset;
/* No need to hold f_lock because we know that f_pos_lock is held. */ if (whence == SEEK_CUR) return vfs_setpos_cookie(file, file->f_pos + offset, maxsize, cookie);
/** * generic_file_llseek - generic llseek implementation for regular files * @file: file structure to seek on * @offset: file offset to seek to * @whence: type of seek * * This is a generic implementation of ->llseek useable for all normal local * filesystems. It just updates the file offset to the value specified by * @offset and @whence.
*/
loff_t generic_file_llseek(struct file *file, loff_t offset, int whence)
{ struct inode *inode = file->f_mapping->host;
/** * fixed_size_llseek - llseek implementation for fixed-sized devices * @file: file structure to seek on * @offset: file offset to seek to * @whence: type of seek * @size: size of the file *
*/
loff_t fixed_size_llseek(struct file *file, loff_t offset, int whence, loff_t size)
{ switch (whence) { case SEEK_SET: case SEEK_CUR: case SEEK_END: return generic_file_llseek_size(file, offset, whence,
size, size); default: return -EINVAL;
}
}
EXPORT_SYMBOL(fixed_size_llseek);
/** * no_seek_end_llseek - llseek implementation for fixed-sized devices * @file: file structure to seek on * @offset: file offset to seek to * @whence: type of seek *
*/
loff_t no_seek_end_llseek(struct file *file, loff_t offset, int whence)
{ switch (whence) { case SEEK_SET: case SEEK_CUR: return generic_file_llseek_size(file, offset, whence,
OFFSET_MAX, 0); default: return -EINVAL;
}
}
EXPORT_SYMBOL(no_seek_end_llseek);
/** * no_seek_end_llseek_size - llseek implementation for fixed-sized devices * @file: file structure to seek on * @offset: file offset to seek to * @whence: type of seek * @size: maximal offset allowed *
*/
loff_t no_seek_end_llseek_size(struct file *file, loff_t offset, int whence, loff_t size)
{ switch (whence) { case SEEK_SET: case SEEK_CUR: return generic_file_llseek_size(file, offset, whence,
size, 0); default: return -EINVAL;
}
}
EXPORT_SYMBOL(no_seek_end_llseek_size);
/** * noop_llseek - No Operation Performed llseek implementation * @file: file structure to seek on * @offset: file offset to seek to * @whence: type of seek * * This is an implementation of ->llseek useable for the rare special case when * userspace expects the seek to succeed but the (device) file is actually not * able to perform the seek. In this case you use noop_llseek() instead of * falling back to the default implementation of ->llseek.
*/
loff_t noop_llseek(struct file *file, loff_t offset, int whence)
{ return file->f_pos;
}
EXPORT_SYMBOL(noop_llseek);
retval = inode_lock_killable(inode); if (retval) return retval; switch (whence) { case SEEK_END:
offset += i_size_read(inode); break; case SEEK_CUR: if (offset == 0) {
retval = file->f_pos; goto out;
}
offset += file->f_pos; break; case SEEK_DATA: /* * In the generic case the entire file is data, so as * long as offset isn't at the end of the file then the * offset is data.
*/ if (offset >= inode->i_size) {
retval = -ENXIO; goto out;
} break; case SEEK_HOLE: /* * There is a virtual hole at the end of the file, so * as long as offset isn't i_size or larger, return * i_size.
*/ if (offset >= inode->i_size) {
retval = -ENXIO; goto out;
}
offset = inode->i_size; break;
}
retval = -EINVAL; if (offset >= 0 || unsigned_offsets(file)) { if (offset != file->f_pos)
file->f_pos = offset;
retval = offset;
}
out:
inode_unlock(inode); return retval;
}
EXPORT_SYMBOL(default_llseek);
if (WARN_ON_ONCE(!(file->f_mode & FMODE_READ))) return -EINVAL; if (!(file->f_mode & FMODE_CAN_READ)) return -EINVAL; /* * Also fail if ->read_iter and ->read are both wired up as that * implies very convoluted semantics.
*/ if (unlikely(!file->f_op->read_iter || file->f_op->read)) return warn_unsupported(file, "read");
if (WARN_ON_ONCE(!(file->f_mode & FMODE_WRITE))) return -EBADF; if (!(file->f_mode & FMODE_CAN_WRITE)) return -EINVAL; /* * Also fail if ->write_iter and ->write are both wired up as that * implies very convoluted semantics.
*/ if (unlikely(!file->f_op->write_iter || file->f_op->write)) return warn_unsupported(file, "write");
/* caller is responsible for file_start_write/file_end_write */
ssize_t __kernel_write(struct file *file, constvoid *buf, size_t count, loff_t *pos)
{ struct kvec iov = {
.iov_base = (void *)buf,
.iov_len = min_t(size_t, count, MAX_RW_COUNT),
}; struct iov_iter iter;
iov_iter_kvec(&iter, ITER_SOURCE, &iov, 1, iov.iov_len); return __kernel_write_iter(file, &iter, pos);
} /* * This "EXPORT_SYMBOL_GPL()" is more of a "EXPORT_SYMBOL_DONTUSE()", * but autofs is one of the few internal kernel users that actually * wants this _and_ can be built as a module. So we need to export * this symbol for autofs, even though it really isn't appropriate * for any other kernel modules.
*/
EXPORT_SYMBOL_GPL(__kernel_write);
init_sync_kiocb(&kiocb, filp);
ret = kiocb_set_rw_flags(&kiocb, flags, type); if (ret) return ret;
kiocb.ki_pos = (ppos ? *ppos : 0);
if (type == READ)
ret = filp->f_op->read_iter(&kiocb, iter); else
ret = filp->f_op->write_iter(&kiocb, iter);
BUG_ON(ret == -EIOCBQUEUED); if (ppos)
*ppos = kiocb.ki_pos; return ret;
}
/* Do it by hand, with file-ops */ static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter,
loff_t *ppos, int type, rwf_t flags)
{
ssize_t ret = 0;
if (flags & ~RWF_HIPRI) return -EOPNOTSUPP;
while (iov_iter_count(iter)) {
ssize_t nr;
if (type == READ) {
nr = filp->f_op->read(filp, iter_iov_addr(iter),
iter_iov_len(iter), ppos);
} else {
nr = filp->f_op->write(filp, iter_iov_addr(iter),
iter_iov_len(iter), ppos);
}
if (nr < 0) { if (!ret)
ret = nr; break;
}
ret += nr; if (nr != iter_iov_len(iter)) break;
iov_iter_advance(iter, nr);
}
if (pos == -1) return do_writev(fd, vec, vlen, flags);
return do_pwritev(fd, vec, vlen, pos, flags);
}
/* * Various compat syscalls. Note that they all pretend to take a native * iovec - import_iovec will properly treat those as compat_iovecs based on * in_compat_syscall().
*/ #ifdef CONFIG_COMPAT #ifdef __ARCH_WANT_COMPAT_SYS_PREADV64
COMPAT_SYSCALL_DEFINE4(preadv64, unsignedlong, fd, conststruct iovec __user *, vec, unsignedlong, vlen, loff_t, pos)
{ return do_preadv(fd, vec, vlen, pos, 0);
} #endif
/* * Get input file, and verify that it is ok..
*/ CLASS(fd, in)(in_fd); if (fd_empty(in)) return -EBADF; if (!(fd_file(in)->f_mode & FMODE_READ)) return -EBADF; if (!ppos) {
pos = fd_file(in)->f_pos;
} else {
pos = *ppos; if (!(fd_file(in)->f_mode & FMODE_PREAD)) return -ESPIPE;
}
retval = rw_verify_area(READ, fd_file(in), &pos, count); if (retval < 0) return retval; if (count > MAX_RW_COUNT)
count = MAX_RW_COUNT;
/* * Get output file, and verify that it is ok..
*/ CLASS(fd, out)(out_fd); if (fd_empty(out)) return -EBADF; if (!(fd_file(out)->f_mode & FMODE_WRITE)) return -EBADF;
in_inode = file_inode(fd_file(in));
out_inode = file_inode(fd_file(out));
out_pos = fd_file(out)->f_pos;
if (!max)
max = min(in_inode->i_sb->s_maxbytes, out_inode->i_sb->s_maxbytes);
if (unlikely(pos + count > max)) { if (pos >= max) return -EOVERFLOW;
count = max - pos;
}
fl = 0; #if 0 /* * We need to debate whether we can enable this or not. The * man page documents EAGAIN return for the output at least, * and the application is arguably buggy if it doesn't expect * EAGAIN on a non-blocking file descriptor.
*/ if (fd_file(in)->f_flags & O_NONBLOCK)
fl = SPLICE_F_NONBLOCK; #endif
opipe = get_pipe_info(fd_file(out), true); if (!opipe) {
retval = rw_verify_area(WRITE, fd_file(out), &out_pos, count); if (retval < 0) return retval;
retval = do_splice_direct(fd_file(in), &pos, fd_file(out), &out_pos,
count, fl);
} else { if (fd_file(out)->f_flags & O_NONBLOCK)
fl |= SPLICE_F_NONBLOCK;
/* * Performs necessary checks before doing a file copy * * Can adjust amount of bytes to copy via @req_count argument. * Returns appropriate error code that caller should return or * zero in case the copy should be allowed.
*/ staticint generic_copy_file_checks(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out,
size_t *req_count, unsignedint flags)
{ struct inode *inode_in = file_inode(file_in); struct inode *inode_out = file_inode(file_out);
uint64_t count = *req_count;
loff_t size_in; int ret;
ret = generic_file_rw_checks(file_in, file_out); if (ret) return ret;
/* * We allow some filesystems to handle cross sb copy, but passing * a file of the wrong filesystem type to filesystem driver can result * in an attempt to dereference the wrong type of ->private_data, so * avoid doing that until we really have a good reason. * * nfs and cifs define several different file_system_type structures * and several different sets of file_operations, but they all end up * using the same ->copy_file_range() function pointer.
*/ if (flags & COPY_FILE_SPLICE) { /* cross sb splice is allowed */
} elseif (file_out->f_op->copy_file_range) { if (file_in->f_op->copy_file_range !=
file_out->f_op->copy_file_range) return -EXDEV;
} elseif (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) { return -EXDEV;
}
/* Don't touch certain kinds of inodes */ if (IS_IMMUTABLE(inode_out)) return -EPERM;
if (IS_SWAPFILE(inode_in) || IS_SWAPFILE(inode_out)) return -ETXTBSY;
/* Shorten the copy to EOF */
size_in = i_size_read(inode_in); if (pos_in >= size_in)
count = 0; else
count = min(count, size_in - (uint64_t)pos_in);
ret = generic_write_check_limits(file_out, pos_out, &count); if (ret) return ret;
/* Don't allow overlapped copying within the same file. */ if (inode_in == inode_out &&
pos_out + count > pos_in &&
pos_out < pos_in + count) return -EINVAL;
*req_count = count; return 0;
}
/* * copy_file_range() differs from regular file read and write in that it * specifically allows return partial success. When it does so is up to * the copy_file_range method.
*/
ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out,
size_t len, unsignedint flags)
{
ssize_t ret; bool splice = flags & COPY_FILE_SPLICE; bool samesb = file_inode(file_in)->i_sb == file_inode(file_out)->i_sb;
if (flags & ~COPY_FILE_SPLICE) return -EINVAL;
ret = generic_copy_file_checks(file_in, pos_in, file_out, pos_out, &len,
flags); if (unlikely(ret)) return ret;
ret = rw_verify_area(READ, file_in, &pos_in, len); if (unlikely(ret)) return ret;
ret = rw_verify_area(WRITE, file_out, &pos_out, len); if (unlikely(ret)) return ret;
if (len == 0) return 0;
/* * Make sure return value doesn't overflow in 32bit compat mode. Also * limit the size for all cases except when calling ->copy_file_range().
*/ if (splice || !file_out->f_op->copy_file_range || in_compat_syscall())
len = min_t(size_t, MAX_RW_COUNT, len);
file_start_write(file_out);
/* * Cloning is supported by more file systems, so we implement copy on * same sb using clone, but for filesystems where both clone and copy * are supported (e.g. nfs,cifs), we only call the copy method.
*/ if (!splice && file_out->f_op->copy_file_range) {
ret = file_out->f_op->copy_file_range(file_in, pos_in,
file_out, pos_out,
len, flags);
} elseif (!splice && file_in->f_op->remap_file_range && samesb) {
ret = file_in->f_op->remap_file_range(file_in, pos_in,
file_out, pos_out, len, REMAP_FILE_CAN_SHORTEN); /* fallback to splice */ if (ret <= 0)
splice = true;
} elseif (samesb) { /* Fallback to splice for same sb copy for backward compat */
splice = true;
}
file_end_write(file_out);
if (!splice) goto done;
/* * We can get here for same sb copy of filesystems that do not implement * ->copy_file_range() in case filesystem does not support clone or in * case filesystem supports clone but rejected the clone request (e.g. * because it was not block aligned). * * In both cases, fall back to kernel copy so we are able to maintain a * consistent story about which filesystems support copy_file_range() * and which filesystems do not, that will allow userspace tools to * make consistent desicions w.r.t using copy_file_range(). * * We also get here if caller (e.g. nfsd) requested COPY_FILE_SPLICE * for server-side-copy between any two sb. * * In any case, we call do_splice_direct() and not splice_file_range(), * without file_start_write() held, to avoid possible deadlocks related * to splicing from input file, while file_start_write() is held on * the output file on a different sb.
*/
ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out, len, 0);
done: if (ret > 0) {
fsnotify_access(file_in);
add_rchar(current, ret);
fsnotify_modify(file_out);
add_wchar(current, ret);
}
CLASS(fd, f_in)(fd_in); if (fd_empty(f_in)) return -EBADF;
CLASS(fd, f_out)(fd_out); if (fd_empty(f_out)) return -EBADF;
if (off_in) { if (copy_from_user(&pos_in, off_in, sizeof(loff_t))) return -EFAULT;
} else {
pos_in = fd_file(f_in)->f_pos;
}
if (off_out) { if (copy_from_user(&pos_out, off_out, sizeof(loff_t))) return -EFAULT;
} else {
pos_out = fd_file(f_out)->f_pos;
}
if (flags != 0) return -EINVAL;
ret = vfs_copy_file_range(fd_file(f_in), pos_in, fd_file(f_out), pos_out, len,
flags); if (ret > 0) {
pos_in += ret;
pos_out += ret;
if (off_in) { if (copy_to_user(off_in, &pos_in, sizeof(loff_t)))
ret = -EFAULT;
} else {
fd_file(f_in)->f_pos = pos_in;
}
if (off_out) { if (copy_to_user(off_out, &pos_out, sizeof(loff_t)))
ret = -EFAULT;
} else {
fd_file(f_out)->f_pos = pos_out;
}
} return ret;
}
/* * Don't operate on ranges the page cache doesn't support, and don't exceed the * LFS limits. If pos is under the limit it becomes a short access. If it * exceeds the limit we return -EFBIG.
*/ int generic_write_check_limits(struct file *file, loff_t pos, loff_t *count)
{ struct inode *inode = file->f_mapping->host;
loff_t max_size = inode->i_sb->s_maxbytes;
loff_t limit = rlimit(RLIMIT_FSIZE);
/* * Performs necessary checks before doing a write * * Can adjust writing position or amount of bytes to write. * Returns appropriate error code that caller should return or * zero in case that write should be allowed.
*/
ssize_t generic_write_checks(struct kiocb *iocb, struct iov_iter *from)
{
loff_t count = iov_iter_count(from); int ret;
ret = generic_write_checks_count(iocb, &count); if (ret) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.