at v4.14 263 lines 5.9 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * file.c 4 * 5 * Copyright (C) 1995, 1996 by Volker Lendecke 6 * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache 7 * 8 */ 9 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11 12#include <linux/uaccess.h> 13 14#include <linux/time.h> 15#include <linux/kernel.h> 16#include <linux/errno.h> 17#include <linux/fcntl.h> 18#include <linux/stat.h> 19#include <linux/mm.h> 20#include <linux/vmalloc.h> 21#include <linux/sched.h> 22 23#include "ncp_fs.h" 24 25static int ncp_fsync(struct file *file, loff_t start, loff_t end, int datasync) 26{ 27 return file_write_and_wait_range(file, start, end); 28} 29 30/* 31 * Open a file with the specified read/write mode. 32 */ 33int ncp_make_open(struct inode *inode, int right) 34{ 35 int error; 36 int access; 37 38 error = -EINVAL; 39 if (!inode) { 40 pr_err("%s: got NULL inode\n", __func__); 41 goto out; 42 } 43 44 ncp_dbg(1, "opened=%d, volume # %u, dir entry # %u\n", 45 atomic_read(&NCP_FINFO(inode)->opened), 46 NCP_FINFO(inode)->volNumber, 47 NCP_FINFO(inode)->dirEntNum); 48 error = -EACCES; 49 mutex_lock(&NCP_FINFO(inode)->open_mutex); 50 if (!atomic_read(&NCP_FINFO(inode)->opened)) { 51 struct ncp_entry_info finfo; 52 int result; 53 54 /* tries max. rights */ 55 finfo.access = O_RDWR; 56 result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), 57 inode, NULL, OC_MODE_OPEN, 58 0, AR_READ | AR_WRITE, &finfo); 59 if (!result) 60 goto update; 61 /* RDWR did not succeeded, try readonly or writeonly as requested */ 62 switch (right) { 63 case O_RDONLY: 64 finfo.access = O_RDONLY; 65 result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), 66 inode, NULL, OC_MODE_OPEN, 67 0, AR_READ, &finfo); 68 break; 69 case O_WRONLY: 70 finfo.access = O_WRONLY; 71 result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), 72 inode, NULL, OC_MODE_OPEN, 73 0, AR_WRITE, &finfo); 74 break; 75 } 76 if (result) { 77 ncp_vdbg("failed, result=%d\n", result); 78 goto out_unlock; 79 } 80 /* 81 * Update the inode information. 82 */ 83 update: 84 ncp_update_inode(inode, &finfo); 85 atomic_set(&NCP_FINFO(inode)->opened, 1); 86 } 87 88 access = NCP_FINFO(inode)->access; 89 ncp_vdbg("file open, access=%x\n", access); 90 if (access == right || access == O_RDWR) { 91 atomic_inc(&NCP_FINFO(inode)->opened); 92 error = 0; 93 } 94 95out_unlock: 96 mutex_unlock(&NCP_FINFO(inode)->open_mutex); 97out: 98 return error; 99} 100 101static ssize_t 102ncp_file_read_iter(struct kiocb *iocb, struct iov_iter *to) 103{ 104 struct file *file = iocb->ki_filp; 105 struct inode *inode = file_inode(file); 106 size_t already_read = 0; 107 off_t pos = iocb->ki_pos; 108 size_t bufsize; 109 int error; 110 void *freepage; 111 size_t freelen; 112 113 ncp_dbg(1, "enter %pD2\n", file); 114 115 if (!iov_iter_count(to)) 116 return 0; 117 if (pos > inode->i_sb->s_maxbytes) 118 return 0; 119 iov_iter_truncate(to, inode->i_sb->s_maxbytes - pos); 120 121 error = ncp_make_open(inode, O_RDONLY); 122 if (error) { 123 ncp_dbg(1, "open failed, error=%d\n", error); 124 return error; 125 } 126 127 bufsize = NCP_SERVER(inode)->buffer_size; 128 129 error = -EIO; 130 freelen = ncp_read_bounce_size(bufsize); 131 freepage = vmalloc(freelen); 132 if (!freepage) 133 goto outrel; 134 error = 0; 135 /* First read in as much as possible for each bufsize. */ 136 while (iov_iter_count(to)) { 137 int read_this_time; 138 size_t to_read = min_t(size_t, 139 bufsize - (pos % bufsize), 140 iov_iter_count(to)); 141 142 error = ncp_read_bounce(NCP_SERVER(inode), 143 NCP_FINFO(inode)->file_handle, 144 pos, to_read, to, &read_this_time, 145 freepage, freelen); 146 if (error) { 147 error = -EIO; /* NW errno -> Linux errno */ 148 break; 149 } 150 pos += read_this_time; 151 already_read += read_this_time; 152 153 if (read_this_time != to_read) 154 break; 155 } 156 vfree(freepage); 157 158 iocb->ki_pos = pos; 159 160 file_accessed(file); 161 162 ncp_dbg(1, "exit %pD2\n", file); 163outrel: 164 ncp_inode_close(inode); 165 return already_read ? already_read : error; 166} 167 168static ssize_t 169ncp_file_write_iter(struct kiocb *iocb, struct iov_iter *from) 170{ 171 struct file *file = iocb->ki_filp; 172 struct inode *inode = file_inode(file); 173 size_t already_written = 0; 174 size_t bufsize; 175 int errno; 176 void *bouncebuffer; 177 off_t pos; 178 179 ncp_dbg(1, "enter %pD2\n", file); 180 errno = generic_write_checks(iocb, from); 181 if (errno <= 0) 182 return errno; 183 184 errno = ncp_make_open(inode, O_WRONLY); 185 if (errno) { 186 ncp_dbg(1, "open failed, error=%d\n", errno); 187 return errno; 188 } 189 bufsize = NCP_SERVER(inode)->buffer_size; 190 191 errno = file_update_time(file); 192 if (errno) 193 goto outrel; 194 195 bouncebuffer = vmalloc(bufsize); 196 if (!bouncebuffer) { 197 errno = -EIO; /* -ENOMEM */ 198 goto outrel; 199 } 200 pos = iocb->ki_pos; 201 while (iov_iter_count(from)) { 202 int written_this_time; 203 size_t to_write = min_t(size_t, 204 bufsize - (pos % bufsize), 205 iov_iter_count(from)); 206 207 if (!copy_from_iter_full(bouncebuffer, to_write, from)) { 208 errno = -EFAULT; 209 break; 210 } 211 if (ncp_write_kernel(NCP_SERVER(inode), 212 NCP_FINFO(inode)->file_handle, 213 pos, to_write, bouncebuffer, &written_this_time) != 0) { 214 errno = -EIO; 215 break; 216 } 217 pos += written_this_time; 218 already_written += written_this_time; 219 220 if (written_this_time != to_write) 221 break; 222 } 223 vfree(bouncebuffer); 224 225 iocb->ki_pos = pos; 226 227 if (pos > i_size_read(inode)) { 228 inode_lock(inode); 229 if (pos > i_size_read(inode)) 230 i_size_write(inode, pos); 231 inode_unlock(inode); 232 } 233 ncp_dbg(1, "exit %pD2\n", file); 234outrel: 235 ncp_inode_close(inode); 236 return already_written ? already_written : errno; 237} 238 239static int ncp_release(struct inode *inode, struct file *file) { 240 if (ncp_make_closed(inode)) { 241 ncp_dbg(1, "failed to close\n"); 242 } 243 return 0; 244} 245 246const struct file_operations ncp_file_operations = 247{ 248 .llseek = generic_file_llseek, 249 .read_iter = ncp_file_read_iter, 250 .write_iter = ncp_file_write_iter, 251 .unlocked_ioctl = ncp_ioctl, 252#ifdef CONFIG_COMPAT 253 .compat_ioctl = ncp_compat_ioctl, 254#endif 255 .mmap = ncp_mmap, 256 .release = ncp_release, 257 .fsync = ncp_fsync, 258}; 259 260const struct inode_operations ncp_file_inode_operations = 261{ 262 .setattr = ncp_notify_change, 263};