/*
* linux/fs/read_write.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/mm.h>
#include <linux/uio.h>
#include <asm/segment.h>
asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
/* KOMENTOWAL: ADAM KIEZUN
/* fd - deskryptor pliku, na którym wykonujemy operacje */
/* offset - przesuniecie, jakie zostanie dokonane */
/* origin - liczba ze zbioru 0, 1, 2 o nastepujacym znaczeniu: */
/* 0 - przesuniecie bedzie dokanonane wzgledem poczatku pliku */
/* 1 - wzgledem biezacej pozycji w pliku */
/* 2 - wzgledem konca pliku */
{
struct file * file;
long tmp = -1;
/*sprawdzamy, czy podana liczba moze byc deskryptorem (czy miesci sie w zakresie wielkosci
tablicy deskryptorów), czy na podanym miejscu w rzeczonej tablicy znajduje sie wskaznik
do tablicy plików i czy polu w tablicy plików odpowiada jakis i-wezel*/
if (fd >= NR_OPEN || !(file=current->files->fd[fd]) || !(file->f_inode))
return -EBADF;
/*czy argument origin miesci sie w przewidzianym przedziale*/
if (origin > 2)
return -EINVAL;
/*jesli dla tego systemu plików jest jakas specjalna funkcja lseek - wywolaj ja,
jesli nie ma - kontunuuj
w systemie EXT2 nie ma takiej specjalnej funkcji, wiec kontynuujemy wykonywanie
funkcji domyslnej*/
if (file->f_op && file->f_op->lseek)
return file->f_op->lseek(file->f_inode,file,offset,origin);
/*obliczymy teraz pozycje, jaka bedziemy mieli, po wykonaniu operacji, pozycje w pliku */
/*uzyjemy zmiennej tmp*/
switch (origin) {
case 0: /*przesuniecie wzgledem poczatku pliku*/
tmp = offset;
break;
case 1: /*przesuniecie wzgledem biezacej pozycji*/
tmp = file->f_pos + offset;
break;
case 2:/*przesuniecie wzgledem konca pliku*/
if (!file->f_inode) /*sprawdzenie, czy jest i-wezel odpowiadajacy
plikowi; potrzebne, bo za chwile skorzystamy
z tegoz i-wezla*/
return -EINVAL;
tmp = file->f_inode->i_size + offset;
break;
}
if (tmp < 0) /*jesli mielibysmy sie znalezc przed poczatkiem pliku,
to byloby niedobrze*/
return -EINVAL;
if (tmp != file->f_pos) { /*jesli nowa pozycja jest rózna od starej to przesuwamy*/
file->f_pos = tmp;
file->f_reada = 0;
file->f_version = ++event;
}
return file->f_pos; /*zwracamy obliczona nowa pozycje*/
}
asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high,
unsigned long offset_low, loff_t * result,
unsigned int origin)
{
struct file * file;
loff_t tmp = -1;
loff_t offset;
int err;
if (fd >= NR_OPEN || !(file=current->files->fd[fd]) || !(file->f_inode))
return -EBADF;
if (origin > 2)
return -EINVAL;
if ((err = verify_area(VERIFY_WRITE, result, sizeof(loff_t))))
return err;
offset = (loff_t) (((unsigned long long) offset_high << 32) | offset_low);
/* if there is a fs-specific handler, we can't just ignore it.. */
/* accept llseek() only for the signed long subset of long long */
if (file->f_op && file->f_op->lseek) {
if (offset != (long) offset)
return -EINVAL;
return file->f_op->lseek(file->f_inode,file,offset,origin);
}
switch (origin) {
case 0:
tmp = offset;
break;
case 1:
tmp = file->f_pos + offset;
break;
case 2:
if (!file->f_inode)
return -EINVAL;
tmp = file->f_inode->i_size + offset;
break;
}
if (tmp < 0)
return -EINVAL;
if (tmp != file->f_pos) {
file->f_pos = tmp;
file->f_reada = 0;
file->f_version = ++event;
}
memcpy_tofs(result, &file->f_pos, sizeof(loff_t));
return 0;
}
asmlinkage int sys_read(unsigned int fd,char * buf,int count)
/* fd - deskryptor pliku
buf - adres bufora w procesie użytkownika
count - liczba bajtów do wczytania
wynik: liczba bajtów skopiowanych do bufora użytkownika
*/
{
int error;
struct file * file;
struct inode * inode;
error = -EBADF;
file = fget(fd);
if (!file)
goto bad_file;
/* pobierz i-węzeł odpowiadający deskryptorowi pliku użytkownika */
inode = file->f_inode;
if (!inode)
goto out;
error = -EBADF;
/* sprawdź prawo do czytania dla pliku */
if (!(file->f_mode & 1))
goto out;
error = -EINVAL;
/* sprawdź możliwość wywołania funkcji read dla konkretnego systemu plików */
if (!file->f_op || !file->f_op->read)
goto out;
error = 0;
if (count <= 0)
goto out;
/* sprawdź, czy nie ma blokady na rekord do czytania */
error = locks_verify_area(FLOCK_VERIFY_READ,inode,file,file->f_pos,count);
if (error)
goto out;
/* sprawdź, czy struktura danych użytkownika jest zaalokowana w jego
przestrzeni adresowej */
error = verify_area(VERIFY_WRITE,buf,count);
if (error)
goto out;
/* wywołaj funkcję read właściwą dla danego systemu plików
dla systemu plików EXT2 jest to funkcja generic_file_read() */
error = file->f_op->read(inode,file,buf,count);
out:
fput(file, inode);
bad_file:
return error;
}
asmlinkage int sys_write(unsigned int fd,char * buf,unsigned int count)
/* KOMENTOWAL: ADAM KIEZUN */
/* fd - deskryptor pliku,
* buf - bufor, który chcemy zapisac,
* count - ile bajtów zapisac
*/
{
int error;
struct file * file;
struct inode * inode;
error = -EBADF;
file = fget(fd); /* pobiera wskaznik do pliku z tablicy deskryptorów */
if (!file) /*brak takiej informacji*/
goto bad_file;
inode = file->f_inode; /*pobiera i-wezel pliku*/
if (!inode)/*brak i-wezla*/
goto out;
if (!(file->f_mode & 2))
goto out;
error = -EINVAL;
if (!file->f_op || !file->f_op->write) /*brak operacji pisania dla tego pliku*/
goto out;
error = 0;
if (!count)
goto out;
/*sprawdzenie, czy nie ma blokad*/
error = locks_verify_area(FLOCK_VERIFY_WRITE,inode,file,file->f_pos,count);
if (error)
goto out;
/*sprawdzenie prawa do czytania z podanego obszaru pamieci*/
error = verify_area(VERIFY_READ,buf,count);
if (error)
goto out;
if (!suser() && (inode->i_mode & (S_ISUID | S_ISGID))) {
struct iattr newattrs;
newattrs.ia_mode = inode->i_mode &
~(S_ISUID | ((inode->i_mode & S_IXGRP) ? S_ISGID : 0));
newattrs.ia_valid = ATTR_CTIME | ATTR_MODE | ATTR_FORCE;
notify_change(inode, &newattrs);
}
down(&inode->i_sem); /*opuszczenie semafora w i-wezle; tylko 1 proces pisze na raz*/
error = file->f_op->write(inode,file,buf,count); /*funkcja zalezna od systemu plików;
tu hiperpolaczenie prowadzi do funkcji
dla EXT2*/
up(&inode->i_sem); /*podniesienie semafora w i-wezle*/
out:
fput(file, inode);
bad_file:
return error;
}
static int sock_readv_writev(int type, struct inode * inode, struct file * file,
const struct iovec * iov, long count, long size)
{
struct msghdr msg;
struct socket *sock;
sock = &inode->u.socket_i;
if (!sock->ops)
return -EOPNOTSUPP;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_iov = (struct iovec *) iov;
msg.msg_iovlen = count;
/* read() does a VERIFY_WRITE */
if (type == VERIFY_WRITE) {
if (!sock->ops->recvmsg)
return -EOPNOTSUPP;
return sock->ops->recvmsg(sock, &msg, size,
(file->f_flags & O_NONBLOCK), 0, NULL);
}
if (!sock->ops->sendmsg)
return -EOPNOTSUPP;
return sock->ops->sendmsg(sock, &msg, size,
(file->f_flags & O_NONBLOCK), 0);
}
typedef int (*IO_fn_t)(struct inode *, struct file *, char *, int);
static int do_readv_writev(int type, struct inode * inode, struct file * file,
const struct iovec * vector, unsigned long count)
{
size_t tot_len;
struct iovec iov[UIO_MAXIOV];
int retval, i;
IO_fn_t fn;
/*
* First get the "struct iovec" from user memory and
* verify all the pointers
*/
if (!count)
return 0;
if (count > UIO_MAXIOV)
return -EINVAL;
retval = verify_area(VERIFY_READ, vector, count*sizeof(*vector));
if (retval)
return retval;
memcpy_fromfs(iov, vector, count*sizeof(*vector));
tot_len = 0;
for (i = 0 ; i < count ; i++) {
tot_len += iov[i].iov_len;
retval = verify_area(type, iov[i].iov_base, iov[i].iov_len);
if (retval)
return retval;
}
retval = locks_verify_area(type == VERIFY_READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
inode, file, file->f_pos, tot_len);
if (retval)
return retval;
/*
* Then do the actual IO. Note that sockets need to be handled
* specially as they have atomicity guarantees and can handle
* iovec's natively
*/
if (inode->i_sock)
return sock_readv_writev(type, inode, file, iov, count, tot_len);
if (!file->f_op)
return -EINVAL;
/* VERIFY_WRITE actually means a read, as we write to user space */
fn = file->f_op->read;
if (type == VERIFY_READ)
fn = (IO_fn_t) file->f_op->write;
vector = iov;
while (count > 0) {
void * base;
int len, nr;
base = vector->iov_base;
len = vector->iov_len;
vector++;
count--;
nr = fn(inode, file, base, len);
if (nr < 0) {
if (retval)
break;
retval = nr;
break;
}
retval += nr;
if (nr != len)
break;
}
return retval;
}
asmlinkage int sys_readv(unsigned long fd, const struct iovec * vector, long count)
{
struct file * file;
struct inode * inode;
if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode = file->f_inode))
return -EBADF;
if (!(file->f_mode & 1))
return -EBADF;
return do_readv_writev(VERIFY_WRITE, inode, file, vector, count);
}
asmlinkage int sys_writev(unsigned long fd, const struct iovec * vector, long count)
{
int error;
struct file * file;
struct inode * inode;
if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode = file->f_inode))
return -EBADF;
if (!(file->f_mode & 2))
return -EBADF;
down(&inode->i_sem);
error = do_readv_writev(VERIFY_READ, inode, file, vector, count);
up(&inode->i_sem);
return error;
}