ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { struct inode *inode = file->f_dentry->d_inode; ssize_t ret; if (!(file->f_mode & FMODE_READ)) return -EBADF; if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read)) return -EINVAL; ret = locks_verify_area(FLOCK_VERIFY_READ, inode, file, *pos, count); if (!ret) { ret = security_file_permission (file, MAY_READ); if (!ret) { if (file->f_op->read) ret = file->f_op->read(file, buf, count, pos); else ret = do_sync_read(file, buf, count, pos); if (ret > 0) dnotify_parent(file->f_dentry, DN_ACCESS); } } return ret; }
/** * sysfs_update_file - update the modified timestamp on an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. * * Also call dnotify for the dentry, which lots of userspace programs * use. */ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) { struct dentry * dir = kobj->dentry; struct dentry * victim; int res = -ENOENT; down(&dir->d_inode->i_sem); victim = sysfs_get_dentry(dir, attr->name); if (!IS_ERR(victim)) { /* make sure dentry is really there */ if (victim->d_inode && (victim->d_parent->d_inode == dir->d_inode)) { victim->d_inode->i_mtime = CURRENT_TIME; dnotify_parent(victim, DN_MODIFY); /** * Drop reference from initial sysfs_get_dentry(). */ dput(victim); res = 0; } /** * Drop the reference acquired from sysfs_get_dentry() above. */ dput(victim); } up(&dir->d_inode->i_sem); return res; }
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { struct inode *inode = file->f_dentry->d_inode; ssize_t ret; if (!(file->f_mode & FMODE_WRITE)) return -EBADF; if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write)) return -EINVAL; ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, *pos, count); if (!ret) { ret = security_file_permission (file, MAY_WRITE); if (!ret) { if (file->f_op->write) ret = file->f_op->write(file, buf, count, pos); else ret = do_sync_write(file, buf, count, pos); if (ret > 0) dnotify_parent(file->f_dentry, DN_MODIFY); } } return ret; }
asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, size_t count, loff_t pos) { ssize_t ret; struct file * file; ssize_t (*read)(struct file *, char *, size_t, loff_t *); ret = -EBADF; file = fget(fd); if (!file) goto bad_file; if (!(file->f_mode & FMODE_READ)) goto out; ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode, file, pos, count); if (ret) goto out; ret = -EINVAL; if (!file->f_op || !(read = file->f_op->read)) goto out; if (pos < 0) goto out; ret = read(file, buf, count, &pos); if (ret > 0) dnotify_parent(file->f_dentry, DN_ACCESS); out: fput(file); bad_file: return ret; }
asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, size_t count, loff_t pos) { ssize_t ret; struct file * file; ssize_t (*write)(struct file *, const char *, size_t, loff_t *); ret = -EBADF; file = fget(fd); if (!file) goto bad_file; if (!(file->f_mode & FMODE_WRITE)) goto out; ret = locks_verify_area(FLOCK_VERIFY_WRITE, file->f_dentry->d_inode, file, pos, count); if (ret) goto out; ret = -EINVAL; if (!file->f_op || !(write = file->f_op->write)) goto out; if (pos < 0) goto out; ret = write(file, buf, count, &pos); if (ret > 0) dnotify_parent(file->f_dentry, DN_MODIFY); out: fput(file); bad_file: return ret; }
asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count) { ssize_t ret; struct file * file; ret = -EBADF; file = fget(fd); if (file) { if (file->f_mode & FMODE_WRITE) { struct inode *inode = file->f_dentry->d_inode; ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, file->f_pos, count); if (!ret) { ssize_t (*write)(struct file *, const char *, size_t, loff_t *); ret = -EINVAL; if (file->f_op && (write = file->f_op->write) != NULL) ret = write(file, buf, count, &file->f_pos); } } if (ret > 0) dnotify_parent(file->f_dentry, DN_MODIFY); fput(file); } return ret; }
/* * Flush all pending data to disk. This operation will block. */ static int alq_doio(struct alq *alq) { mm_segment_t oldfs; struct iovec aiov[2]; struct file *fp; struct ale *ale; struct ale *alstart; int totlen; int iov; int err; fp = alq->aq_fp; totlen = 0; iov = 0; alstart = ale = alq->aq_entvalid; alq->aq_entvalid = NULL; memset(&aiov, 0, sizeof(aiov)); do { if (aiov[iov].iov_base == NULL) aiov[iov].iov_base = ale->ae_data; aiov[iov].iov_len += alq->aq_entlen; totlen += alq->aq_entlen; /* Check to see if we're wrapping the buffer */ if (ale->ae_data + alq->aq_entlen != ale->ae_next->ae_data) iov++; ale->ae_flags &= ~AE_VALID; ale = ale->ae_next; } while (ale->ae_flags & AE_VALID); alq->aq_flags |= AQ_FLUSHING; spin_unlock_irq(&alq->aq_lock); if (iov == 2 || aiov[iov].iov_base == NULL) iov--; oldfs = get_fs(); set_fs(KERNEL_DS); /* XXX ignore errors */ err = fp->f_op->writev(fp, aiov, iov + 1, &fp->f_pos); set_fs(oldfs); if (err >= 0) dnotify_parent(fp->f_dentry, DN_MODIFY); spin_lock_irq(&alq->aq_lock); alq->aq_flags &= ~AQ_FLUSHING; if (alq->aq_entfree == NULL) alq->aq_entfree = alstart; if (alq->aq_flags & AQ_WANTED) { alq->aq_flags &= ~AQ_WANTED; return (1); } return(0); }
int notify_change(struct dentry * dentry, struct iattr * attr) { struct inode *inode = dentry->d_inode; int error; time_t now = CURRENT_TIME; unsigned int ia_valid = attr->ia_valid; if (!inode) BUG(); attr->ia_ctime = now; if (!(ia_valid & ATTR_ATIME_SET)) attr->ia_atime = now; if (!(ia_valid & ATTR_MTIME_SET)) attr->ia_mtime = now; lock_kernel(); if (inode->i_op && inode->i_op->setattr) error = inode->i_op->setattr(dentry, attr); else { error = inode_change_ok(inode, attr); if (!error) { if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; if (!error) error = inode_setattr(inode, attr); } } unlock_kernel(); if (!error) { unsigned long dn_mask = setattr_mask(ia_valid); if (dn_mask) dnotify_parent(dentry, dn_mask); } return error; }
asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count) { ssize_t ret; struct file * file; ret = -EBADF; file = fget(fd); if (file) { if (file->f_mode & FMODE_READ) { ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode, file, file->f_pos, count); if (!ret) { ssize_t (*read)(struct file *, char *, size_t, loff_t *); ret = -EINVAL; if (file->f_op && (read = file->f_op->read) != NULL) ret = read(file, buf, count, &file->f_pos); } } if (ret > 0) dnotify_parent(file->f_dentry, DN_ACCESS); fput(file); } return ret; }
static ssize_t do_readv_writev(int type, struct file *file, const struct iovec __user * uvector, unsigned long nr_segs, loff_t *pos) { typedef ssize_t (*io_fn_t)(struct file *, char __user *, size_t, loff_t *); typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *); size_t tot_len; struct iovec iovstack[UIO_FASTIOV]; struct iovec *iov=iovstack, *vector; ssize_t ret; int seg; io_fn_t fn; iov_fn_t fnv; struct inode *inode; /* * SuS says "The readv() function *may* fail if the iovcnt argument * was less than or equal to 0, or greater than {IOV_MAX}. Linux has * traditionally returned zero for zero segments, so... */ ret = 0; if (nr_segs == 0) goto out; /* * First get the "struct iovec" from user memory and * verify all the pointers */ ret = -EINVAL; if ((nr_segs > UIO_MAXIOV) || (nr_segs <= 0)) goto out; if (!file->f_op) goto out; if (nr_segs > UIO_FASTIOV) { ret = -ENOMEM; iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL); if (!iov) goto out; } ret = -EFAULT; if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) goto out; /* * Single unix specification: * We should -EINVAL if an element length is not >= 0 and fitting an * ssize_t. The total length is fitting an ssize_t * * Be careful here because iov_len is a size_t not an ssize_t */ tot_len = 0; ret = -EINVAL; for (seg = 0; seg < nr_segs; seg++) { ssize_t len = (ssize_t)iov[seg].iov_len; if (len < 0) /* size_t not fitting an ssize_t .. */ goto out; tot_len += len; if ((ssize_t)tot_len < 0) /* maths overflow on the ssize_t */ goto out; } if (tot_len == 0) { ret = 0; goto out; } inode = file->f_dentry->d_inode; /* VERIFY_WRITE actually means a read, as we write to user space */ ret = locks_verify_area((type == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), inode, file, *pos, tot_len); if (ret) goto out; fnv = NULL; if (type == READ) { fn = file->f_op->read; fnv = file->f_op->readv; } else { fn = (io_fn_t)file->f_op->write; fnv = file->f_op->writev; } if (fnv) { ret = fnv(file, iov, nr_segs, pos); goto out; } /* Do it by hand, with file-ops */ ret = 0; vector = iov; while (nr_segs > 0) { void __user * base; size_t len; ssize_t nr; base = vector->iov_base; len = vector->iov_len; vector++; nr_segs--; nr = fn(file, base, len, pos); if (nr < 0) { if (!ret) ret = nr; break; } ret += nr; if (nr != len) break; } out: if (iov != iovstack) kfree(iov); if ((ret + (type == READ)) > 0) dnotify_parent(file->f_dentry, (type == READ) ? DN_ACCESS : DN_MODIFY); return ret; }
static ssize_t do_readv_writev(int type, struct file *file, const struct iovec * vector, unsigned long count) { typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *); size_t tot_len; struct iovec iovstack[UIO_FASTIOV]; struct iovec *iov=iovstack; ssize_t ret, i; iov_fn_t fnv; struct inode *inode; /* * First get the "struct iovec" from user memory and * verify all the pointers */ ret = 0; if (!count) goto out_nofree; ret = -EINVAL; if (count > UIO_MAXIOV) goto out_nofree; if (!file->f_op) goto out_nofree; if (count > UIO_FASTIOV) { ret = -ENOMEM; iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); if (!iov) goto out_nofree; } ret = -EFAULT; if (copy_from_user(iov, vector, count*sizeof(*vector))) goto out; /* * Single unix specification: * We should -EINVAL if an element length is not >= 0 and fitting an ssize_t * The total length is fitting an ssize_t * * Be careful here because iov_len is a size_t not an ssize_t */ tot_len = 0; ret = -EINVAL; for (i = 0 ; i < count ; i++) { ssize_t len = (ssize_t) iov[i].iov_len; if (len < 0) /* size_t not fitting an ssize_t .. */ goto out; tot_len += len; /* We must do this work unsigned - signed overflow is undefined and gcc 3.2 now uses that fact sometimes... FIXME: put in a proper limits.h for each platform */ #if BITS_PER_LONG==64 if (tot_len > 0x7FFFFFFFFFFFFFFFUL) #else if (tot_len > 0x7FFFFFFFUL) #endif goto out; } inode = file->f_dentry->d_inode; /* VERIFY_WRITE actually means a read, as we write to user space */ ret = locks_verify_area((type == VERIFY_WRITE ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), inode, file, file->f_pos, tot_len); if (ret) goto out; fnv = (type == VERIFY_WRITE ? file->f_op->readv : file->f_op->writev); if (fnv) { ret = fnv(file, iov, count, &file->f_pos); goto out; } ret = fallback_readv_writev(type == VERIFY_WRITE ? READ : WRITE, file, iov, count, &file->f_pos); out: if (iov != iovstack) kfree(iov); out_nofree: /* VERIFY_WRITE actually means a read, as we write to user space */ if ((ret + (type == VERIFY_WRITE)) > 0) dnotify_parent(file->f_dentry, (type == VERIFY_WRITE) ? DN_ACCESS : DN_MODIFY); return ret; }
int notify_change(struct dentry * dentry, struct iattr * attr) { struct inode *inode = dentry->d_inode; mode_t mode = inode->i_mode; int error; struct timespec now = CURRENT_TIME; unsigned int ia_valid = attr->ia_valid; if (!inode) BUG(); attr->ia_ctime = now; if (!(ia_valid & ATTR_ATIME_SET)) attr->ia_atime = now; if (!(ia_valid & ATTR_MTIME_SET)) attr->ia_mtime = now; if (ia_valid & ATTR_KILL_SUID) { attr->ia_valid &= ~ATTR_KILL_SUID; if (mode & S_ISUID) { if (!(ia_valid & ATTR_MODE)) { ia_valid = attr->ia_valid |= ATTR_MODE; attr->ia_mode = inode->i_mode; } attr->ia_mode &= ~S_ISUID; } } if (ia_valid & ATTR_KILL_SGID) { attr->ia_valid &= ~ ATTR_KILL_SGID; if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { if (!(ia_valid & ATTR_MODE)) { ia_valid = attr->ia_valid |= ATTR_MODE; attr->ia_mode = inode->i_mode; } attr->ia_mode &= ~S_ISGID; } } if (!attr->ia_valid) return 0; if (inode->i_op && inode->i_op->setattr) { error = security_inode_setattr(dentry, attr); if (!error) error = inode->i_op->setattr(dentry, attr); } else { error = inode_change_ok(inode, attr); if (!error) error = security_inode_setattr(dentry, attr); if (!error) { if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; if (!error) error = inode_setattr(inode, attr); } } if (!error) { #ifdef CONFIG_MOT_FEAT_INOTIFY fsnotify_change(dentry, ia_valid); #else unsigned long dn_mask = setattr_mask(ia_valid); if (dn_mask) dnotify_parent(dentry, dn_mask); #endif } return error; }